Building Modern Frameworks addresses versioning and the importance of getting the API right the first time. Then, it says every app has its own copy of the framework. So then, can't I change my framework carelessly, i.e., without worrying about breaking other apps that are using older versions of my framework?
If we're talking about your own app on iOS, you can do whatever you like. The "framework" is merely a module like any other module; it is included in the app and is simply part of the app's code, so if you revise it, the next update gets the revision and the new code that uses it and there's no problem.
On OS X, however, there's an ability to install a framework into the library where the app will see it. Clearly in that case the code that uses the framework must be careful about versioning. Similarly, even on iOS, if you are using your framework as a way to convey a module to other developers, you must try not to break heedlessly their existing code that uses your framework.
Related
I have an app implemented in native iOS (Swift). There is a web version of the app as well. A client wants to embed my app to its own app and suggested I use an iFrame and load the web version.
I understand this is a tricky solution as Apple might reject the app for not using native implementation.
What I want to ask is if there is a way to package my app entirely as a Framework and load it that way (app size is fairly big, with several viewControllers and functionality).
I understand that I won't have access to App-load functions like the AppDelegate.
Also what happens if my app has Library dependencies ? (such as Alamofire)
Any other things I should be concerned about ?
Thank you
There are obviously a lot of options around this as far as design/approach.
I've done this multiple times (with apps live on the app store) and really it's just like developing any Framework.
First: AppDelegate. The easy way around this is to have the app's AppDelegate subclass your Framework's AppDelegate:
#UIApplicationMain class ParentAppDelegate: FrameworkAppDelegate { }
Just make sure the App calls super on all the relevant methods.
Second: Dependencies. This is probably the most annoying part since Frameworks can't embed other frameworks. But you still have a few easy options:
Have the enclosing app embed the needed framework
Add the sources of the needed framework directly to your framework.
Use a dependency manager (e.g. cocoapods) that takes care of this for you.
Other Concerns: One trap you can easily run into is working with Bundles. Anytime you dynamically load images/strings/IB references/etc. you will need to specify you're using the Framework's bundle, as at times it can default to using the app's bundle. The easiest way to do this is with this init e.g. Bundle(for: self.self)
Also keep in mind that the settings in info.plist and entitlements your framework needs will need to be added by the parent app.
General Comments on Approach: My advice (take it or leave it ☺️) would be caution around simply adding your full application to a client's application. Aside from IP and App-Review concerns, it can result in adding a lot of complexity or a fork of your current application to support it, making future maintenance a hassle.
Instead I would recommend putting only the portions of the application your client requires into a separate framework that both you and your client use for your separate applications.
I support a suite of related iOS apps, some of which make use of extensions (WatchKit and Today Widget). All of these apps and extensions make use of a shared private framework I've built up over time for handling certain workflows around authentication and common business logic. This framework is maintained as a private pod.
Recently, I've run into a problem where I'd like to add a method to the framework that's only really useful for the iOS apps (extensions don't need it) that uses certain APIs that are unavailable to extensions (such as [UIApplication sharedApplicaion]). I'd like to get the usual benefit of shared code, where this is implemented in only one place (the shared framework) for all my various apps to leverage. However, I can't find a way to conditionally include that method for just the apps and not the extensions without getting a compile-time error.
Normal recommendations around this problem usually suggest the use of a preprocessor macro to opt-out around the problematic code if desired, but that doesn't really work for a shared framework situation. Macros are applied at compile-time, so the shared framework is either going to include that method, or not, and there doesn't seem to be a runtime solution to optionally exclude it. If it's included, the extensions can't compile. If it's not included, my apps can't make use of the feature.
I also started investigating if there was some way that Cocoapods could automatically make two versions of the framework, one to be used by the apps, and one by the extensions, but this would seem to introduce problems around duplicate symbols, and generally doesn't seem supported.
Are there any other suggestions for how to handle this, apart from just extracting out the problematic functionality into a different framework? (I really would prefer to just share one)
The first thing I tried is to create a static library but later I found out that it's not supported yet. Apple Xcode Beta 4 Release Notes:
Xcode does not support building static libraries that include Swift
code. (17181019)
I was hoping that Apple will be able to add this in the next Beta release or the GA version but I read the following on their blog:
While your app’s runtime
compatibility is ensured, the Swift language itself will continue to
evolve, and the binary interface will also change. To be safe, all
components of your app should be built with the same version of Xcode
and the Swift compiler to ensure that they work together.
This means that frameworks need to be managed carefully. For instance,
if your project uses frameworks to share code with an embedded
extension, you will want to build the frameworks, app, and extensions
together. It would be dangerous to rely upon binary frameworks that
use Swift — especially from third parties. As Swift changes, those
frameworks will be incompatible with the rest of your app. When the
binary interface stabilizes in a year or two, the Swift runtime will
become part of the host OS and this limitation will no longer exist.
The news is really alarming for me a person who writes components for other developers to use and include in their apps. Is this means that I have to distribute the source code or wait for two years?. Is there any other way to distribute the library without exposing the code (company policy)?
Update:
Is Swift code obfuscation an option at this point ?
Swift is beta now, and even for 1.0 Apple has been pretty clear they're after a restricted feature set -- better to do a small number of things well than to try to do everything.
So for now, there's no way to distribute binary static libraries. Presumably that'll change sometime after Swift 1.0. For now, you can:
Distribute source
Ship a binary framework (instead of a library) if you're okay with the ABI being fragile
Use ObjC for library code
You can always combine approaches, too: e.g., implement the critical (secret) details of your library in ObjC, and ship Swift source that wraps it in a nice Swift API.
Obfuscating code written in a language that's very much subject to change sounds like a recipe for a maintenance nightmare.
I believe the whole approach is wrong. You cannot do something that is not (yet) doable by the technology you are trying to use.
Rationale: Swift is a new language, currently in Beta, and therefore changing. As I see it, this fact means not only that you are not able to ship static libraries, but that (real) developers will not be actually use third-party static libraries. What's the actual use of a library that may not work in the next release of the compiler? The issue gets bigger if you whant to use more than one library, because they might not be compatible! Therefore, even if you would be able to ship static libraries, they wouldn't be actually useful for production environment. So, what's the point?
Suggestion: write your static libraries in Objective-C (or C or whatever "non-beta"). Developers who need third-party libraries (e.g. yours) shouldn't expect them to be written in Swift until Swift is stable. You don't use experimental materials to build real bridges, right? You use well-tested, predictable ones.
From Xcode 9 beta 4, Xcode supports static library with Swift sources.
I'm developing an iOS SDK that integrates other SDKs (Facebook SDK 3.5, for example).
To prevent collisions and allow my customers to import those SDKs as well, I want to rename all of the classes/enums in my code (for example, rename FBSession to RDFBSession, etc).
Is there an easy way to do this instead of going class-by-class and using Xcode's rename feature?
Apple provide a command-line tool called tops(1) that is designed for scripting large-scale code refactoring (renaming C functions, Objective-C methods, classes, and other tokens):
tops -verbose replace "FBSession" with "RDFBSession" Sources/*.[hm]
If you have a lot of replacements, you can put all of the replace... commands into a file that you pass with the -scriptfile option. The man page has more information on the more complex commands/options (and examples).
Xcode also offers textual Search and Replace. This will be faster than individual refactors, but it is ultimately less automated. You can make the step by step refactoring faster by first minimizing the project to the relevant dependencies/sources (if possible).
However, renaming the declarations in a library will not alter the symbol names of its associated binary. If it is distributed with a binary, then renaming will just result in linker errors or (in some cases) runtime errors.
The best idea if you need to use a 3rd party library which your clients might also use is to simply inform them they need to link the library with their app, then publish the version(s) the current release supports so they know they have some extra testing if they go too far ahead with some libraries.
I think that a better approach than simply renaming your classes would be to download Facebook's open source code, rename the classes there and compile a new static library with a set of renamed header files. Then you can be sure that no collisions occur and that you're using symbols that you named yourself.
I must warn you though - working like this may make updating the SDK a nightmare regardless of how you tackle this specific issue.
I am writing an iOS library which depends on some other open-source libraries. Apparently it is not possible to have two classes with the same name, so it is possible that the library compiles, and a project that potentially could use it compiles as well, but they do not work well together (at the linking phase).
The library is targeted at a large audience, so I can not make any assumptions on whether these developers will be importing the same libraries or not, or if they might be using a different, incompatible version of the same libraries.
I have been looking around but couldn't find any clear solution to my problem (maybe there isn't). So far I am thinking of these options:
Inform the users that X libraries are already included in the project, so they do not include them as well. This means they can not use a different version of X libraries.
As a refined version of the first one, use CocoaPods, so dependencies are resolved automatically. Still has the disadvantage that two versions of the library can not coexist.
Import and rename all classes my library depends on, prefixing them, so the names don't conflict with the original ones. This is tedious work, but more importantly, has the disadvantage that I would not be able to pull/push code from/to the original library, as the code would change too much. Still seems to me the best option from the user perspective.
Can you think of a better idea? I'm pretty new to library projects, so maybe there is something obvious I am missing.
We're still not decided whether to distribute in binary or source code form. If there is a reason to choose one or another I would also like to hear your opinion.
When I was faced with this problem I choose your third option and prefixed the dependent classes within my library. The reason you might want to consider doing this rather than relying on the user to import the others would be that you can guarantee compatibility and you don't have to worry about versions of who you depend on.
First point -
Inform the users that X libraries are already included in the project,
so they do not include them as well
so you have a static library Foolib.a, it has a 3rd party dependency Barlib.a, in order for Foolib to build, Foolib's HEADER_SEARCH_PATHS must be set to the path of Barlib's public headers. No more.
If you are distributing your source code you can use CocoaPods (this is a good way to go), or you can add Barlib's repository as a git submodule (or whatever for your choice of VCS) of your repository and hard code the HEADER_SEARCH_PATHS to that path, or you can require that your user grabs their own Barlib and manually edits HEADER_SEARCH_PATHS to the correct path (if you go the CocoaPods or submodule route the user can easily do this as well, so has more options).
Nothing from Barlib is 'in' your project.
On the other hand, if you are distributing a binary for your user to link into their app you must specify in your instructions that Foolib requires Barlib to be linked into the app. Instructions for how to get hold of Barlib would be nice.
Nothing from Barlib is 'in' your project or compiled into your library.
Second Point -
use CocoaPods, so dependencies are resolved automatically. Still has
the disadvantage that two versions of the library can not coexist
Two versions of the same library in one App is impossible, but the situation where the end user already requires Barlib 3.0, wants to use your Foolib, but Foolib requires Barlib 4.0 doesn't have to ever arise - It is up to you the developer. You can be generous and support multiple versions of Barlib (i.e. all Foolib needs to work is a Barlib1.0, Barlib2.0, Barlib3.0 OR Barlib4.0 linked into the app - similar to writing an app that supports iOS5 and iOS6) or, you can be opinionated and require a specific version, and if the user is already requiring a different version of Barlib, tough luck, they will have to change their code if they want to use your library.
Third point -
Import and rename all classes my library depends on, prefixing them,
so the names don't conflict with the original one
This is just too terrible for me to consider at the moment. Sorry.
Nothing from Barlib is ever 'in' your project or compiled into your library. You don't distribute any copy of Barlib - either linked into your binary or as source code.