Tuesday 3 June 2014

Swift and Objective-C in the Same Project

Swift and Objective-C in the Same Project

Swift’s compatibility with Objective-C lets you create a project that contains files written in either language. You can use this feature, called mix and match, to write apps that have a mixed-language codebase. Using mix and match, you can implement part of your app’s functionality using the latest Swift features and seamlessly incorporate it back into your existing Objective-C codebase.

Mix and Match Overview

Objective-C and Swift files can coexist in a single project, whether the project was originally an Objective-C or Swift project. You can simply add a file of the other language directly to an existing project. This natural workflow makes creating mixed-language app and framework targets as straightforward as creating an app or framework target written in a single language.
The process for working with mixed-language targets differs slightly depending on whether you’re writing an app or a framework. The general import model for working with both languages within the same target is depicted below and described in more detail in the following sections.
image: ../Art/DAG_2x.png

Importing Code from Within the Same App Target

If you’re writing a mixed-language app, you may need to access your Objective-C code from Swift and your Swift code from Objective-C. The process described in this section applies to non-framework targets.

Importing Objective-C into Swift

To import a set of Objective-C files in the same app target as your Swift code, you rely on an Objective-C bridging header to expose those files to Swift. Xcode offers to create this header file when you add a Swift file to an existing Objective-C app, or an Objective-C file to an existing Swift app.
image: ../Art/bridgingheader_2x.png
If you accept, Xcode creates the header file along with the file you were creating, and names it by your product module name followed by adding “-Bridging-Header.h”. For information on the product module name
You’ll need to edit this file to expose your Objective-C code to your Swift code.
To import Objective-C code into Swift from the same target
  1. In your Objective-C bridging header file, import every Objective-C header you want to expose to Swift. For example:
    Objective-C
    • #import "XYZCustomCell.h"
    • #import "XYZCustomView.h"
    • #import "XYZCustomViewController.h"
  2. Under Build Settings, make sure the Objective-C Bridging Header build setting under Swift Compiler - Code Generation has a path to the header. The path must be directly to the file itself, not the directory that it’s in.
    The path should be relative to your project, similar to the way your Info.plist path is specified in Build Settings. In most cases, you should not need to modify this setting.
Any public Objective-C headers listed in this bridging header file will be visible to Swift. The Objective-C functionality will be available in any Swift file within that target automatically, without any import statements. Use your custom Objective-C code with the same Swift syntax you use with system classes.
Swift
  • let myCell = XYZCustomCell()
  • myCell.subtitle = "A custom cell"

Importing Swift into Objective-C

When you import Swift code into Objective-C, you rely on an Xcode-generated header file to expose those files to Objective-C. This automatically-generated file is an Objective-C header that declares all of the Swift interfaces in your target. It can be thought of as an umbrella header for your Swift code. The name of this header is your product module name followed by adding “-Swift.h”. For information on the product module name
You don’t need to do anything special to create this file—you just need to import it to use its contents in your Objective-C code. Note that the Swift interfaces in the generated header include references to all of the Objective-C types used in them. If you use your own Objective-C types in your Swift code, make sure to import the Objective-C headers for those types prior to importing the Swift generated header into the Objective-C .m file you want to access the Swift code from.
To import Swift code into Objective-C from the same target
  • Import the Swift code from that target into any Objective-C .m file within that target using this syntax, and substituting the appropriate name:
    Objective-C
    • #import “ProductModuleName-Swift.h”
Any Swift files in your target will be visible in Objective-C .m files containing this import statement. For information on using Swift from Objective-C code


Import into Swift
Import into Objective-C
Swift code
No import statement
#import "ProductModuleName-Swift.h”
Objective-C code
No import statement; Objective-C bridging header required
#import "Header.h”

Importing Code from Within the Same Framework Target

If you’re writing a mixed-language framework, you may need to access your Objective-C code from Swift and your Swift code from Objective-C.

Importing Objective-C into Swift

To import a set of Objective-C files in the same framework target as your Swift code, you’ll need to import those files into the Objective-C umbrella header for the framework.
To import Objective-C code into Swift from the same framework
  1. Under Build Settings, in Packaging, make sure the Defines Module setting for that framework target is set to Yes.
  2. In your umbrella header file, import every Objective-C header you want to expose to Swift. For example:
    Objective-C
    • #import <XYZ/XYZCustomCell.h>
    • #import <XYZ/XYZCustomView.h>
    • #import <XYZ/XYZCustomViewController.h>
Swift will see every header you expose publicly in your umbrella header. The contents of the Objective-C files in that framework will be available in any Swift file within that framework target automatically, without any import statements. Use your custom Objective-C code with the same Swift syntax you use with system classes.
Swift
  • let myCell = XYZCustomCell()
  • myCell.subtitle = "A custom cell"

