cocoad pods - build static framework - ios

Hi have this cocoapod framework, which I distribute in binary mode only (no sources). Basically, I am distributing the output of carthage build, which creates a dynamic framework.
I have been asked to release a static version of this framework as well. I have made a new target in XCode which builds the framework using a static library (using same sources as the dynamic framework).
Eventually, I file ...blabla.a and see it contains only arm architectures.
I have been looking into: https://github.com/Carthage/Carthage/blob/master/Documentation/StaticFrameworks.md - but this just fails for me (at linking state it tries to execute Ld which fails).
My code is only objc, and I am running Xcode 10.2.1.

Version v0.30 and above of Carthage can build static frameworks. Documentation does not say how :)
The link provided above is useless (does not work on modern systems). Instead I used this: https://github.com/Carthage/Carthage/issues/2534#issuecomment-407807111
and got a static framework.
As a bonus, I was able to make a new cocoapod (I added a new podfile, with same name and a static postfix), which links to my static build (in the same repo as the dynamic, just a different path), only diff between those two pods, is s.static_framework = true.
I am unsure how to make this a single pod which will honour use_framworks!, this this method

Related

Xcode development workflow with binary framework

I'm using an xcframework to hide code.
So I've got an App.xcodeproj and the xcframework in one repo visible to contractors.
Then I have another private project which has everything in App.xcodeproj except has the framework project as a sub-project for easier development (instead of the compiled xcframework).
This works reasonably well except I have to keep the internal (private) and external projects in sync. Changes the contractors make to App.xcodeproj have to be manually brought over to the internal project, updating paths accordingly.
Is there a better way to do this?
I solved this by switching to separate Frameworks instead of a single xcframework, and using an xcworkspace.
It turns out Xcode will prioritize an implicit dependency over the compiled framework. So I only needed my app and framework projects in one (private) xcworkspace.
I'm using the fat framework approach to support the simulator, which is a bit uglier than using an xcframework. There was also a wrinkle in that I'm using Metal in the framework, so I had to load a separate Metallib when running in the simulator.
Perhaps at some point Xcode will have a target type for xcframeworks so it can track the implicit dependencies. See here for more on implicit dependencies.

Why Am I Not Able To Embed My Custom Framework?

I have a very simple framework project, MyFramework, that builds successfully. The framework defines a single, global function, myFunction.
The framework also defines 2 global variables: MyFrameworkVersionNumber and MyFrameworkVersionString (These 2 variables were created for me by Xcode)
I have a very simple application project, MyApplication. I am adding the framework to the application project by dragging and dropping the framework package (i.e. the MyFramework.framework in DerivedData that was produced by building the framework) onto the Xcode Navigator and then selecting the framework in the Embedded Binaries section of the project's General tab.
If I add code to MyApplication that references the variable MyFrameworkVersionNumber then I am able to successfully build and run MyApplication.
If I add code to MyApplication that references the function myFunction then I am not able to build MyApplication.
First Update
I suddenly remembered that Carthage does exactly what I am trying to do. So, I used Carthage to build MyFramework and Voila! - I can drag/drop the Carthage build of the framework into the application project and successfully use it. So now my inquiry has become: What does Carthage know that I do not?
Oh for heaven's sake! It turned out to be so simple. MyFramework was being built for a Generic iOS Device (arm64 architecture). MyApplication was being built for a simulator (x86_64 architecture). As soon as I matched those two up all was well. The reason that the Carthage build worked so well is that it produces a universal binary (i.e. one that contains both architectures).
I still do not understand why the MyFrameworkVersionNumber global variable was able to be accessed regardless of the architecture. But I am okay with deferring that little mystery as some arcane bit of information that will be revealed in the goodness of time (perhaps something such as that it is in a header that is structured the same for both architectures).

Trying To Merge Static Frameworks Using Carthage

carthage version: 0.25.0
xcodebuild -version: 8.3.3
Are you using --no-build? No
Are you using --no-use-binaries? No
Are you using --use-submodules? No
Are you using --cache-builds? No
Cartfile
github "rs/SDWebImage"
github "AgileBits/onepassword-extension"
github "hsousa/HCSStarRatingView"
github "sugoi-wada/facebook-ios-sdk" "780a72cd6086c57939d83a7143462ae13dfb9a74"
github "CSStickyHeaderFlowLayout/CSStickyHeaderFlowLayout"
github "card-io/card.io-iOS-source"
I'm able to build my dependencies using a build script as outlined in the documentation here. Now I'm trying to merge my static frameworks into a dynamic one and running into problems.
To start, I'm adding a single framework to the dylib target, I've disabled bitcode and added the following OTHER_LDFLAGS: -all_load, -ObjC. I'm starting small with the HCSStarRatingView. It has been added to they dylib target and that builds.
To test this new combined framework I've added an app target. I can import the framework target and the app target recognizes it. However when I try to declare a new view with let view = HCSStarRatingView(frame: .zero) I'm getting the following error: Use of unresolved identifier 'HCSStarRatingView'.
I'm assuming that I don't import HCSStarRatingView directly because it's now part of my framework target. I'm also assuming that I don't have to add anything to my dynamic framework target's umbrella header to expose the linked static frameworks inside. Are those assumptions correct? Any idea what I could be missing here?
i think your assumption might be wrong.
I have a similar setup and got it working by importing both: dynamic framework and the static library that it depends on. eg:
import framework-target
import HCSStarRatingView
furthermore i had to make sure that the app target included the carthage build path

