iOS: Dynamic Framework based on static library dependency - ios

I am working on creating a Dynamic Framework (Cocoa Touch Framework) that has to use a VideoKit lib as a core. Library is a .a file with a number of header files (about 20) and example ViewControllers in ObjC (you can grab demo SDK from: Link). In the future my Framework will be providing a player widget written in Swift (based on sources from ObjC and some other specific features) to DemoApp. But for now I cannot handle dependency in my Dynamic Framework in a proper way.
My first approach was to add the SDK to Framework project. I followed instructions provided
by a producer but instead of adding SDK to the App project I added it to the Dynamic Framework Project. There was no module map file and no Umbrella header for VideoKit library so I created them. The result was that some errors went away (related to Headers in the module). I also removed Controllers source files from compiled sources because I did not need them - I just wanted to run the core in the first place. I got stuck on errors saying that there are no symbols for various architectures (x86, arm64).
My second approach was based on what I read on SO and other resources. I moved the dependency to the App project. In the Dynamic Framework Build Settings I added paths to Headers and Library sources from VideoKit SDK that are now stored in the App Project. Again, I am stuck with ā€˛Undefined symbols for architectureā€¯ errors.
I would appreciate if someone can guide me what I am doing wrong - it is my first time when I have to build framework with dependency on static library and cannot use Dependency Managers to resolve it - actually it is my first time on building iOS Framework ever.
Here is a link to the repo with my actual work on this problem: Repo
I had to remove VideoKit SDK from project because of license restrictions. You have to download it and add it manually.

Related

How to add a local framework to a Flutter plugin for iOS

So, I've been struggling for some time with embedding a third party framework into a Flutter plugin I'm writing for iOS.
The plugin is fairly straightforward, mostly just forwarding calls and events to/from the third party framework.
The third party framework in questions is for controlling a Sphero robot (RobotKit.framework).
This particular framework is an umbrella framework, with several sub frameworks within.
To include this .framework in my plugin, I've tried a lot of combinations between
preserve_paths
vendored_frameworks
resource
xcconfig
in the .podspec file to link to the RobotKit.framework package (inspired by blog), but without luck.
I can use
vendored_frameworks = 'Frameworks/RobotKit.framework'
to get Xcode to detect the package and import the headers, but when building I get an error linking to a header in one of the sub frameworks (RobotKitClassic is a sub framework):
fatal error: 'RobotKitClassic/RobotKitClassic.h' file not found #import <RobotKitClassic/RobotKitClassic.h>
Am i approaching this the wrong way, or is there a trick that I'm missing?
I ended up working around this by implementing the plugin functionality directly in my main app project instead of in a separate Flutter plugin.
Doing this, i can add the RobotKit framework to the Runner project and it links, builds and runs just fine.
IF your case is nested frameworks to use the result in app then nesting frameworks isn't allowed in IOS , here from apple forums , I faced error like can't load frameWork #rpath to do this all code in dynamic framework must be pure code not pods or plugins

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

iOS8 framework library linking WITHOUT Pods

