Cocoapod subspec dependency not adding header/library search paths - ios

I'm writing a Cocoapods podspec for a Swift library (https://github.com/promotedai/ios-metrics-sdk). One of our clients asked us to add Firebase Analytics tracking to the internals of this library. As a result, in the podspec, I broke the Firebase dependency into a subspec.
Pod::Spec.new do |s|
s.name = 'PromotedAIMetricsSDK'
...
s.subspec 'FirebaseAnalytics' do |a|
a.source_files = ['Sources/PromotedFirebaseAnalytics/**/*.{h,m,swift}']
a.dependency 'Firebase/Analytics', '~> 7.11.0'
a.dependency 'PromotedAIMetricsSDK/Core'
end
end
The problem I ran into was that the above subspec neither builds nor passes pod lib lint. The build complained about a missing Firebase framework, and the lint step complained about the same thing. I had to add the following xcconfig to the subspec to get things working.
analytics_xcconfig = {
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/Firebase/CoreOnly/Sources"',
'FRAMEWORK_SEARCH_PATHS' => '"${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleUtilities" "${PODS_XCFRAMEWORKS_BUILD_DIR}/nanopb"',
'OTHER_LDFLAGS' => '-framework "FirebaseAnalytics" -framework "GoogleUtilities" -framework "nanopb"'
}
s.subspec 'FirebaseAnalytics' do |a|
a.source_files = ['Sources/PromotedFirebaseAnalytics/**/*.{h,m,swift}']
a.dependency 'Firebase/Analytics', '~> 7.11.0'
a.dependency 'PromotedAIMetricsSDK/Core'
a.pod_target_xcconfig = analytics_xcconfig
end
While the above configuration allows the subspec to build and pass lint validation, it defeats the purpose of having a package manager take care of these dependencies. I have other subspecs that pull in dependencies that don't encounter the same problem. Also, pulling in Firebase/Analytics directly in the app's Podfile works fine.
Am I missing something? Is there a reason why Firebase/Analytics requires this xcconfig hack when building from a subspec of a podspec? Can I get rid of this hack somehow?
For a demonstration of this problem, pull the above repo and remove the a.pod_target_xcconfig = ... lines from the FirebaseAnalytics subspec, then run pod lib lint. Commit 67cb6173ee0f43b33cfb726b6337cc14463d220b in the repo demonstrates a similar problem where the linker can't find the FirebaseAnalytics framework (run pod lib lint on that commit).
I'm using Cocoapods 1.10.1, Xcode 12.5, and macOS 11.3.
Thanks,

Related

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

iOS Dyanmic Framework with Firebase Dependencies

