Also compatible with Xcode 7 and Xcode 8

Source: Internet
Author: User

As an iOS developer, you're definitely excited about the new features in iOS 10 and can't wait to practice in the app. You want to do it right away so you can "get on the Boat" the first time. But IOS 10 is a few months from now, and until then, you'll need to keep it posted every few weeks. Does this sound like you're in the right situation?

Of course, you can't compile an app that needs to be published with Xcode 8来 at the moment-because it can't be verified by the App Store. So you need to split the project into two branches, stable branch and IOS 10 Development Branch ...

And inevitably, it sucks. It is still possible to develop a feature on a branch only. But what if it lasts for months to maintain this huge branch? Not only has the entire codebase changed, but the main branch has been evolving, and you're going to encounter some non-descriptive combinations of pain. I'm saying, have you ever tried to handle .xcodeproj merge conflicts with files?

The purpose of this article is to show you how to completely avoid using branching. For most applications, it is possible to use only one project file to support both iOS 9 (Xcode 7) and iOS (Xcode 8). And even if you have to use a branch, these tips can help you reduce the differences between the two branches and make it more comfortable to sync them.

You're going to use Swift 2.3.

Let me explain one thing:

We are all excited for Swift 3. It's great, but if you're reading this article, please don't use it (or temporarily). Although it's good, it's very incompatible at the code level, which is much more serious than the incompatibility of Swift 2 a year ago. And once the application has a dependency on the third-party Swift library, it will have to wait until the libraries are upgraded to Swift 3 before they can be upgraded.

The good news is that it's also unprecedented, and Xcode 8 supports two versions of swift:2.3 and 3.0.

In order to prevent you from missing certain notifications, the swift 2.3 and Swift 2.3 in Xcode 7 are basically consistent, with the exception of a handful of API tweaks (which are described in more detail later).

So! To maintain compatibility, we are developing with Swift 2.3来.

Settings for Xcode

You should have understood that so much. Now let me show you how to set up your Xcode project so that it can run on these two versions.

Swift version

First, open your project in Xcode 7. Then open the project's Settings page, check the Build settings option, and then click "+" to add a user-defined setting:

“SWIFT_VERSION” = “2.3”

