Dynamic libraries not linked after updating to React Native 0.60+ - ios

After updating to React Native 0.61.5 I am having problems linking libraries that can be linked only dynamically (#react-native-mapbox-gl/maps).
The solution would normally be to enable dynamic libraries in the Podfile (setting use_frameworks!). However, as result this makes all libraries dynamic and causes problems with libraries that can only be linked statically (react-native-firebase).
Is there any solution that would satisfy these conflicting requirements?
When use_frameworks! is set, this error gets thrown at runtime:
dyld: Library not loaded: #rpath/MapboxMobileEvents.framework/MapboxMobileEvents
Referenced from: /private/var/containers/Bundle/Application/4A4F5BC3-2A1D-4949-9423-71EF7EFE79FD/ImmoWert2Go.app/Frameworks/Mapbox.framework/Mapbox
Reason: image not found
When use_frameworks! is not set, pod install throws this error:
[!] The 'Pods-ImmoWert2Go' target has transitive dependencies that include statically linked binaries: (FirebaseCore, FirebaseCoreDiagnostics, GoogleDataTransportCCTSupport, GoogleDataTransport, and FirebaseInstanceID)

#react-native-mapbox-gl/maps is currently not available as a static library, so the whole project needs to be configured to use dynamic libraries, which is discouraged by Apple.
The solution was to add these lines at the beginning of the Podfile:
# Set libraries as dynamic by default
use_frameworks!
# Set specific libraries as static (react-native-firebase)
pre_install do |installer|
installer.pod_targets.each do |pod|
if pod.name.start_with?('RNFB')
def pod.build_type;
Pod::Target::BuildType.static_library
end
end
end
end
Once this was done, RNFirebaseUtil.h needs to be patched from
#import <Firebase.h>
to
#import "Firebase.h"

Related

iOS: How to build a static framework based on CocoaPods dependencies?

I am trying to build a framework that makes use of the geopackage-ios CocoaPods dependency. I would then like to statically embed this framework into one or more apps.
Similar questions have been asked before, but I couldn't find a solution to my problem. Here's what I've been trying so far:
First Approach: Building Manually
In this approach, I would create a static library using Xcode, add the CocoaPods dependencies to it and build it. The problem with this approach, however, is that the resulting framework will not have the CocoaPods dependencies embedded into it.
Create a new static library from Xcode (File > New > Project… > iOS: Static Library) named "MyLibrary" with language Swift.
Create a Podfile in the project root directory with the following content:
target 'MyLibrary' do
pod 'geopackage-ios', '~> 7.2.1'
end
Run pod install. (This will install the geopackage-ios pod and 10 depending pods.)
Open the newly created MyLibrary.xcworkspace and add a BridgingHeader.h to the "MyLibrary" group having the following content:
#ifndef BridgingHeader_h
#define BridgingHeader_h
#import "geopackage-ios-Bridging-Header.h"
#endif
Reference the bridging header by clicking on the "MyLibrary" project in the project navigator, selecting the "MyLibrary" target and, in the Build Settings, specifying MyLibrary/BridgingHeader.h as the "Objective-C Bridging Header".
Modify MyLibrary.swift to use the GeoPackage dependency:
public class MyLibrary {
public init() {}
public func test() {
let manager = GPKGGeoPackageFactory.manager()!
print("Manager:", manager)
}
}
Build the library for the iOS simulator by pressing Cmd + B.
Optionally, create a static framework using a shell script like the one in this article (which will compile the library once for the simulator and once for physical devices and then combine the two results to a universal framework using the lipo command).
When I embed either the static library (along with the MyLibrary.swiftmodule) or the universal framework into an app, linking will fail for this app:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_GPKGGeoPackageManager", referenced from:
objc-class-ref in libMyLibrary.a(MyLibrary.o)
"_OBJC_CLASS_$_GPKGGeoPackageFactory", referenced from:
objc-class-ref in libMyLibrary.a(MyLibrary.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I could also add the geopackage_ios.framework (found in the build folder of "MyLibrary") to the app (making sure to choose the Embed & Sign embedding option), which will allow the app to be built successfully. Then, however, the app will crash upon launch:
dyld[79209]: Library not loaded: #rpath/ogc_api_features_json_ios.framework/ogc_api_features_json_ios
Referenced from: /Users/admin1/Library/Developer/CoreSimulator/Devices/C3D61224-CFE8-45CC-951B-7B6AB54BC8B3/data/Containers/Bundle/Application/D41A82D6-4E69-4BB6-AC59-6BC28AE58795/TestApp.app/Frameworks/geopackage_ios.framework/geopackage_ios
One might be able to fix this by adding all eleven pod frameworks to the app, which, however, is what I'm trying to avoid.
Second Approach: Using PodBuilder
A second approach would be to use PodBuilder, which aims at automating this particular kind of build task. This approach, however, fails with a build error, the cause of which I can't quite make out.
Again, I would start out with a new Xcode project. This time, I'd create an iOS Swift app named "MyApp".
I would create a Podfile with almost the same content as above:
use_frameworks!
target 'MyApp' do
pod 'geopackage-ios', '~> 7.2.1'
end
Then, I'd run pod install.
Next, I would install PodBuilder, create an empty Git repository (which PodBuilder requires for some reason), initialize PodBuilder, and start the build task:
sudo gem install pod-builder
git init
pod_builder init
pod_builder build geopackage-ios
The build will fail, however, with errors like these written to /tmp/pod_builder/pod_builder.err:
Undefined symbols for architecture arm64:
"_PROJ_WEB_MERCATOR_MAX_LAT_RANGE", referenced from:
+[GPKGTileBoundingBoxUtils toWebMercatorWithBoundingBox:] in GPKGTileBoundingBoxUtils.o
+[GPKGTileBoundingBoxUtils boundDegreesBoundingBoxWithWebMercatorLimits:] in GPKGTileBoundingBoxUtils.o
-[GPKGTileDao isXYZTiles] in GPKGTileDao.o
-[GPKGTileGenerator adjustXYZBounds] in GPKGTileGenerator.o
"_PROJ_UNDEFINED_CARTESIAN", referenced from:
-[GPKGSpatialReferenceSystemDao createIfNeededWithSrs:andOrganization:andCoordsysId:] in GPKGSpatialReferenceSystemDao.o
"_PROJ_AUTHORITY_NONE", referenced from:
-[GPKGSpatialReferenceSystemDao createIfNeededWithSrs:andOrganization:andCoordsysId:] in GPKGSpatialReferenceSystemDao.o
[…]
This seems to be a problem with the geopackage-ios pod itself, which can be fixed by cloning our own version of the repo …
cd ..
git clone --branch 7.2.1 https://github.com/ngageoint/geopackage-ios.git
…, installing the pods …
cd geopackage-ios
pod install
… manually copying the proj-ios source files …
cp -r Pods/proj-ios/proj-ios geopackage-ios
…, and opening the geopackage-ios.xcodeproj to reference the proj-ios files in the geopackage-ios group inside that project.
Then telling CocoaPods to use our local version of the pod …
use_frameworks!
target 'MyApp' do
pod 'geopackage-ios', :path => '../geopackage-ios'
end
…, reinitializing PodBuilder …
cd ../MyApp
pod_builder deintegrate
pod deintegrate
pod install
pod_builder init
… and allowing PodBuilder to use local pods by editing the ./PodBuilder/PodBuilder.json file:
{
…,
"allow_building_development_pods": true,
…
}
Then, one can try to rebuild:
pod_builder build geopackage-ios
This time, the build will fail with a different error:
The following build commands failed:
CompileC /tmp/pod_builder/build/Pods.build/Release-iphoneos/sf-proj-ios.build/Objects-normal/arm64/SFPGeometryTransform.o /tmp/pod_builder/Pods/sf-proj-ios/sf-proj-ios/SFPGeometryTransform.m normal arm64 objective-c com.apple.compilers.llvm.clang.1_0.compiler (in target 'sf-proj-ios' from project 'Pods')
(1 failure)
The SFPGeometryTransform.m file, however, looks fine.
The full build log is available here.
Third Approach: Using Rome
The third approach would be to use cocoapods-rome.
To use it, one would install Rome …
sudo gem install cocoapods-rome
…, get rid of the PodBuilder stuff …
pod_builder deintegrate
pod deintegrate
…, quickly adjust the Podfile:
use_frameworks!
platform :ios, '13.0'
plugin 'cocoapods-rome', { :pre_compile => Proc.new { |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '5.0'
end
end
installer.pods_project.save
},
dsym: false,
configuration: 'Release'
}
target 'MyApp' do
pod 'geopackage-ios', :path => '../geopackage-ios'
end
… and install the pods:
pod install
This, however, will fail with the exact same error as PodBuilder.
Can anyone give me some advice on any of these approaches? Or does anyone perhaps have a whole different idea on how to build such a framework?
(EDIT: It seems like the PROJProjectionTransform.h file imported to SFPGeometryTransform.m cannot be found. Anyway, I don't think these approaches are expedient. So any help is highly appreciated.)
Solved the problem by simply feeding all the aggregated source files from the pod installation into a new static library, then using either the lipo command to make a fat binary out of it or creating an Xcode framework from it (as described here). The compiled framework could then be used to create another framework built on top of it.

cocoa podspec subspec swift compatibility header file not found

Pod lib could not find mymodule-swift.h header.
One of my objc.m file imports following code.
#import <devillogin/devillogin-Swift.h>
Xcode build is success. Everything is ok.
But when I'm trying to distribute with pod, following error printed.
pod lib lint mymodule.podspec
fatal error: 'devillogin/devillogin-Swift.h' file not found
My mylib.podpec is as following
Pod::Spec.new do |s|
...
s.subspec 'DevilLogin' do |devilLogin|
devilLogin.source_files = 'devillogin/devillogin/source/**/*.*'
devilLogin.public_header_files = 'Pod/Headers/*.h'
devilLogin.dependency 'devil/DevilCore'
devilLogin.dependency 'KakaoSDK'
end
end
Is there any syntax in podspec for mylib-swift.h ?
I'm answering to myself.
I found that the issue only occur when it is in the subspec.
No problem with root podspec.
Consequently I couldn't find way to let my pod-subspec import "XXX-swift" header.
But I found workaround.
I referenced firebase frameworks which use many pod-subspecs.
https://github.com/firebase/firebase-ios-sdk
Just watched root pod spect and 1 subspec.
It distributes sub-specs as root pod spec which is success with 'XXX-swift.h' header
And each subspecs depend on it above.
There is Firebase.podspec and FirebaseDynamicLinks.podspec.
FirebaseDynamicLinks is standalone independent frameworks.
But it is actually subspec.
Firebase podspec defines subspec like followings.
This is workaround
s.subspec 'DynamicLinks' do |ss|
ss.dependency 'Firebase/CoreOnly'
ss.ios.dependency 'FirebaseDynamicLinks', '~> 7.9.0'
end
It defines DynamicLinks as subspec and define dependency on independent FirebaseDynamicLinks.
Done

Custom Compiler Flags for GoogleSignIn/Crashlytics/Firebase

I'm trying to resolve compiler flags, like -Wno-auto-import, in my main project. But I can't set the compiler flags for GoogleSignIn/Crashlytics/Firebase pods. I tried to add this following code to my Podfile:
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['WARNING_CFLAGS'] = "$(inherited) -Wno-auto-import"
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
end
end
end
But I'm still getting auto import compiler errors. It's working fine for facebook signin and all other pods. The compiler flags are also listed under Pods -> GTMOAuth2 -> Apple LLVM 9.0 - Custom Compiler Flags -> Other Warning Flags -> -Wno-auto-import
How do I fix this?
You are able to silence all cocoapod warnings by specifying this flag in your pod file:
inhibit_all_warnings!
You can be a bit more granular and specify it per pod if you wanted:
pod 'GoogleSignIn', inhibit_warnings: true
pod 'Crashlytics', inhibit_warnings: true
pod 'Firebase', inhibit_warnings: true
This may not work if you have -Weverything enabled in your project, which is generally discouraged because it includes warnings that are buggy or still in development. This flag will give you false-positives for your cocoapod frameworks. More information here: https://softwareengineering.stackexchange.com/questions/122608/clang-warning-flags-for-objective-c-development/124574#124574
If you still wish to try manually edit the warning flags - then your code sample looks fine, make sure it's not getting overwritten by an xcconfig file
Also make sure you do a clean so that no warnings are left over from previous builds! Xcode can be a pain and keep them around longer than they need to be sometimes. Check the actual build log produced to validate if they are still being raised

CocoaPods project depend on a local static C library

I have locally built C library (.h and .a files) that I want to include in a Swift-based CocoaPods pod. How do I configure the podspec to depend on the .a files and the module.map? With a normal non-CocoaPods Xcode project, I simply drag in the directory that contains include and lib and then add a module.map. With CocoaPods I can't do this because pod install will overwrite the Xcode project file. s.library won't work because the the static library isn't hosted anywhere. I tried s.vendored_libraries but module.map still remains unknown to Xcode, the end result being that import foo from my Swift files is an error.
Edit: I tried using preserve_paths, vendored_libraries and xcconfig as answered here. The issue is still how to import the module from Swift.
Edit 2: I also tried using module_map to point to my module.map file as documented here, but sadly CocoaPods 1.1.1 crashes ([!] Oh no, an error occurred.).
I got it working. In my case I'm depending the libtiff C library which is prebuilt for iOS (x86 and arm) using https://github.com/ashtons/libtiff-ios.
I used a subspec as outline here. Here's the podspec subspec snippet, assuming the static library lives at libtiff off the root of the pod module.
s.subspec 'libtiff' do |libtiff|
libtiff.source_files = 'libtiff/include/*.h'
libtiff.public_header_files = 'libtiff/include/*.h'
libtiff.preserve_paths = 'libtiff/include/*.h'
libtiff.vendored_libraries = 'libtiff/lib/libjpeg.a', 'libtiff/lib/libpng.a', 'libtiff/lib/libtiff.a', 'libtiff/lib/libtiffxx.a'
libtiff.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/libtiff/include/**" }
# you can't specify "libz" here, must specify "z", see https://github.com/CocoaPods/CocoaPods/issues/3232
libtiff.library = 'z'
end

Include Pod's Bridging-Header to Build Settings of Project's Target?

I created an objective-c pod with two files:
Source/SomeViewController.h
Source/SomeViewController.m
I also created a bridging header in the pod:
Source/Bridging-Header.h
with the content:
#import "SomeViewController.h"
My podspec looks like this:
Pod::Spec.new do |s|
s.name = 'TestLib'
s.version = '0.0.1'
s.license = 'MIT'
s.ios.deployment_target = '7.0'
s.source_files = 'Source/*.{h,m}'
s.requires_arc = true
s.xcconfig = { 'SWIFT_OBJC_BRIDGING_HEADER' => 'Source/Bridging-Header.h' }
end
I created a demo project did pod init and inserted my pod. Then after pod install I get the following output:
Installing TestLib 0.0.1 (was 0.0.1)
Generating Pods project
Integrating client project
[!] The `TestLibProject [Debug]` target overrides the `SWIFT_OBJC_BRIDGING_HEADER` build setting defined in `Pods/Target Support Files/Pods-TestLibProject/Pods-TestLibProject.debug.xcconfig'. This can lead to problems with the CocoaPods installation
- Use the `$(inherited)` flag, or
- Remove the build settings from the target.
[!] The `TestLibProject [Release]` target overrides the `SWIFT_OBJC_BRIDGING_HEADER` build setting defined in `Pods/Target Support Files/Pods-TestLibProject/Pods-TestLibProject.release.xcconfig'. This can lead to problems with the CocoaPods installation
- Use the `$(inherited)` flag, or
- Remove the build settings from the target.
When I open my TestLibProject.xcworkspace file I see that the pod was installed correctly but the bridging header from the pod is not installed correctly. I my Swift project I tried to do:
let vc: SomeViewController
This gives an error because the bridging header from the pod ist not installed.
How do I have to configure the podspec in order to get the bridging header of the pod being installed correctly?
Podspecs build frameworks, and frameworks cannot include bridging headers. If you want to import non-modular code into a Swift framework, you'll need to use a custom module map, instead, e.g. at MyLib/module.modulemap:
framework module MyLib {
umbrella header "MyLib.h"
// Load an SDK header, e.g. CommonCrypto.h
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h"
export *
module * { export * }
}
Once there, you can specify the custom module map both in your Xcode project (either as the MODULEMAP_FILE setting in an .xcconfig file, or as the Module Map File of your target's Build Settings.
Now, the final piece of the puzzle: the podspec. You need to set the module_map:
Pod::Spec.new do |s|
# …
s.module_map = 'MyLib/module.modulemap'
end
The above is how SQLite.swift distributes itself, both as a general framework and as a pod.
Edit: Seems like I missed the point of the original question, as was clarified in this thread. The OP wanted to use a pod framework's bridging header to automatically load itself into the installing project's Swift code. This isn't possible. Even if Swift frameworks did support bridging headers, they would only be able to load Objective-C/C code (i.e. private framework code) into the framework's Swift code.

Resources