We are developing a framework that is dependent on some firebase dependencies like login, analytics, etc. Once our framework is developed we will distribute it to our customers.
Things to be taken care of are
Code should not be visible(Best suggestion is to create XCFramework)
If possible create a dynamic framework instead of a static framework
Can be distributed via Swift package manager or cocoapods
What we have tried
We have tried to create a dynamic framework using pods and then create a XCFramework. But while importing into client app, pods module are not found
We created static library and add firebase manually(In project directly) instead of pods, in that case XCFramework is not imported
We have tried to create XCFrame Work as mentioned here (for dynamic framework)
XCFramework with Pods Dependencies
To hide code umbrella framework and universal library can be used,but with firebase, this approach is to typical and also not suggested on the internet at many places
Is there any other/alternative way where we can fulfill our requirements?
We have the exact same setup right now, and it works quite well. Hope that'll also be of help for you.
Things that are taken care of:
It's an XCFramework distribution.
It's been distributed by CocoaPods (albeit in a private Podspec repository)
It's a dynamic framework.
Prerequisites:
CocoaPods version >= 1.10.1
Xcode version >= 11.6 (could be lower though, not sure)
After creating your .xcframework, you need to have a .podspec for your framework, which should look like:
Pod::Spec.new do |s|
# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
s.name = "MyAwesomeSDK"
s.version = "1.0.0"
s.summary = "Best framework ever: MyAwesomeSDK"
s.description = <<-DESC
"Best framework ever: MyAwesomeSDK"
DESC
s.homepage = "http://github.com"
s.license = "MIT"
s.author = { "ItIsI" => "me#myself.com" }
# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
s.platform = :ios
s.ios.deployment_target = '11.3'
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
s.source = { :http => '<MyAwesomeSDK.zip> (We're storing our zipped .xcframework in a hosted page)' }
s.vendored_frameworks = 'MyAwesomeSDK.xcframework'
s.swift_version = "5.0"
# ――― Dependencies ―――――――――――――――――――――――――――---――――――――――――――――――――――――――――――― #
s.dependency 'SwiftProtobuf', '1.12.0'
s.dependency 'lottie-ios', '3.1.8'
# Any other dependency you might need.
end
Then, we are consuming it in another project via Podfile, that will look like:
platform :ios, '13.0'
# If you're going to have a private Podspec repo, add the source URL here.
# Don't forget to add the original source if you're going to specify another source.
# source 'https://cdn.cocoapods.org/'
target 'Test' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# If you are publishing the SDK publicly or in a private Podspec repository, this is it:
pod 'MyAwesomeSDK'
# If not, you should provide the .podspec to your customers, and:
pod 'MyAwesomeSDK', :podspec => '<path/to/MyAwesomeSDK.podspec>'
target 'TestTests' do
inherit! :search_paths
# Pods for testing
end
target 'TestUITests' do
# Pods for testing
end
end
Then, that's it! When running pod install, you should see:
Analyzing dependencies
Downloading dependencies
Installing MyAwesomeSDK (1.0.0)
# These are our own:
# ---
Installing SwiftProtobuf (1.12.0)
Installing lottie-ios (3.1.8)
# ---
Generating Pods project
Integrating client project
Pod installation complete! There is 1 dependency from the Podfile and 3 total pods installed.
P.S: We also had to add a post_install setup in our Podfile, otherwise it'll not properly link the dependency frameworks:
if ["SwiftProtobuf", "lottie-ios"].include? target.name
target.build_configurations.each do |config|
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
end
end

Cannot Lint/Push a Swift Cocoapods podspec

I tried to create my first podspec combining ObjC and Swift code, but I soon stumbled uppon a stubborn error that I cannot solve while uploading
Copying MyLibrary from /Users/lukasschwoebel/Library/Caches/CocoaPods/Pods/External/MyLibrary/540307feb534d63ad9015f3f6452b3ad-be661 to
../../../../../../private/var/folders/2p/_pc_vts51b3_pfydgm7_2n200000gn/T/CocoaPods/Lint/Pods/MyLibrary
- Running pre install hooks
-> MyLibrary (0.1.0)
- ERROR | [iOS] Encountered an unknown error (Pods written in Swift can only be integrated as frameworks; this feature is still in beta. Add use_frameworks! to your Podfile or target to opt into using it. The Swift Pod being used is: MyLibrary) during validation.
Here is the command I use:
pod repo push test-podspecs MyLibrary/MyLibrary.podspec --use-libraries --allow-warnings --verbose
As you might see, this is the minimum untouched sample project from CocoaPods as described here: https://guides.cocoapods.org/making/using-pod-lib-create.html
As I can not even make this sample running, I suspect something wrong with my cocoapods configuration.
I made sure there is no single Objective C line in the pod that is to be uploaded (though in the end, I would like to have a podspec with ObjC and Swift code mixed, but at this time I just want to have a working podspec with Swift).
I cannot even upload this simple pod with pure Swift. I first tried with CocoaPods v0.39.0, then downgraded to 0.38.2 and after that 0.38.0, even 0.36.0. Even with a complete un-install of CocoaPods.
It seems I need to configure something in CocoaPods to be able to upload a Swift podspec? The error I get obviously is during the building-phase of the pod where the Podfile needs use_frameworks! to be included. So how can I make CocoaPods use it while compiling the pod during the podspec verification?
Also, I have Xcode 7.0.1 and OSX 10.10.5 installed and it is Swift 2.0 code.
Thank you in advance!
Here is the complete podspec, adapted from the original created MyLibrary.podspec (and yes, that is a local Podspec-Repo, but that does not cause/change the error)
Pod::Spec.new do |s|
s.name = "MyLibrary"
s.version = "0.1.0"
s.summary = "A short description of MyLibrary."
s.description = 'Sample Description'
s.license = 'MIT'
s.author = { "Luke A." => "ls#mymail.to" }
s.source = { :git => "file:///Users/.../test/MyLibrary/", :tag => s.version.to_s }
s.platform = :ios, '8.0'
s.requires_arc = true
s.source_files = 'Pod/Classes/**/*'
s.resource_bundles = {
'MyLibrary' => ['Pod/Assets/*.png']
}
s.frameworks = 'UIKit'
end
I just found the answer, which seems a little obvious.
The command to upload/push the pod was in a bash-script so I did not always have to type all the flags but just and over the filename/path to the podspec. So in the default bash-script I had the flag --use-libraries which is required for one of my pods.
As I was trying something out and had a typo in the pod repo push .. command I stumbled across the man-page:
--use-libraries
Linter uses static libraries to install the spec
With static libraries, this flag seems to be incompatible with Swift podspecs. Removing that flag lints and pushes my podspec successfully.

