As i understand the big change from ios dynamic framework and static is that static is linked statically to the code at link time (prior to launch) and dynamic is linked at launch/runtime
Now i have a test project:
My project have a dynamic framework linked to it - A.framework.
import A.framework
A.framework have a framework embedded inside of it - B.framework
In my main project i want to use classes from B.framework
Now i see that with a simple import statement in the main project:
import B.framework
It actually work and i can use code from inside of the B.framework which is embedded in linked A.framework
How can it be? is it something that is safe and reliable to use? How does the main project recognize the B.framework?
What about cases where the main project directly link the B.framework to the project? in this case i see many "duplicate symbol errors" at link time
Most importantly how can i build A.framework while not embedding B.framework inside of it, while off course using its classes and functions
Any clarifications will help :)
As you note, linking B.framework would lead to duplicate symbols. This is why A.framework should not embed B.framework. You must never embed a framework in another framework if there is any chance that the consuming application will care about the embedded framework (in practice, this means you really should just never do it).
A.framework was incorrectly packaged. If you packaged it, you should remove the embedded framework and link everything at the application layer. If someone else packaged it, you should open an issue with them to correct this error. This issue is not new to dynamic frameworks. It was equally a problem with static frameworks. The only appropriate time to link dependencies is at the application layer.
(There is an exception if you control the entire ecosystem (e.g. Apple). Then things like umbrella frameworks are acceptable. But you're not Apple.)
EDIT: It is ok to link, but not embed, a shared framework into another shared framework. The key is that the only copy of the shared framework needs to come from the top-level application. Since that final link step will happen at load, then you won't have duplicate symbols because there is only one copy of the shared framework. Just don't embed the sub-framework into yours.
For example:
Create project with framework target
Drag in GMA.framework to framework target (this will cause it to link but not embed)
Create App target
Have app link both GMA.framework and your test framework. This will work fine without collisions because there is only one GMA.framework, and it's only embedded in the app.
Related
Trying to prepare a single dynamic framework to my customer. My framework (A.framework) uses third-party recognition static framework (B.framework). I can't provide separate A and B frameworks to the customer.
Ideally B.framework should be built and included into my A.framework's binary, so the customer's app will only embed A.framework without any additional actions to link with that third-party app.
What I did:
Added B.framework to the project.
Added B.framework to "Linked Frameworks and Libraries" in the corresponding target.
Built A.framework.
Created a demo application and included A.framework to the project.
Added A.framework to "Embedded Binaries".
Demo app's build fails with message "Missing required module 'B'" (despite the fact that it is used in A.framework only).
Note:
I neither created any modulemap files for B.framework, nor additional run scripts
Making A.framework static is not acceptable because it includes some resources (storyboards, icons and some other files)
Tried to make un-recommended "umbrella" framework but got stuck on loading B.framework's bundle in demo app
Tried to make fake "umbrella" framework by simply copying B.framework inside A.framework, but got 2 problems - huge size of A.framework and Mach-O error while exporting the demo application (because of Mach-O difference between dynamic A and static B frameworks)
Any ideas would be highly appreciated!
UPD 1: This is not about umbrella framework because the proper umbrella framework implementation requires to load sub-framework from bundle which is not good. The fake framework implementation (sub-framework simply copied to umbrella) won't work for release because of different Mach-O values - dynamic and static. Plus fake umbrella framework has a huge size because sub-framework is being fully copied inside umbrella.
UPD 2: Created a small test project: StaticFrameworkTest which has 3 sub-projects:
Demo-application with dynamic framework dependency (framework A) and shouldn't know anything about framework B
Dynamic framework with static framework dependency (framework B) which ideally should be included in A framework's binary.
Static framework B
A static framework is by definition a fat static library combined with any additional required resources. As such you can embed your third party static library inside your own and also include images, storyboards, plist etc.
You can't do that in a static library (i.e. *.a), but in a static framework you can do.
See for example https://www.raywenderlich.com/65964/create-a-framework-for-ios for details on how to do that (at the end of the article it creates the static *.framework out of the static *.a and some resources)
A dynamic framework can never embed a third party static library. The main application that imports the dynamic framework will always have to also explicitly link against the static library, which it seems that is not what you want.
They asked me to create a cocoatouch framework that bundles all off our 3th party frameworks. So that we can use that framework all over the company.
Now I think it's better when you make the application responsible for adding the dependencies that your framework need but this not possible here.
So I created a framework added some frameworks like PureLayout.framework, etc.. and added them to the linked frameworks and libraries.
It compiles and delivers a .framework but when I add that framework to the project and run it, then I get this error:
dyld: Library not loaded
Reason: image not found
So I think they are only linked against and not embedded in the framework.
How can I really embed them?
So in Xcode 6, we finally have the possibility to create and distribute our own libraries as Frameworks (as opposed to Static Libraries before).
The question, is it possible to "embed" another framework or library directly inside the framework rather than ask the end user to link them?
The reason is as follow: Creating and distributing frameworks for other people often requires them to manually add whichever framework we link against.
That's fine when these libraries are a default ones that can be added straight from Xcode, but when we need to link against other public frameworks.
One example would be if the framework uses AWS as a backend, it's a bit overkill to ask developers to also download a specific version of their SDK and link against specific bits that are required. And it becomes more overkill when we need others for performance logging or more.
On OSX, there is the possibility to use Umbrella Frameworks, but it's undocumented on iOS.
Thank you.
Recently done this myself on iOS, unfortunately any framework that has sub-frameworks must also be linked to in the project the parent framework gets used in.
Create the framework as per normal, and include the other frameworks under that framework (it should be an aggregate target).
Then build the parent framework, and link this into the main project. Attempt to compile and it will mention that it needs it sub-frameworks also linked. You can then link these sub-frameworks in addition and it will compile.
This is unfortunately a limitation of Xcode/iOS as it currently stands.
Base on this question
Why don't iOS framework dependencies need to be explicitly linked to a static library
I read the selected answer and still don't understand so I made an example project
Test Project on Github
In the test project, I remove all framework from Link Binary With Libraries and File navigation for both main project and the static library (including Foundation.framework and UIKit.framework too), basically, both project link to 0 frameworks.
Questions are
In static library, it's including MapKit/MapKit.h without referencing the Mapkit.framework to the project, why is its still working?
In main project, I remove UIKit.framework and Foundation.framework from the project, why is it still working?
Since it's working for now, will there be any issue later?
Thank you for your comment.
P.S. By working, I mean I can run on the simulator and I can archive the main project without any error.
Edit 25/07/2014
I tried with the real app that I'm working on, it's the same.
I highlight Foundation, UIKit, CoreData and 10 another frameworks in File Navigation, well, all of them.
Uncheck the target in Utilities Panel --> Target Membership
Build : Pass, Run : Pass
Every functionality of my app is still working as expected. I don't get this.
Check your project build settings. Underneath LLVM 5.1 — Language — Modules you should see the option 'Link Frameworks Automatically'. In your case it sounds like it's set to 'YES', the default.
In that case, instead of producing an error when you reference a class that the compiler doesn't know, it'll figure out which Framework contains that class and link it. In your code it'll be MKMapView or one of the other MapKit classes that triggers the linkage.
EDIT: from the relevant 'What's New?' document:
Auto Linking is enabled for frameworks imported by code modules. When
a source file includes a header from a framework that supports
modules, the compiler generates extra information in the object file
to automatically link in that framework. The result is that, in most
cases, you will not need to specify a separate list of the frameworks
to link with your target when you use a framework API that supports
modules.
Another way of looking at it is that the compiler is smart enough to mutate #import to #import when the framework has been built appropriately. All system frameworks have been.
To elaborate #Tommy's answer, a framework that supports modules satisfies the following 2 conditions:
Under Build Settings > Packaging
Define Modules is set to YES
Module Map File exists.
So, if you're certain that the framework you're using in your code modularizes like that, you can choose to not explicitly add it in the link phase as it will be automatically added as long as in the project file, under Apple Clang - Language - Modules, The option Link Frameworks Automatically is set to YES.
I'm working on an iOS app where I use third party libraries. I want to migrate my project to use ARC, but the third party libraries are still using the old memory management. So I want to separate third party code and put it in a separate project without ARC, and then somehow link that project into my iOS-app project, so that they will be built together using the same configuration.
Is this possible to do in a very simple way, or am i better of just turning off ARC for the individual files? (seems very tedious..)
Can I use a workspace? Where one project is my iOS app and the other just contains third party code?
I've played around a bit and googled a lot, but there just doesn't seem to be any simple soultion, or am I wrong?
So I figured it out myself, with a lot of help from different blogs. Something this basic should be more trivial and well documented... But here we go, this is what I did to get a library for AsiHttpRequest:
Create a new iOS project. Select the 'Cocoa Touch Static Library' template. Call it whatever you like. You don't want to tick 'Use automatic reference counting', since AsiHttpRequest does not support it.
Select a location for your library project (will matter later on).
Delete the default .h- and .m-file created by Xcode.
Drag and drop the AsiHttpRequest files into the project
You can add the frameworks that AsiHttpRequest is dependent of, but you will have to add them to your main project anyway, so it is not necessary.
Try to build the project, it should do so without errors.
Open your main project
From finder, drag your library .proj-file into your main project (in Xcode, so that it 'lands' onto the main project file)
The library project should now appear under your main project (still in XCode). It should be expandable and you should be able to see the library project files as well. If it doesn't, try closing all open projects and reopen the main project.
Select the main project, and select target. Under Build Phases - Link Binary With Libraries, click the +-sign.
In the list of frameworks you should see your library project (called something like libname.a). Select that file
The newly added file might appear red in the list of frameworks, don't worry, it works anyway. Guess it's a bug.
Still under target, go to Build Settings
Under Header Search Paths add the relative search path to where the library .h-files are. This is relative to your main projects .proj-file. (For example ../some folder/libproject/)
Hopefully your main project will build without errors and the library project will be built at the same time, using the same configuration as the main project.
I have no idea if this is a good approach or if there is some easier way to do it. However, I like this, since I can use the library project in several projects. And if I want to update the library project, I only have to do it in one place, and the other projects will be updated as well, since they all reference the same project.
Edit1:
I had some problems with library projects using objective c categories. I received unrecognized selector sent to instance errors in runtime when trying to call those methods. This problem was solved by following the answer given here.
Go to build settings of the target in the main project and add -ObjC to the entry called Other Linker Flags
Edit2:
I found this template for creating Universal frameworks. I haven't tried it, but I guess something like this would work as well.