I have an app (let's call it MyApp) written in Swift with the following targets :
MyApp
: the main targetMyAppKit
: a target building a framework for code that is shared between the app and its extension(s), mainly the API backend and database handlingMyAppWidget
: a Today View Widget (or whatever it's called now) which uses the MyAppKit
framework.The MyAppKit
framework is linked into each target that uses it, namely MyApp
and MyAppWidget
. Enter Cocoapods : I used to have the following Podfile structure :
platform :ios, '8.0' use_frameworks! target 'MyApp' do # Mostly UI or convenience pods pod 'Eureka', '~> 2.0.0-beta' pod 'PKHUD', '~> 4.0' pod '1PasswordExtension', '~> 1.8' end target 'MyAppKit' do # Backend pods for networking, storage, etc. pod 'Alamofire', '~> 4.0' pod 'Fuzi', '~> 1.0' pod 'KeychainAccess', '~> 3.0' pod 'RealmSwift', '~> 2.0' pod 'Result', '~> 3.0' end target 'MyAppWidget' do # Added here event though the target only imports MyAppKit but it worked pod 'RealmSwift', '~> 2.0' end
The aim here was to expose only the MyAppKit
framework to the other parts and not all its pods (e.g. I don't want to be able to import Alamofire
inside the main app). However, starting with the Cocoapods 1.2.0 RCs, pod install
failed with the following error : [!] The 'Pods-MyApp' target has frameworks with conflicting names: realm and realmswift.
. It used to work because the pods were declared for the extension but only embedded in the host app (see this issue for more info). So I removed the pods from the widget's target, leaving me with just a blank target 'MyAppWidget'
line.
With this configuration, pod install
runs fine but compiling fails at the linking stage for the MyAppWidget
target : ld: framework not found Realm for architecture x86_64
. This can be fixed by explicitly adding both Realm.framework
and RealmSwift.framework
to the "Link Binary With Libraries" section and the following build setting in the target's Pods-MyAppWidget.[debug/release].xcconfig
:
FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Realm" "$PODS_CONFIGURATION_BUILD_DIR/RealmSwift"`
However, whenever I run pod install
, the build settings are naturally reverted and I have to add the build settings again.
I see the following solutions :
post_install
hook adding these settings each time but it does seem "hacky" and after a few misguided tries, I have found no API reference and don't know how to add these settings to the MyAppWidget
target through script.Change the Podfile to the following structure (or even wrapping it in an abstract target) :
[...] target 'MyAppKit' do # Backend pods for networking, storage, etc. pod 'Alamofire', '~> 4.0' pod 'Fuzi', '~> 1.0' pod 'KeychainAccess', '~> 3.0' pod 'RealmSwift', '~> 2.0' pod 'Result', '~> 3.0' target 'MyAppWidget' do inherit! :search_paths # Because else we get the "conflicting names" error end end
Which seems logical to me in the sense of "the widget should know where to look during linking but doesn't need the pods per se" but this doesn't add the aforementioned build settings (I probably misunderstand the (edit: it actually works, but not with an abstract target). This idea came to me because in older versions of CocoaPods, the solution was apparently to add :search_paths
inheritance)link_with
, which is now deprecated.
MyApp
target, however this conflicts with my goal of not having access to "backend" code in the main code (it might be purely esthetic?).So, here's my question : what's the best way to integrate pods in a framework shared between the main app and the extension while still being able to compile, without tweaking around and manually adding stuff?
Cheers and thanks in advance!
Following Prientus' comment I've explored the possibilities of abstraction and inheritance. The underlying issues I've now uncovered are actually manifold :
inherit! :search_paths
) from an abstract target.MyAppKit
, but this exposes all these pods to MyApp
's code (which I want to avoid), and there still is the issue of linking the Realm framework (because actually the widget uses the tiniest bit of a getter and therefore needs it).Using this last option and manually linking Realm.framework works but is suboptimal regarding my intents and what used to work. Some of these issues seem to be a bug according to various issues on Cocoapods' GitHub. I've added my own issue and will update when I have news.
Create a Cocoa Touch Framework Xcode project Under the hood, a CocoaPods library is a Cocoa Touch framework library. Launch Xcode and create a new project, choose Cocoa Touch Framework . Enter the name SwiftyLib , select the checkbox Include Unit Tests .
use_frameworks! tells cocoa pods to use dynamic libraries, and was very prevalent at one point due in particular to swift not supporting static libraries, meaning there was no choice - however you often don't need use_frameworks! anymore. As of Xcode 9 beta 4, and CocoaPods 1.5.
So, what gives :
import RealmSwift
statement.The fixed and working Podfile therefore is :
platform :ios, '8.0' use_frameworks! target 'MyApp' do pod 'Eureka', '~> 2.0.0-beta' pod 'PKHUD', '~> 4.0' pod '1PasswordExtension', '~> 1.8' end target 'MyAppKit' do pod 'Fuzi', '~> 1.0' pod 'RealmSwift', '~> 2.0' pod 'Alamofire', '~> 4.0' pod 'KeychainAccess', '~> 3.0' pod 'Result', '~> 3.0' target 'MyAppWidget' do inherit! :search_paths end end
And that's it. I would say that the old behaviour was more obvious and didn't require reading up on "podfile target inheritance". I did learn a lot though. Cheers!
I don't know you. But for me, it's totally legit and reasonable to have the extension and the host app contain all the pods that a framework defines. And this is what i mean:
def shared_pods pod 'Alamofire' end target 'Framework' do shared_pods end target 'Host' do shared_pods // Some other pods end target 'Extension' do shared_pods end
I know you are worried about but if you think about it, all those 3rd party frameworks you use, they all have dependencies. You don't have to worries about them because Cocoapods takes care of them for you. If you want to utilise that, then you'll need to put a local pod entry in the list.
target 'Host' do pod Framework, :path => '../Framework' end
But then you have to maintain the podspec
file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With