How to import Alamofire/AFNetworking in my custom cocoapod

How do I include Alamofire (a web request pod like AFNetworking) in my cocoapod source files? I have a service in my cocoapod that needs to make web requests using Alamofire, my cocoapod doesn't seem to have a podfile that I can see, so I don't know how to add the dependency to my cocoapod.
I am creating a cocoapod using pod lib create The build fails whenever I go to import Alamofire in any of my files. In a normal project, I'd just add Alamofire to my podfile, but this is a cocoapod, so I can't figure out where to add the dependency, or how to get it to build successfully.
I followed the guide here but it doesn't say anything about importing another pod into my cocoapod's files.
My directory structure looks like this:
MyLib.podspec
Example/
MyLib example project
Pod/
Source files for my cocoapod
If your pod depends on other pods you can define that in your pod's .podspec file. You can add dependencies there.
Have a look at RealmSwift's podspec file as an example. The RealmSwift pod has a dependency to the Realm pod. This is defined in RealmSwift.podspec:
Pod::Spec.new do |s|
s.name = 'RealmSwift'
s.version = `sh build.sh get-version`
s.summary = 'Realm is a modern data framework & database for iOS & OS X.'
s.description = <<-DESC
The Realm database, for Swift. (If you want to use Realm from Objective-C, see the “Realm” pod.)
Realm is a mobile database: a replacement for Core Data & SQLite. You can use it on iOS & OS X. Realm is not an ORM on top SQLite: instead it uses its own persistence engine, built for simplicity (& speed). Learn more and get help at https://realm.io
DESC
s.homepage = "https://realm.io"
s.source = { :git => 'https://github.com/realm/realm-cocoa.git', :tag => "v#{s.version}" }
s.author = { 'Realm' => 'help#realm.io' }
s.requires_arc = true
s.social_media_url = 'https://twitter.com/realm'
s.documentation_url = "https://realm.io/docs/swift/#{s.version}"
s.license = { :type => 'Apache 2.0', :file => 'LICENSE' }
# ↓↓↓ THIS IS WHERE YOU DEFINE THE DEPENDENCY TO ANOTHER POD ↓↓↓
s.dependency 'Realm', "= #{s.version}"
# ↑↑↑ THIS IS WHERE YOU DEFINE THE DEPENDENCY TO ANOTHER POD ↑↑↑
s.source_files = 'RealmSwift/*.swift'
s.prepare_command = 'sh build.sh cocoapods-setup without-core'
s.preserve_paths = %w(build.sh)
s.pod_target_xcconfig = { 'SWIFT_WHOLE_MODULE_OPTIMIZATION' => 'YES',
'APPLICATION_EXTENSION_API_ONLY' => 'YES' }
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
s.watchos.deployment_target = '2.0' if s.respond_to?(:watchos)
end
You should check out the AlamofireImage project. It uses Carthage to add the Alamofire submodule to the project. The Alamofire project is then added as a dependency to the AlamofireImage project.
The AlamofireImage.podspec also demonstrates how to add Alamofire as a dependency for CocoaPods. If you follow the AlamofireImage project structure exactly, you'll be up and running in no time. Here are some useful commands to get you going:
Cartfile
github "Alamofire/Alamofire" ~> 3.0
Checkout through Carthage
brew update
brew doctor
brew install carthage
// or
brew upgrade carthage
carthage update --no-build --use-submodules
Hopefully that helps!
If you have created a pod and in your .podspec file you are trying to add a dependency (like Alamofire, RealmSwift..) after that you should go to the Example/.. folder and do a pod install to make the dependencies required from the .podspec of your custom pod visible to the .swift files in your custom pod/framework.
A typical example of a pod project folder hierarchy would be:
- MyLib/
- _Pods.xcodeproj
- Example/ // <-- do a pod install under this folder in order to get the dependencies declared in your .podspec
- Podfile
- MyLib.xcodeproj
- MyLib.xcworkspace
- MyLib/
- Classes/ // <-- folder with pod specific logic that also uses Alamofire
- Assets/
- MyLib.podspec // <-- your podspec with dependencies (Alamofire..)