Imagine the following scenario;
I'm developing a cocoa touch framework that requires SomeLibrary (e.g. AFNetworking). My framework is going to be included into someone's project that might require SomeLibrary as well.
How do I accomplish this without running into these nasty duplicate warnings when I include AFNetworking into my framework directly (either via source code or Cocoapods)?
I've tried it with Cocoapods on both projects (my framework, and a test project that includes my framework), but that results in duplicate code warnings as well.
When I don't add AFNetworking into my framework development project, the compiler can't find the required files, which is why I can't build it. I've tried with including AFNetworking's source code directly into the main project, and using the pod, but in both cases the AFNetworking/AFNetworking.h import in the framework project fails.
How can I do this without making a pod out my framework (which isn't really an option)?
I've found this related answer, but I don't know what search path to set for the framework project in order to find a library of the master project;
https://stackoverflow.com/a/23123725/1069487
Any help would be highly appreciated!
You will have to build your framework linked against static library.
You build AFNetworking as a static library (that will give you a .a file as AFNetworking.a)
You build your framework that link against your static library. But be aware that the library won't be embedded in your framework (there is no way to include static library into framework on iOS). Your framework is able to use AFNetworking API because it is linked against it.
any project that use your framework and use AFNetworking methods of your framework need to link with the static library AFNetworking.a that you should provide as a standalone file beside your framework.
See iOS-Framework here for more details : https://github.com/jverkoey/iOS-Framework

handling dependencies for iOS Framework project

I've created iOS Framework project using this method: https://github.com/jverkoey/iOS-Framework
Works pretty neat but I'm a little confused on how to include libraries/frameworks that are needed by my framework to work and, in particular, how to do it so that in case 3rd party client app that uses my framework can include these libs as well without conflicts.
Let's say my framework code needs these two things:
FacebookSDK.framework
libFlurry.a
The first one is an iOS Framework. When I add it to "Link Binary With Libraries" phase in my Framework and try compile the client project that uses my framework the linker complains about missing symbols - I need to add FacebookSDK to the client project which is great: there is no possibility of conflicts if client apps wants to use Facebook.
However when I do the same with Flurry static library I get duplicate symbols error when compiling client project. Which confuses me a bit, because isn't FacebookSDK.framework just a packaged static library?
ukaszs-iMac:FacebookSDK.framework lukasz$ file Versions/A/FacebookSDK
Versions/A/FacebookSDK: Mach-O universal binary with 3 architectures
Versions/A/FacebookSDK (for architecture i386): current ar archive random library
Versions/A/FacebookSDK (for architecture armv7): current ar archive random library
Versions/A/FacebookSDK (for architecture cputype (12) cpusubtype (11)): current ar archive random library
So my questions are:
why a library embedded in framework (like Facebook) is not linked to my Framework project product, whereas library included as .a file is?
how to include .a file in my framework so that it does not produce duplicate symbols error when client app using my framework also needs this particular static library?
For the use case you are describing, you should be linking to these external libraries from your application, NOT your own framework. It can be one or the other, but it can't be both.
If you decide that these dependancies belong as the responsibility of the application, you would remove them from "Link Binary With Libraries" and any other explicit linking configuration, and just project your framework project with the path to these frameworks and libraries so it can find the symbols (but not link against them) at compile time (i.e. the path to the libraries should be included LIBRARY_SEARCH_PATHS).
Use cocoapods , it's easy (http://cocoapods.org/)
Your application developers will have to include the podfile and download the dependencies.
While developing your SDK use a reference application/demo app on top of the SDK to simulate this.
You shouldn't link anything when building your framework but just create a *.a binary with your framework's objects.
Also you should not include code from other libraries in your framework as client applications may be adding the same libraries directly or requiring different versions of them, thus creating conflicts.
Off course you can reference *.h header files from other libraries in your framework in order to compile your objects.
As a result the installation steps for your framework should detail other required frameworks/libraries needed, their supported versions, how to add resource files (if any), etc. Just some of the many reasons why you may want to consider Creating a CocoaPods' podspec instead.
You should use CocoaPods. Your dependency on Facebook can be done by linking against the CocoaPod.
If you want to include that particular version of Facebook in your pod, you can put it in your repo and use the vendored_frameworks property to refer to it.
Similarly if you wanted to vendor libFlurry.a, you could do so using s.vendored_libraries.
For system libraries, you don't need to vendor them, e.g. libZ.a.
I strongly recommend creating your CocoaPod using pod lib create YourPodName. They've recently changed the mechanism for how this works and it's really nice.
You can create an Example project that shows how to use your code in context of an app.
Then one of the other neat things I just learned about, someone can do pod try YourPodName and it will automatically download, integrate and run the Xcode project.
CocoaPods is worth the trouble.
I am building my framework project using CocoaPods.
The framework uses some 3rd libs from CocoaPods.
Podfile specifies to install dependency on target of the framework.
When I build the framework, it includes all libs in the binary.
If I add use_frameworks! in Podfile, when the framework is built, it will not include 3rd party libs.
Use CocoaPods dependancy manager. Here's a good guide,
7 miniute video tutorial
Mostly if you install third party frameworks you can install with cocoapods (which is really nice, I would definitely do that) or they offer you to download the framework and include it in your Project.
If you decide to download the library and include it there is normally a list of frameworks you need in the "Getting started" guide.
Means: Offer them to install using cocoapods and to download your library but do not include anything else, give them a list what they need.

Library? Static? Dynamic? Or Framework? Project inside another project

I have an existing iOS app and want to add a large chunk of code that I've been developing as another project just for ease of testing. The new chunk basically deals with saving an image to various sharing services, etc.. Because that sharing code needs a lot of testing and future updating, I was wondering what the best way to incorporate that code chunk into my existing app.
I don't know if it should be a static library, dynamic library or a framework, and honestly, I'm not really sure what the difference is, or how I should go about it and get it set up in Xcode.
All I know is that I need/want to keep a separate testing and updating app for the sharing code and have the main app use it.
First, some general definitions (specific to iOS):
Static library - a unit of code linked at compile time, which does not change.
However, iOS static libraries are not allowed to contain images/assets (only code). You can get around this challenge by using a media bundle though.
A better, more formal definition can be found on Wikipedia here.
Dynamic library - a unit of code and/or assets linked at runtime that may change.
However, only Apple is allowed to create dynamic libraries for iOS . You're not allowed to create these, as this will get your app rejected. (See this other SO post for confirmation and reasoning on such).
Software Framework - a compiled set of code that accomplishes a task... hence, you can actually have a static framework or a dynamic framework, which are typically just the compiled versions of the above.
See the Wiki on Software Framework for more details.
Hence on iOS, your only option is basically to use a static library or static framework (the main difference being that a static framework is distributed as a compiled .a file most often, whereas a static library may simply be included as a subproject - you can see all of the code - which is compiled first and its resulting .a file used as a dependency by the project).
Now that we're clear(er) on these terms, setting up a static library and supporting media bundle for iOS isn't too difficult, and there are many tutorials on how to do such. I personally would recommend this one:
https://github.com/jverkoey/iOS-Framework
This is a pretty straight-forward guide and doesn't have the disadvantage of dealing with "fake static libraries"... check it out for more info...
Once you've created your static library, it's as easy as including it as a submodule within Git for use across different projects.
Good Luck.
EDIT
Regarding a subproject within a project, as far as I know, to get this to work/compile correctly, you essentially have to set up a compile chain where the subproject is compiled first, which creates a static framework .a file that is used as a dependency by the project.
Here's another useful tutorial which talks about this:
http://www.cocoanetics.com/2011/12/sub-projects-in-xcode/
EDIT 2
As of iOS 8, Apple now permits developers to create dynamic frameworks! (Note: your app must have a minimum target of iOS 8 to include a dynamic framework... back porting isn't allowed.)
This has been added as a new project template. In Xcode 6.1, this can be found at:
New Project -> iOS -> Framework & Library -> Cocoa Touch Framework
Mach-O file format(Mach Object - .o)
In iOS world every source file is converted into object files - ABI[About] Mach-O file[About] which will be packaged into a final executable bundle(application, framework), file (library) and it's behavior is determined by Mach-O type[About]
Package is a directory which behavious itself as a file - opaque file. It is created for user experience to complicate making some changes into internal structure that can cause unpredictable program behaviour. Package is used in Document Package or with a Bundle. You can use Show Package Contents in a Finder
Bundle is a directory with a specific structure to organize a binary(executable code) and resources for that code(e.g. images, nibs... Assets.car file[About]).
Bundle contains Info.plist[About] file. Bundle was created for developer experience. Also it can be packaged. There are several types of bundle:
application bundle - Application target
framework bundle and versioned bundle as a subtype - Framework Target
loadable bundle(aka plug-in bundle) - '... Bundle' (UI Testing Bundle, Unit Testing Bundle) - can be loaded at runtime. .bundle extension for Mac OS
[Mac OS] XPC Service - Cross Process Communication is a kind of Inter Process Communication (IPC). It can be used as a module on a different process(managed by launchd root process)[About]
others(dSYM[About] bundle)
Application - .ipa, .app[About] - packaged application bundle - launchable program.
Application extension[About] - from iOS v8 - extends functionality of Application which are available when user interacts with other application. App extension as a bundle is a part of Containing app but it is run on their own sandbox(processor, memory...), app which try to use app extension is called Host App. Types of extension app:
Action
Share
Photo Editing
Today aka widget
...
to share common code and resources. It's available when Deployment target is iOS 8+.
Tests - packaged loadable bundle which is used to test a binary. Plug-in architecture allows us to add a new functionality(test cases) as a separate module into existing binary
Libraries and Frameworks
[Library vs Framework]
Martin Fowler on InversionOfControl
A Library is essentially a set of functions that you can call, these days usually organized into classes. Each call does some work and returns control to the client.
A Framework embodies some abstract design, with more behavior built in. In order to use it you need to insert your behavior into various places in the framework either by subclassing or by plugging in your own classes. The framework's code then calls your code at these points. The main control of the program is inverted, moved away from you to the framework. This phenomenon is Inversion of Control (also known as the Hollywood Principle - "Don't call us, we'll call you"
Libraries and Frameworks on iOS
They can help you to solve: modularity, reusing, encapsulation, improve build time
Library is a collection of Mach-O object files[check static or dynamic] compiled for one or more architectures.
Static library - .a(aka static archive library, static linked shared library[doc]) - When you add it into your application the static linker during compilation time will merge the object files from the library and package them along with the application object files into one single executable file. The disadvantage is a big output file
From Xcode 9.0, Swift static library is supported.
Dynamic library - .dylib(aka dynamic shared library, shared object, dynamically linked library[doc]) is dynamically linked with the app's executable at load or runtime, but not copied into it. On practice app's package will contain Frameworks folder with .dylib file. All iOS and macOS system libraries are dynamic. The disadvantage is a slow launch time since all dynamic libraries should be copied and linked.
[iOS static vs dynamic library]
[Static vs dynamic linking]
Text-based stub library - .tbd[About], it is a text stub of dynamic library which is located on a target device. As a result you should not package a dynamic library into your bundle. It has a size effect.
Framework aka binary framework - .framework is a not packaged framework bundle(to allow developers to easily take a look at headers and resources) which contains a compiled static or dynamic library, header files and resources.
Static framework contain a static library packaged with its resources.
Dynamic framework aka Embedded framework - from iOS v8 - contains the dynamic library and resources. In addition to that, dynamic framework can include different versions of the same dynamic library in a single bundle (versioned bundle). Also Embedded framework is used in App Extension
[Static vs dynamic framework]
Umbrella framework [Aggregate target] is a framework that contains other frameworks. It is not officially supported on iOS and that is why it is not recommended for developers to create them[Official doc]. In actuality it's a set of sub-frameworks(or Nested Frameworks). When you create a framework which has a dependency, a consumer (such as an app) is responsible for adding this dependency along with your framework into the project. As a developer, it's natural to try to find a way to transfer this duty from consumer to your's. As a result you think that Umbrella framework is the rescue but usually it leads to a serious issues with managing versions and complexity of creating and supporting it.
Fake Framework - is a result of specific operations under a static library to create a bundle with .framework extension that will behave yourself as a dynamic framework. This technic was used when Xcode did not support creating a framework since did not have a framework template. One of realisation of a fake framework. With Xcode 6, Apple has added iOS framework support.
Modular Framework[About] - #import it is a framework which contains a .modulemap file inside. Module can contains submodules. The main advantage is that you save a build time with Modular Framework.
Universal Library or Framework (aka Fat) [lipo] [Aggregate target] contains multiple architectures. For example your release build should support a some arch which you can regulate via Build Active Architecture Only [ONLY_ACTIVE_ARCH]
XCFramework[About] was introduced by Xcode 11 and it is a bundle which includes multiple architectures(arm, x86_64...) and platforms(iOS, MacOS...). It should replace a Universal Framework
Dependency[About] You are able to use third party code as a part of your target. It allows you to reuse a code from a lot of sources like - another project, project in the same workspace, another target, library, framework etc.
How to build and use a Static Library:
[Swift consumer -> Swift static library]
[Swift consumer -> Objective-C static library]
[Objective-C consumer -> Swift static library]
[Objective-C consumer -> Objective-C static library]
How to build and use a Dynamic Framework[change to static]
[Swift consumer -> Swift dynamic framework]
[Swift consumer -> Objective-C dynamic framework]
[Objective-C consumer -> Swift dynamic framework]
[Objective-C consumer -> Objective-C dynamic framework]
[Xcode Build System]
[Xcode components]
[Dynamic linker]
You can also create .podspec file for CocoaPods( http://guides.cocoapods.org/making/private-cocoapods.html#1.-create-a-private-spec-repo ) and use it like any other pod with the only difference that it's your private pod and is not visible to outside world(I'm not sure what will happen if your pod should create CoreData model, but that's not the case, as I understand).

Resources