Migrating to Swift 4

This is a legacy document for Xcode 9 and migrating from Swift 3.

Xcode 9.0 comes with a Swift Migrator tool that helps you migrate your project to Swift 4.

Pre-Migration Preparation

Make sure that the project that you intend to migrate builds successfully in Swift 3.2 mode, and all its tests pass. Keep in mind that Swift 3.2 does have significant changes from 3.1, as well as the SDKs against which you built, so you may need to resolve errors initially.

It’s highly recommended to have your project managed under source control. This will allow you to easily review the changes that were applied via the migration assistant and to discard them and re-try the migration if needed.

Different from last year, the Migrator is built directly into the compiler and not a separate tool, so it can understand both Swift 3.2 and Swift 4 code equally and compile them together, just like the Swift 4 compiler can. That means you decide when and if you’d like to migrate on a per-target basis when it makes sense for your project. While migrating to Swift 4 is definitely encouraged, it’s not an all-or-nothing process, as Swift 3.2 and Swift 4 targets can coexist and link together.

The migration assistant does a migrator build to gather the changes, using the scheme you have selected, so the targets that will get processed are the ones that are included in the scheme. To review and modify what is included in the scheme, invoke the Edit Scheme… sheet and select the Build tab from the column on the left, and make sure all your targets and their unit tests are included.

If your project depends on other open-source projects that are provided by Carthage or CocoaPods, consult the Using Carthage/CocoaPods Projects section.

Swift Migration Assistant

When you open your project with Xcode 9 for the first time, you will see a migration opportunity item in the Issue Navigator: click it to activate a sheet asking you if you’d like to migrate. You can be reminded later or invoke the Migrator manually from the menu Edit -> Convert -> To Current Swift Syntax…

You will be presented with a list of targets to migrate. Targets that do not contain any Swift code will not be selected.

There is only one migration workflow this year, although there is a choice between two kinds of @objc Inference:

  • Minimize Inference: Add an @objc attribute to your code only where it is needed based on static inference. After using this option you need to follow the manual steps detailed in Completing a Swift 4 minimize inference migration to complete the conversion.
  • Match Swift 3 Behavior: Add an @objc attribute to your code anywhere it would be implicitly inferred by the compiler. This option does not change the size of your binary as it adds explicit @objc attributes everywhere they were implicitly added by Swift 3.

For more information and implications of these two choices, see the Xcode Help article Migrate to Swift 4 @objc inference.

Clicking Next will bring up the Generate Preview sheet and the assistant will initiate a migration build to get source changes. When this is done, you will be presented with all the changes that will be applied once you click on ‘Save’. This will also change the Swift Language Version build setting for the migrated targets to Swift 4.

There may have been issues with processing the targets that will negatively impact the migration process. Switch to the Report Navigator and select the Convert entry that was added; this is the conversion build log. Check the log for errors that may have showed up.

If you see errors about not being able to code-sign the target, try disabling code-signing from the build settings of the target. If you see other errors, please file a bug report and include the details. You are strongly encouraged to attach a project that illustrates the faulty migration if possible.

Swift 4 Migration Changes Overview

The vast majority of changes that the Migrator suggests comes from data generated by a comparison of the previous SDK and the current SDK, which may drive renaming of identifiers and types, for example; and from normal compiler fix-its. There are a few special provisions where the Migrator can safely perform straightforward mechanical changes.

SDK Changes

The two most prevalent SDK changes are moving global constants into static type properties and transforming string constants into Swift enumeration cases. These are handled automatically by the Migrator. You’ll also see various type signature changes.

Notable Special Cases

New String

String has new APIs in Swift 4, some of which now return Substring or String. To ease this transition, the Migrator will insert explicit initializer conversions when an API now expects a different type.

For more information about the new String and Substring APIs, see the Swift Evolution Document for SE-0163.

SE-0110: Distinguish between single-tuple and multiple-argument function types

f: (Void) -> ()

When using f: (Void) -> () for the type of a function argument, it is generally meant to be f: () -> (), so the Migrator will suggest you use this type instead. Otherwise, with the new rules in SE-0110 for Swift 4, you would need to call the function f as f(()).

Adding tuple destructuring

For code such as:

func foo(_: ((Int, Int) -> ()) {}
foo { (x, y) in print(x + y) }

The Migrator must add explicit tuple destructuring to continue building in Swift 4, such as:

func foo(_: ((Int, Int) -> ()) {}
foo { let (x, y) = $0; print(x + y) }

For more information about this language change, see the Swift Evolution Document for SE-0110.

Default parameter values must be public

The compiler is now more strict about the accessibility of referenced, non-literal values used as default arguments for your public functions; they must be also be public. Among other things, this exposes an opportunity for optimizing access to the value at the call site. Because this may involve API design, the Migrator does not suggest fixes, although there are some possibilities for you to consider as an API author:

  • Make the referenced default values public.
  • Provide public functions which return a sensible default value.

In both cases, consider the impact of exposing new API, making sure to document your public symbols.

After Migration

While the migrator will take care of many mechanical changes for you, it is likely that you will need to make more manual changes to be able to build the project after applying the migrator changes.

You may see compiler errors that have associated fixits; while the migrator is designed to incorporate fixits that the Swift 4 compiler provides, some fixits may not be applied if they are not applicable 100% of the time.

Even if it compiles fine, the code that the migrator provided may not be ideal. Use your best judgement and check that the changes are appropriate for your project.

Known Migration Issues

For information on the latest known issues for migration in Xcode 9, see the Swift Migration section of the Release Notes for Xcode 9 on the Developer Downloads page.

Using Carthage/CocoaPods Projects

Here are some important points to consider when migrating a project with external dependencies using package managers like Carthage, CocoaPods, or the Swift Package Manager.

  • It is recommended to use source dependencies rather than binary Swift modules, because Swift 3.1 modules will not be compatible with Swift 3.2/4 modules, unless you can get distributions that were built in Swift 3.2 or Swift 4 mode.
  • Make sure your source dependencies build successfully in Swift 3.2 mode as well as your own targets.
  • If you have setup framework search paths for finding the binary Swift modules inside Carthage’s build folder, either remove the search paths or clean the build folder, so that you are sure that you are only using the Swift modules that are built from your Xcode workspace.
  • It is not necessary to migrate your source dependencies as long as they can build in Swift 3.2 mode.

Miscellaneous

  • If you have multiple schemes in your project that cover different targets, you will only get notified that you need to migrate one of them. You will need to manually select the new scheme, then run Edit -> Convert -> To Current Swift Syntax to migrate the remaining schemes. Or you can create a scheme that includes all the targets from your project, and have it selected before running the migration assistant.