Using dependency in swift module (framework)

I am trying to create a swift module (Cocoa Touch Framework) with reusable code inside the environment set up by cocoa pods which includes third party libraries written in Objective-C (namely here Restkit).
Unfortunately I am not able to use Restkit in the module I create.
Here's what I did to create the module:
File -> New target: Cocoa Touch Framework, Language: Swift, Project: MyProject, Embed in Application: MyProject
In the "Info" tab of the project settings in the "Configurations" section I define the Pods.debug and Pods.release xcconfig file for my newly created target.
In the header file, which Xcode automatically created for me, networkModule.h, I add the following line:
#import <RestKit/RestKit.h>
Result: When trying to compile I get the error "include of non-modular header inside framework module 'networkModule'"
I have set the flag for "Allow Non-modular Includes in Framework Modules" to YES in the build settings for the Project Target and the Module/Framework target.
I went to the Cocoa pod project and have tried setting the visibility of the RestKit.h Header file to "Public" in the target membership (which of course is not a good solution to mess with the cocoa pods environment)
I am not able to compile. I still get the same error.
Is it possible in the first place to create a Cocoa Touch Framework with dependencies to a cocoa pod managed framework?
Btw. My first idea of creating a private cocoa pod didn't work out as well, as it doesn't seem to be supported, although I am using the prerelease of cocoa pods 0.36 with support for swift.
You should be able to make your won private Pod. You just need to make a podspec for it. Here is an example of one.
Pod::Spec.new do |s|
s.name = "Commons"
s.version = "1.0.0"
s.summary = "Common code for my iOS projects."
s.license = {:type => 'Commercial', :text => "Copyright (c) Dan Leonard(Or Your Name?). All rights reserved." }
s.source = { :git => "https://github.com/PATHTOPOD", :tag =>
s.version.to_s }
s.homepage = "https://github.com/PATHTOPOD"
s.requires_arc = true
s.ios.deployment_target = '9.0'
s.subspec 'Core' do |ss|
ss.source_files = '*.swift'
end
s.subspec 'Menu' do |ss|
ss.source_files = 'Menu/*.swift'
ss.resources = ['Menu/*.storyboard', 'Menu/*.xcassets']
ss.dependency 'Alamofire'
end
end
Then Inside your project you just have to do pod init open your podfile that was just created and add this
source 'https://github.com/CocoaPods/Specs.git'
xcodeproj 'YOURPROJECT.xcodeproj'
platform :ios, '9.0'
use_frameworks!
pod 'Commons', git: 'https://github.com/PATHTOPODPROJECT'
#pod 'Commons', :path => '/Users/YourUser/Path/To/Project/commons'
pod 'KeychainSwift'
pod 'SQLite.swift', git: 'https://github.com/stephencelis/SQLite.swift.git'
Now in this example Podfile Commons is stated twice the second is commented out. If you uncomment it and comment out the first then do pod install in your projects folder from the terminal. This will make a DevelopmentPod which is a pod that is local. This way you can make changes to the pod locally within Xcode. No switching and pod installing every time you make a change.
You will import the pod just like any other by putting
import Commons not #import <Commons/Commons.h> That is how you do it in Objective C not Swift
Once you have a working version commit it to git hub and point your project to the the github version.
Hope this helps.

Resources