I'm writing an iOS library (SDK) and I need JSON decoding support. There are lots of choices, but the problem is upstream collisions.
If I add, say, JSONKit to my library, and another third party library does the same, then the upstream user who wants to use both my library and the other library will experience a collision.
It would appear then that it's impossible for me to add any third party libraries into my library because of the risk of collisions. How can I use iOS libraries as dependencies of mine without causing collisions for upstream developers?
How can I use iOS libraries as dependencies of mine without causing collisions for upstream developers?
Just specify that that third party library is a dependency of your library, provide appropriate links to the project, and any necessary instructions, version info, etc..
If that package/distro is not well-suited for distribution (e.g. they just have a pile o' source files), then create a public project (fork) which includes a proper static library target.
We're having this same issue. Here's my thoughts on how to solve it (perhaps we can figure out a robust solution together). For versioning control, we're using Git:
One option is simply to prefix all classes within your library with your own identifier. In such, even if the class is part of JSONKit, I'd still prefix it with my own identifier ("JG") to get the classname "JGJSONKit.h". In this manner, it's much less likely that a collision would occur.
This method has the drawback that should a third party repository (such as JSONKit) be updated, however, it's more difficult to pull these changes into our library's version of JSONKit.
This may be acceptable, however, if this code is intended to be exported as a static library (as you'd still maintain full control over the code, and you'd be able to manually pull changes to third party repositories and rename them appropriately, if you should so choose to do such in future updates of the static library).
Another option I've considered is using Git submodules. This solution seems to be better should your library be open source (or at least open source to the developers that have access to it -- not necessarily available publicly). In such, developers could choose to simply not include any submodule that they were already using within their own project.
Ideas?
Edit:
Here's the solution that we came up with:
1) We encapsulated our shared code (which we'd written- not a third party) into a project with a static library target and our shared resources (xibs and images) into a bundle following this tutorial:
https://github.com/jverkoey/iOS-Framework
2) We created a Git repository that included said static library and bundle.
3) We forked all desired third party libraries (so we could make changes to them if desired) and also added the original repos as another remote within each forked repo (so that should changes be made upstream, we could easily pull them into our own fork)
4) We added -objc to the "other linker" flags (important) so that all categories would work correctly during runtime.
All in all, the solution is near perfect for us:
We avoid collisions by either hiding the third party libraries (not including them in the public headers but private instead), or we warn that those libraries are used within the project for publicly exposed headers and that developers should not add them again (they can use the already included code instead, which is kept up-to-date via the simplicity of including them via said Git submodule method)
I hope this helps your efforts too!
I had a look at JSONKit and i see that it isn't a Static Library, it is a src file to copy into your project.
All Classes, etc. in your project must be prefixed with your (cough) globally unique two letter prefix, including classes you copy and paste off the internet.
You would be better off if JSONKit was a library. Your library would depend on JSONKit, but not contain it, and it would be up to whoever building an app with your library to make sure JSONKit was also included and linked against - no collisions (although distributing a library that is dependent on other third party libraries that aren't yours to distribute is inherently somewhat tricky).
If other people are pasting this JSONKit file into their libraries and then distributing them you have only two options*
Modify JSONKit.h & .m, prefixing all symbols (you must do this with any code you include as source)
or choose something else (NSJSONSerialization?).
This doesn't mean you can't have third party library dependencies (or that doing so is dangerous), just that copying a source file into your project is not the same as adding a library dependency.
*arghh, ok 3.. you could weak link all the symbols from JSONKit and leave it up to the library user to supply JSONKit.m, but then still the problem is the other libraries..
TLDR.. avoid JSONKit, it isn't suitable to use 'as is' in a library that you want to distribute.
Related
I know this probably is impossible, but is there any footprint left by cocoapods that can point out (at runtime) if a library was installed using cocoapods or not?
I am currently able to find out if CocoaPods was used or not by getting the list of classes names and then looking for a class with the PodsDummy_Pods_ prefix. But I have no way of knowing if a given library was or not linked using CocoaPods.
Edit
As clarification: I have access to the library source code, but I have no control over the final project. The library is distributed as a binary (static library), and we would like to know if the users installed it using CocoaPods or not.
I guess this mainly depends on whether you have control of the library.
If you don't, no not really. Those PodsDummy- classes exist at a target level, so they just indicate that CocoaPods has been used. However, it won't tell you if a specific library used CocoaPods.
For source code libraries, CocoaPods can add additional build flags which can expose additional APIs or strings which you can lookup in the same way. There is already a "COCOAPODS" build flag added, so it could use that.
For a binary library, it's possible but a bit tricky. In theory: you could add a symbol to the lib/framework that can be discovered in the runtime, then in the Podspec's prepare_command that could be removed. I don't know what command would do that, but maybe ar, strip or lipo.
One of possible solution is to place all Pods libraries into separate bundle. You project may have several bundles. As result you can ask in runtime Bundle(for: type(of: self)) in Swift or
[NSBundle bundleForClass:[self class]] in ObjC
It seems to be impossible to automatically detect this, so we solved it doing this: We added a custom resource_bundle in the Podfile, and then we just check if it's there or not on runtime, if it's there we are sure that the library was installed using CocoaPods.
During my work with development of iOS applications, i noticed that almost every application has some parts that are repeated. For example every application has user management logic, Login, Sign Up, Forgot password.
And every time, i find my self trying to manually import already developed logic (View controllers, nibs, storyboards).
My question is how can i implement these common features in separate component, so i can simply reuse them every time i start new project. Also notice that there should be possibility for small customisations, for example all apps have login screen, but UI design varies for every app.
Long story short, what i need is:
How to encapsulate commonly repeated features in separate component.
How to inject the component in the newly started project.
How to make customizations at the component, without changing the component core.
I guess that here should be made some combination of Framework (Or static library) and cocoa pods, but i wanted to hear if somebody have already developed some concept about this.
Yes, exactly as you supposed, the way I opted for to reuse components is through a static library or sometimes a framework of reusable components, implementing common logic or well structured classes to inherit from in the new projects, which I make available to the new projects as a CocoaPods development pod sitting on my development machine or in a shared git repository. This way should answer your questions 1 and 2. For your question 3, you can either opt to perform customisations to the core dismissing pod updates, or to adapt the core methods to a possible override in the destination project. Hope it helps.
How to encapsulate commonly repeated features in separate component.
Whatever you choose you are going to have to factor out the code your separate component requires from your code base. This is the first step in the entire process - so think long and hard about if it makes sense to turn it into a separate component.
So now you have some code you would like to reuse...
There are a number of ways of doing this, such as Xcode's workspaces, stand alone source files, static libraries and frameworks. Cocoa pods is a package manager and will help you maintain your framework - not write it :(
Xcode's workspaces
A workspace is an Xcode document that groups projects and other
documents so you can work on them together. A workspace can contain
any number of Xcode projects, plus any other files you want to
include. In addition to organizing all the files in each Xcode
project, a workspace provides implicit and explicit relationships
among the included projects and their targets.
Static Libraries
Introduction to Using Static Libraries in iOS
Static libraries provide a convenient mechanism for sharing code among
multiple applications. On iOS, static libraries are the only supported
library type. This document explains how to extract code from your
application into a new static library and how to use that static
library in multiple applications.
Frameworks
In OS X, shared resources are packaged using standard frameworks and
umbrella frameworks. Both types of framework feature the same basic
structure and can contain resources such as a shared library, nib
files, image files, strings files, information property lists,
documentation, header files, and so on. Umbrella frameworks add minor
refinements to the standard framework structure, such as the ability
to encompass other frameworks.
Frameworks are packaged in a bundle structure. The framework bundle
directory ends with the .framework extension, and unlike most other
bundle types, a framework bundle is presented to the user as a
directory and not as a file. This openness makes it easy for
developers to browse any header files and documentation included with
the framework.
Source Files
These are the classes you have factored out of your code base. You could just include them in each project you use them - for instance a separate repo, that contains all of your shared/common code that you add to your Xcode project's workspace. Very simple, not the best to maintain.
How to inject the component in the newly started project.
Depending on how you choose to implement your common code will effect this step. For source files you just need add them to the project and set the target. For frameworks or static libraries you will have to embed them in your project
For workspaces you will add the projects containing the shared code to the main projects workspace.
How to make customizations at the component, without changing the component core.
Again you may find yourself refactoring code so that you expose the UI controls or logic functions that you want to be able to customize. As a general rule the more you expose the more complex the code gets.
I'm build a Framework for iOS and my framework has AFNetworking as dependency.
So what is the best practice to include AFNetworing? Reading other posts (specially this question here) i came up with three options:
Copy all the .h.m files from AFNetworing in to my project and compile my framework with it. But according to this, it will possible cause a situation where some third part developer using my Framework are already using AFNetworking and will get a compile-time error that some class is declared twice.
Use CocoaPods and link AFNetworking to my Framework. This is causing this error: -lPods is not an object file (not allowed in a library).
Build something like the Aeris SDK where the third part developer using my Framework will be responsibly to add AFNetworking to their project.
I think that option 3 is the best but i don't know how to do that. How can i dev my framework calling AFNetworking classes/methods but do not include on the final framework product?
Thanks!
That's a very bad practice to use third party library in you library.
Ideally you should avoid doing that.
But if you really need it, you can define and add prefixes to class names.
Refer this article Avoiding dependency collisions in iOS static library managed by CocoaPods
Ok. I decided to go with the option 3.
A just added all the header files from any third-party lib to the my framework project (you can also add the .m files but not include them on the static library target).
And a i also documented everything, so developers using my framework will know what are the dependencies and how to include/install them on their own projects (including third-party lib versions, CocoaPods support, etc).
I decided not to go with option 1 because that will cause some situations where a project will have two copies of the same lib compiled on the final app. Even if a change the namespace for the libs on my framework code (to prevent "duplicated symbols" errors) that will still cause some other possible problems like an APP with larger size, possible bugs related to two or more instances of the same lib running together, etc...
I have created a static library in iOS where I have certain common functionality for my projects. I used the instructions for the iOS Universal Framework and everything works great.
I have a module in the static library that requires AFNetworking. So I installed the cocoapods, and included it in my static library
However, when I include my static library into another project that also needs AFNetworking, and I run the project, I get a duplicate symbol error for all the common classes.
What is the best approach to avoid a conflict like this?
The best approach is not to include any 3rd-party libraries / frameworks inside your framework.
Here's Apple reference regarding "How to build your own framework". There are a lot of guidelines that will help to build a good framework that will not cause any errors if you'll link it to any project.
AFNetworking is a framework that is application specific, so you should not include it into your framework. You may weakly link your framework to AFNetworking, but you should avoid strongly linking it.
Here's resource regarding weak linking.
For example, if you are using 3rd-party library, you should specify that your framework require user to add dependencies in order to use it.
Here's an example how AFNetworking does it.
Also, check this framework, and it's requirements regarding dependencies in its specification.
The best way to do this (in my opinion) is to underscore the class names to keep them unique.
So let's say your library is called XYLibrary. It will have a bunch of AFNetworking files like AFNetworking.h and AFNetworking.m. Rename those to XY_AFNetworking.h and XY_AFNetworking.m. This should hopefully keep them unique from other instances of that library in another project.
You should do this for any other 3rd party libraries as well.
(This may have been asked many times before but I'm not seeing it in the suggested questions/search)
Assuming I have 3rd party code libraries like ImageMagick and AdMob which I may use in multiple iOS projects, is it "better" to link to them or to include them in the project?
I'm using XCode with git. In one project I have included them so they are all under source control. In another project they are linked and I am getting "?" (question mark) icons next to all the library files. Confusing.
My honest suggestion for using 3rd party libraries would be to use CocoaPods for as many as you can get your hands on. Which there is a good chance all would be available.
Reasons why CocoaPod inclusion is better:
Easy to add and remove from project
Automatic linkage to your project
Easy to update after including
Drawbacks to CocoaPod use:
Doesn't help you if your library isn't included
You don't want to use the newly created xcworkspace instead of xcodeproj
The reasons not to use them are pretty weak, and I will admit to be biased in favor of them. I have had to work with lots of static libraries and frameworks, most of which are created in house. CocoaPods has made sharing, maintaining, and installing libraries a piece of cake. So please consider using them in your project.
If CocoaPods aren't your thing or not an option, linking against the library or framework is probably second best. If you drag and drop into your project (while easy) makes updating later kind of a pain. Dynamic linking allows you to swap them out from the file directory without having to change anything in your Xcode project. It requires a bit more finesse to get set up, but ultimately will be better for the long haul. IMO anyway.