Embedding frameworks inside closed-source Swift framework

Our company wants to distribute a closed-source SDK for iOS to our clients. I've been using Cocoapods to build the framework and built an example app making use of it. Previously the app worked fine on the simulator as well as when deployed on the device. However, I was also embedding the Pods.framework file in the app itself. One other piece of information that may be of interest is that the framework is written in Swift, the included cocoapods dependencies are both Swift and Objective-C.
I wanted to make the pods requirements easier to manage so the user doesn't need to be concerned with them and tried to embed the Pods.framework file inside of the SDK we're building - so I removed the steps to Embed Pods Frameworks and Copy Pods Resources from the example app, leaving them only in the framework, I also removed Pods.framework as a dependency of the example app, leaving it only in the SDK. This seemed to work in the simulator, but the app now crashes on mobile device with dyld: Library not loaded error.
Upon researching it, I stumbled into a few related discussions:
https://github.com/CocoaPods/CocoaPods/issues/344 https://objectpartners.com/2014/06/25/developing-private-in-house-libraries-with-cocoapods/
However, the suggested solution of using private pods does not look like it would work for us, it's my understanding that the source code in the private pod would still be open, and we can't share it with our clients.
Could someone advise on a solution that would work in this case?
OK, I finally have a more durable solution. It's a modified, cleaner version of my old one now that I understand how Xcode links in my Swift sub-frameworks better
Problem that makes distribution/compilation a bit ugly:
Since Swift standard libraries aren't bundled on the device like Obj-C, nor are they guaranteed to be stable between versions yet (stable binary interface promised in Swift 3: https://github.com/apple/swift-evolution#development-major-version--swift-30) we have to make sure the entire project is compiled against the same version of Swift. That means the guy using your closed-source framework has to be using the same version of Swift in their Xcode for their project as you did for compiling the library, even if he's not using Swift in his code because ultimately it's his version of Swift that gets bundled into the app and your SDK runs against. This is only an issue for closed-source frameworks because open-source ones will always be compiled against the same version as final project. Possible workaround is to restrict clients to same version you use or distribute multiple compilations (i.e. Swift 2.1 and Swift 2.0). To remedy this, you could provide users with copies of binaries compiled against multiple versions of Swift.
Aside from that, here is what I had to do during compilation/distribution to make a binary framework that works in Swift:
When building the framework:
In project target, make sure to add Pods.framework to Linked Frameworks and Libraries (make sure this is a pre-compiled RED version of Pods.framework, I had a black compiled Pods.framework in the same directory which built fine but then resulted in a framework that would cause the project to complain about missing armv7 architecture during linker phase in later project)
In Build Settings, under User-Defined section, add a field called BITCODE_GENERATION_MODE and set it to bitcode
DO NOT #import any frameworks in your bridging header, all instructions telling you to do that are leftover from Swift 1.0-1.2 days, you don't need it anymore and it does more harm than good (the later project will complain that it can't find these headers that aren't even exposed to it)
Change build target to Generic iOS Device, Archive and Export the framework
When building the project using the framework:
Drag and drop the framework into the project, in General tab add it to Embedded Binaries and Linked Frameworks and Libraries (you only need to add the framework itself, not the sub-frameworks or the pods file)
In Build Settings tab, add a new path to Framework Search Paths: $(PROJECT_DIR)/MyFramework.framework/Frameworks
Build the project

Can you make a Podspec with conditional dependencies?

I want to know if you can define dependencies on an static lib in a podspec depending on the type of the Target that the Pods project is going to be linked to.
I need to be able to NOT add a dependency to an static lib if the target project is itself an static lib.
Why I need this?
I've built a podspec for cocos2d-iphone v3 and using it in a personal pet project. Works perfectly when you use it as a dependency for an executable. Unfortunately it doesn't works well the way I've organiced my project:
It's a multiplatform project app consisting in two xcode projects for the ios and osx executables. Both link to an static library with shared code (another xcode project). I've made the cocos2d a dependency for that last static library.
Unfortunately, cocos2d has a dependency over lib z, which is also static. As you can't link two static libraries the link phase fails. The error message is as follows:
file: -lz is not an object file (not allowed in a library)
It's easy to remove that dependency but I would prefer the process to be more easier for the end user.
My approach to solve this problem would be add a conditional in the podspec so the lib z only is added if the project is not an static library. I guess it can be done with pre_install hooks, but they are discouraged by the cocoapods team.
Is there any other way to accomplish this?

Resources