Importing Swift into Objective-C

To import a set of Swift files in the same framework target as your Objective-C code, you don’t need to import anything into the umbrella header for the framework. Instead, import the Xcode-generated header file for your Swift code into any Objective-C .m file you want to use that code from.
To import Swift code into Objective-C from the same framework
  1. Under Build Settings, in Packaging, make sure the Defines Module setting for that framework target is set to Yes.
  2. Import the Swift code from that framework target into any Objective-C .m file within that framework target using this syntax, and substituting the appropriate names:
    Objective-C
    • #import <ProductName/ProductModuleName-Swift.h>
Any Swift files in your framework target will be visible in Objective-C .m files containing this import statement. For information on using Swift from Objective-C code


Import into Swift
Import into Objective-C
Swift code
No import statement
#import <ProductName/ProductModuleName-Swift.h>
Objective-C code
No import statement; Objective-C umbrella header required
#import "Header.h”

Importing External Frameworks

You can import external frameworks that have a pure Objective-C codebase, a pure Swift codebase, or a mixed-language codebase. The process for importing an external framework is the same whether the framework is written in a single language or contains files from both languages. When you import an external framework, make sure the Defines Module build setting for the framework you’re importing is set to Yes.
You can import a framework into any Swift file within a different target using the following syntax:
Swift
  • import FrameworkName
You can import a framework into any Objective-C .m file within a different target using the following syntax:
Objective-C
  • @import FrameworkName;


Import into Swift
Import into Objective-C
Any language framework
import FrameworkName
@import FrameworkName;

Using Swift from Objective-C

Once you import your Swift code into Objective-C, use regular Objective-C syntax for working with Swift classes.
Objective-C
  • MySwiftClass *swiftObject = [[MySwiftClass alloc] init];
  • [swiftObject swiftMethod];
A Swift class or protocol must be marked with the @objc attribute to be accessible and usable in Objective-C. This attribute tells the compiler that this piece of Swift code can be accessed from Objective-C. If your Swift class is a descendant of an Objective-C class, the compiler automatically adds the @objc attribute for you. For more information
You’ll have access to anything within a class or protocol that’s marked with the @objc attribute as long as it’s compatible with Objective-C. This excludes Swift-only features such as those listed here:
  • Generics
  • Tuples
  • Enumerations defined in Swift
  • Structures defined in Swift
  • Top-level functions defined in Swift
  • Global variables defined in Swift
  • Typealiases defined in Swift
  • Swift-style variadics
  • Nested types
  • Curried functions
For example, a method that takes a generic type as an argument or returns a tuple will not be usable from Objective-C.
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. However, note that you cannot subclass a Swift class in Objective-C.
To reference a Swift class in an Objective-C header file
  • Forward declare the Swift class you’re using:
    Objective-C
    • // MyObjcClass.h
    • @class MySwiftClass;
    • @interface MyObjcClass : NSObject
    • - (MySwiftClass *)returnSwiftObject;
    • /* ... */
    • @end

Naming Your Product Module

The name of the Xcode-generated header for Swift code, and the name of the Objective-C bridging header that Xcode creates for you, are generated from your product module name. By default, your product module name is the same as your product name. However, if your product name has any nonalphanumeric characters, such as a period (.), they are replaced with an underscore (_) in your product module name. If the name begins with a number, the first number is replaced with an underscore.
You can also provide a custom name for the product module name, and Xcode will use this when naming the bridging and generated headers. To do this, change the Product Module Name build setting.

Troubleshooting Tips and Reminders

  • Treat your Swift and Objective-C files as the same collection of code, and watch out for naming collisions.
  • If you’re working with frameworks, make sure the Defines Module build setting under Packaging is set to Yes.
  • If you’re working with the Objective-C bridging header, make sure the Objective-C Bridging Header build setting under Swift Compiler - Code Generation has a path to the header that’s relative to your project. The path must be directly to the file itself, not just to the directory that it’s in.
  • Xcode uses your product module name—not your target name—when naming the Objective-C bridging header and the generated header for your Swift code. For information on product module naming
  • To be accessible and usable in Objective-C, a Swift class must be a descendant of an Objective-C class or it must be marked @objc.
  • When you bring Swift code into Objective-C, remember that Objective-C won’t be able to translate certain features that are specific to Swift. 
  • If you use your own Objective-C types in your Swift code, make sure to import the Objective-C headers for those types prior to importing the Swift generated header into the Objective-C .m file you want to access the Swift code from.

No comments:

Post a Comment