エンジニア

SwiftとObjective-Cを共存させる

投稿日:2014年7月22日 更新日:

今回のエンジニアブログ担当の山下です。

先月Objective-Cに代わるiOSアプリ開発言語としてSwiftが発表されました。
Objective-C好きな私には辛い現実ですが、
今後Swiftによってよりモダンで安全なiOSアプリ開発が可能になるのはありがたいことです。

またSwiftはObjective-Cとの親和性も考慮されており、
Objective-Cで書かれた過去の資産は有効活用出来ます。
今回は2つの言語間での連携についてまとめてみました。

環境構築

SwiftはXcode 6のベータ版で開発することになります。
これまでベータ版はiOS Developer Programに参加しているデベロッパに限定公開されていましたが、
今回はなんと一般公開されています。下記のURLからダウンロードし、インストールを行ってください。
https://developer.apple.com/xcode/downloads/
※無料のApple Developer Programへの登録が必要です

今回は執筆時点の最新版であるXcode 6 beta 4を使用しています

SwiftからObjective-Cのクラスを参照する

まずObjective-Cで書かれたクラスをSwiftからコールしてみます。
Xcode 6 betaを開き、新しいプロジェクトを作成してください。
テンプレートはSingle View Applicationを選択し、
LanguageはSwiftを選択します。これでSwiftのプロジェクトが作成されます。

ファイル一覧を見てみると、AppDelegate等のクラスは.swiftファイルに一本化されています。中を見てみるとCocoa TouchでおなじみのメソッドはSwiftで書き直されています。

ではまず呼び出されるObjective-Cのクラスを作成します。
メニューのFile > New > Fileを選択し、iOSのCocoa Touch Classを選択します。

クラス名や親クラスを指定する画面では、記述する言語が選択できるようになっていますので、Objective-Cを選択しましょう。

言語選択

その後ファイルの保存先を決めると、次のようなメッセージが表示されます。

bridging header作成の確認

SwiftがObjective-Cのクラスを参照するには、Objective-C bridging headerと呼ばれるファイルに
ヘッダファイルのインポート文を書くことで可能になります。
Yesを選択すると、< プロジェクト名>-Bridging-Header.hという名前でそのヘッダファイルがプロジェクトに追加されます。
ファイルを開き、今回のクラスのimport文を以下のように追加します。

< プロジェクト名>-Bridging-Header.h

#import "ObjCClass.h"

ではObjective-Cクラスを簡単に実装してみます。
インスタンスメソッドとして文字列と数値を結合して返すメソッドを実装します。

ObjCClass.h

#import <Foundation/Foundation.h>
@interface ObjCClass : NSObject
- (NSString *)joinString:(NSString *)string withNumber:(NSNumber *)number;
@end

ObjCClass.m

#import "ObjCClass.h"
@implementation ObjCClass
- (NSString *)joinString:(NSString *)string withNumber:(NSNumber *)number
{
    return [NSString stringWithFormat:@"%@, %@", string, number];
}
@end

ではこのクラスをSwiftから参照してみましょう。
ViewController.swiftを開き、以下のように実装します。
参考までにほぼ同じ意味のObjective-Cコードをコメントとして残してみました。
Swiftは型推論やイニシャライズメソッド、メソッドの呼び方の変化等々でよりシンプルな
記述が可能になっています。見比べてみて下さい。

override func viewDidLoad() {
    super.viewDidLoad()

    var object = ObjCClass()
    var out = object.joinString("String", withNumber: 123)
    println(out)

    /*  
   ObjCClass *object = [[ObjCClass alloc] init];  
   NSString *out = [object joinString:@"String" withNumber:@123];  
   NSLog(@"%@", out);  
   */
}

以上で実装は終わりです。実行してみると以下のような結果になります。

実行結果

String, 123  
Objective-CからSwiftのクラスを参照する

次は逆に、Objective-CからSwiftのメソッドをコールしてみます。
新たにCocoa Touch Classを作成して下さい。親クラスはNSObjectで、言語は今度はSwiftを選択します。
作成したら以下のように実装します。先ほどObj-Cで実装したものと機能的には同じです。

import Foundation

class SwiftClass : NSObject
{
    func joinString(string:String, number:NSNumber) -> String
    {
        return "\(string), \(number)"
    }
}

ではこれをObjective-Cからコールします。
先ほど作成したObjective-Cクラスを流用し、Swiftで書かれたクラスを呼ぶように変更してみましょう。
ファイル一覧には見えていませんが、"< プロジェクト名>-Swift.h"というファイルをインポートすることで
Swiftのクラスを参照出来るようになります。

#import "ObjCClass.h"
#import "SwiftObjC-Swift.h"

@implementation ObjCClass
- (NSString *)joinString:(NSString *)string withNumber:(NSNumber *)number
{
    SwiftClass *object = [[SwiftClass alloc] init];

    return [object joinString:string number:number];
}
@end

ではこれを実行してみましょう。先ほどと同じ結果が表示されるはずですが、
実際はSwift → Objective-C → Swiftと言語を超えたコールの連鎖が起こっています。

実行結果

String, 123  
Swiftのアクセス制御

少し余談ですが、Xcode 6 beta 4よりSwiftにアクセス制御が追加されました。
Swiftのアクセス修飾子はpublic, internal, privateの3種類があり、
それぞれ以下のような意味合いを持ちます。

public:
  同一モジュール、モジュールをimportしているモジュールの全てからアクセス可能
internal:
  同一モジュールからアクセス可能
private:
  同一ファイルからのみアクセス可能

アクセス修飾子を指定しない場合のデフォルト設定はinternalがデフォルトで指定されます。

Swiftでアクセス修飾子を指定した場合、Objective-Cからはどう見えるのか確認してみました。
先ほど作ったクラスのメソッドをprivateにし、ビルドしてみます。

    private func joinString(string:String, number:NSNumber) -> String
    {
        return "\(string), \(number)"
    }

ビルドした結果がこちら。コンパイルエラーとなりました。

No visible @interface for 'SwiftClass' declares the selector 'joinString:number:'

private指定により隠蔽されているため、Objective-C側では該当するセレクタが見つからずエラーとなります。
Objective-CからSwiftをコールする際にも、アクセス制御が有効であることが確認出来ました。


いかがでしたでしょうか。
このようにあまり手間がかからず言語間の連携ができるため、Swiftへの移行がスムーズに行えると思います。
既存プロジェクトに機能追加する際はこれを機にSwiftに挑戦してみてください。

採用情報

ワンダープラネットでは、一緒に働く仲間を幅広い職種で募集しております。

-エンジニア
-, ,

© WonderPlanet Inc.