This option is new for Xcode 8, so when it tells Xcode 8 to use Swift 2.3, Xcode 7 ( in fact it doesn't have swift 2.3) completely ignores this setting and continues to build with Swift 2.2来.

Framework Provisioning

The Framework provisioning works slightly differently on Xcode 8-if it is an emulator, they will continue to compile as-is and fail for a real chance build.

The way to fix this problem is to iterate through all the Framework targets in Build Settings and add the following options, like this SWIFT_VERSION :

“PROVISIONING_PROFILE_SPECIFIER” = “ABCDEFGHIJ/“

You need to replace "ABCDEFGHIJ" with your team ID (you can find it in the Apple Developer Portal) and then keep the last slash.

This is actually telling Xcode 8 "Hey, I'm from this team, you pay attention to the codesign, okay?" , and Xcode 7 will still ignore this setting, so it's all right.

Interface Builder

Traverse all .xib and .storyboard files, open the right sidebar, select the first option (file Inspector), and find the "Opens in" setting.

In most cases it displays "Default (7.0)" and modifies it to "Xcode 7.0". This ensures that even if you are creating a new file in Xcode 8, it can only do some changes that are backwards compatible with Xcode 7.

Again, be sure to note the changes you made to XIB in Xcode 8. Because it adds some data about the Xcode version (not sure whether the data will be removed after the app is uploaded to the app Store), and sometimes it tries to roll the file back to a format that only supports Xcode 8 (this is a bug). So we try to avoid creating interface files in Xcode 8, and if that's not possible, then each time you commit the code, carefully review the code and then just commit the lines you need.

SDK version

Make sure that the "Base SDK" settings for all projects and build targets are set to "Latest IOS". (In most cases, this is the default setting, but you'll want to confirm it again.) In this way, Xcode 7 is compiled for iOS 9来, and the same project will get the new features of iOS 10 in Xcode 8.

CocoaPods settings

If you use CocoaPods, you also need to update the settings of the Pods project to make sure that their Swift and provisioning settings are correct.

You can also Podfile replace the manual setting by adding the following Post-Install code to the file:

post_install do  |  installer| Installer.pods_project.build_configurations. each     do  |config| # Configure Pod targets for Xcode 8 compatibility  Config.build_settings[ ' swift_version ' ] =  ' 2.3 '  config.build_settings[ ' provisioning_profile_specifier ' ] =  ' abcdefghij/'  Config.build_settings[ ' always_embed_swift_standard_libraries ' ] =  ' NO '  end   end    

Again, remember to ABCDEFGHIJ replace the ID with your team. Then run pod install to regenerate the Pods project.

(If you find that this Pod is not compatible with Swift 2.3, then you need to pull a separate branch for Xcode 8, which is a solution provided by Igor Palaguta)

Open in Xcode 8

OK, now it's time to open the project in Xcode 8. The first time you open it, you'll be bombarded by a lot of requests.

Xcode will remind you to update to the latest version of Swift. Ignore.

Xcode also recommends that the update item be set to "recommended settings" and also ignored.

Remember, we have set up the project so that it can be compiled and passed in two versions. So what we have to do now is to make as few changes as possible to ensure compatibility. More importantly, because the files we publish to the APP Store are the same, we don't want the .xcodeproj files to contain any Xcode 8 related data.

Handling Differences in Swift 2.3

As I said before, Swift 2.3 and Swift 2.2 are the same languages . However, frameworks of the IOS SDK has updated some Swift annotations. I'm not talking about big changes (that's just the thing with Swift 3.0)--but some of the naming, type, and API options for Swift 2.3 are slightly different.

Conditional compilation

Given that you might overlook this, Swift 2.2 introduces a compilation preprocessing macro. The usage is simple:

#if swift(>=2.3)// this compiles on Xcode 8 / Swift 2.3 / iOS 10#else// this compiles on Xcode 7 / Swift 2.2 / iOS 9#endif

That's great! A file that does not require branching and is compatible with Xcode's two versions

There are two things to keep in mind:

    • #if swift(<2.3)This kind of writing does not exist, only >=. If you want to express the opposite meaning, you can write #if !swift(>=2.3) . (You can also use and if you #else want #elseif ).
    • Unlike the preprocessing of C, #if and #else must be a valid Swift code, for example, you can't just change the method signature without changing the method body. (There will be a corresponding solution for this later)
Changes in the availability of options

Many signatures in Swift 2.3 remove unnecessary options, while others (such as many NSURL attributes) are also optional.

Of course you can also use conditional compilation to deal with this problem, such as:

#if swift(>=2.3)let""#elselet specifier = url.resourceSpecifier#endif

However, the following methods may be of little help:

func optionalize<T>(x: T?) -> T? {    return x}

I know it's a little hard to understand. It may be a lot easier after you've seen the results:

let""// works on both versions!

This gives you the encapsulation advantage of an optional value that avoids writing nasty conditional compilation Code at the time of the call. optionalize()the way to do this is to convert any incoming value into an optional value, and it returns the parameter directly unless it is already an optional value. This way, url.resourceSpecifier the "optionalized" version is always the same, either (Xcode 8) or not (Xcode 7) selectable values.

(in more depth: in Swift, you Foo can be understood as a Foo? subclass, because you can Foo encapsulate any type of value as an optional value without losing information.) Once the compiler knows this, it allows a non-optional value to be passed into an optional value parameter-the Foo package Foo? . )

Use aliases to save changes to the method signature

In Swift 2.3, some methods, especially in the MacOS SDK, modified the parameter type.

For example, the previous NSWindow construction method is this:

init(contentRectNSRect, styleMask: Int, backing: NSBackingStoreType, defer: Bool)

Now it turns out that:

init(contentRectNSRect, styleMask: NSWindowStyleMask, backing: NSBackingStoreType, defer: Bool)

Note styleMask the type of the watch. Previously it was an Int loose type (an option entered as a global constant), but in Xcode 8 it was entered with a more reasonable OptionSetType type.

Unfortunately, you cannot conditionally compile the same method body, and the method signature differs from the two versions. Don't worry, you can solve this problem by conditionally compiling the type alias!

#if !swift(>=2.3)typealias NSWindowStyleMask = Int#endif

This allows you to use the method signature in the same way as Swift 2.3 NSWindowStyleMask . For Swift 2.2, this type does not exist, NSWindowStyleMask just Int an alias, and the type Checker can still work perfectly.

Informal vs formal agreements

Swift 2.3 has changed some of the previous informal agreements to formal agreements.

For example, to implement a CALayer proxy, you only need to inherit NSObject it, and you do not need to declare it to conform to the CALayerDelegate protocol. In fact, this protocol does not exist in Xcode 7 at all, just now.

Similarly, it is not feasible to do conditional compilation of the class declaration code directly. But you can solve this problem by declaring the virtual protocol in Swift 2.2, as follows:

#if !swift(>=2.3)private protocol CALayerDelegate {}#endifclass MyView: NSView, CALayerDelegate { . . . }

(Joe Groff mentioned that he could give CALayerDelegate an Any alias-the same result, but with little overhead.) )

Features for building IOS 10

At this point, your project can be compiled on both Xcode 7 and Xcode 8, and no branching is required, which is fantastic!

Now is the time to build IOS 10 features, because there are a lot of tips and tricks, so this is going to be very easy. However, there are some things to be aware of:

    1. The only use @available(iOS 10, *) and #available(iOS 10, *) is not enough. First, don't compile any IOS 10 code in your published app, because it's more secure. More importantly, because the compiler needs to check the code to ensure that the API is used securely, you need to be aware that the API being called exists. If you use a method or type that does not exist in the SDK for IOS 9, your code cannot be compiled in Xcode 7.
    2. You need to encapsulate all iOS 10-specific code #if swift(>=2.3) (currently you can think Swift 2.3 and iOS 10 are equal).
    3. Most of the time, you'll need conditional compilation at the same time (so you don't compile those unusable code in Xcode 7) and @available/#available (for the security check through Xcode 8).
    4. If you need to deal with features unique to IOS 10, the simplest way is to pull the relevant code out of a separate file-so that you can include the entire contents of the file in a single #if swift… judgment. (This file may still be processed by the compiler in Xcode 7, but the contents will be ignored.) )
Application Extensions

The problem is that you might want to add some new extensions to your app on IOS 10, rather than just adding more code to the app itself.

This is tricky. We can conditionally compile our code, but there's no such thing as a "conditional target."

The good news is that Xcode 7 doesn't need to really compile these goals, so it doesn't complain to you. (Of course, it warns you that there is a target in the project that will be published to a higher version of IOS than the base SDK, but that's not a big problem.) )

So the approach is to keep the build target and its code everywhere, but selectively remove them from the "target Dependencies" and "Embed app Extensions" options that apply to the build target.

How do you do it? The best way I can find it is to set the app extension in build settings to not be available, which is compatible with Xcode 7 by default. These extensions are then temporarily added only when you use Xcode 8, and you never commit these changes.

If you do it manually every time, it sounds too capricious (not to mention incompatibility with CI and automation builds), don't worry, I wrote you a script!

Installation:

sudo gem install configure_extensions

Before submitting any changes to the Xcode project, remove the IOS 10 dedicated app extension from your app's build target:

remove MyApp.xcodeproj MyAppTarget NotificationsUI Intents

Then, when used in Xcode 8, add them back:

add MyApp.xcodeproj MyAppTarget NotificationsUI Intents

You can put this in your folder and you can add it to the pre-processing of the script/ Xcode build, or you can add it to your Git pre-commit hooks, or integrate it into your CI and automated build systems. (For more information, refer to GitHub)

The last thing to note about IOS 10 app extensions: Xcode's templates for these extensions are based on Swift 3 instead of the swift 2.3 code. It is important to note that the "Use Legacy Swift Language Version" build option for the extended application is set to "Yes" and then the code is rewritten with Swift 2.3.

By the September

by September, IOS 10 came out, and this time we need to get rid of the support for Xcode 7 and clean up the project!

I have a confirmation list for you (remember to bookmark it so that you can refer to it later):

    • Remove all Swift 2.2 code and unnecessary #if swift(>=2.3) checks
    • Remove all transition processing, such as the optionalize() use of a pair, a temporary defined alias, or a virtual protocol
    • Remove the configure_extensions script and submit the project settings that add support for the new app extension to the code base
    • If you use CocoaPods, update it, and then remove it before we add it to the Podfile post_install Hook (Basic will not be available after September)
    • Update the project settings recommended for Xcode (select the item in the sidebar, then select: Editor→validate Settings ...) in the menu. )
    • Consider upgrading the provisioning setting, using the newPROVISIONING_PROFILE_SPECIFIER
    • .xibRoll All and .storyboard the settings back to "Opens in:latest Xcode (8.0)"
    • Ensure that all dependent swift libraries are updated to Swift 3. If not, consider making a contribution to the Swift 3 window.
    • Once the above steps are done, you can update the app to Swift 3! Find edit→convert→to current Swift Syntax ..., select all the build goals (remember, you need to convert all at once), review the diff, Test, and submit!
    • If you haven't done so yet, consider removing support for IOS 8-so you can get rid of more @available checks and other conditional statements.

Good luck!

Also compatible with Xcode 7 and Xcode 8

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.