スイスイSwift!第2回比較してわかるSwiftの後方互換
skill

スイスイSwift!第2回
比較してわかるSwiftの後方互換

2014.11.18

ent77_img01.jpg

第二回は、iOSアプリ開発言語をSwiftにスイッチすることで開発がどう変わるのか?という点についてソースコードをベースに見ていこう。 果たしてSwiftに切り替えることで開発効率は上がるのだろうか?
繁田 卓二

本題に入る前にここ1ヶ月のSwiftの動向を振り返っておこう。2014年10月17日AppleはMac OSのメジャーバージョンである10.10(OS X Yosemite)をリリースした。Swift自体のバージョンも1.1としていくつかの改善が行われXcode6.1に同梱されている。1.1での最も大きな変更は、これまでiOSアプリの作成にしか使用できなかったSwiftがついにMacアプリの開発にも選択できるようになった事だろう。Mac OSアプリもSwiftでさらに開発者が増え、質の良いアプリケーションが世に出てくることを期待したい。
Xcode 6.1 Release
Notes https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html

■ Swiftプロジェクトの作成

それでは早速Swift言語でアプリを作成してみよう。と言っても、今回はXcodeに予め用意されているプロジェクトテンプレートを使用するので実際にコードを書くことは無い。Objective-CとSwiftで同じプロジェクトテンプレートから生成されたプロジェクトファイルついて比較をおこなうだけなので手元に環境が無くても気軽に読んでいただきたい。

結論から言うと後方互換を重視した仕様にはお見事!と言いたい。特にXcodeの操作手順やInterface Builderの連携などに関しても何も変わることは無く、言語的にも、クラス構成やデリゲート、KVC/KVOなどおなじみの仕組みも踏襲されている。Objective-C特有の角括弧[]やアットマーク@の記号は見当たらず、Javaや軽量言語開発者であれば馴染みある文法に近い。むしろこれまでの文法がどうかしていたのでは無いかとさえ思わせられるだろう。

プロジェクトの作成に関しては従来通り、[File]→[New]→[Project] (もしくはCommand+N)でプロジェクトウィザードが開く。今回は「Master-Detail Application」のプロジェクトテンプレートを使用しアプリを作成する。テーブルビューで一覧を表示し、セルのタップで詳細画面へ遷移するだけの単純なアプリだ。

ent77_img02.jpg
図1) Master-Detail Applicationのストーリーボード構成

では、プロジェクトウィザードを開きテンプレート一覧からMaster-Detail Applicationを選択したらNext をクリックしよう。

ent77_img03.jpg
図2) プロジェクトウィザード1

次の画面ではProduct Nameの欄には適当に SwiftSampleのようにプロジェクト名を入力。そしてLanguage欄でSwiftを選択。たったこれだけだ。

ent77_img04.jpg

ちなみにここでObjective-Cを選択したとしても後から Swiftのクラスを追加することは可能である。反対にSwiftを選択して生成したプロジェクトであってもObjective-Cのクラスは追加できる。あくまでもプロジェクトに追加される初期ファイルがどちらの言語であるかというだけである。

後は保存先を决めてプロジェクトウィザードを完了させよう。これでプロジェクトファイル一式が生成されプロジェクトビューがオープンする。開発言語にSwiftを選択する以外はこれまでの操作と何ら変わることは無いだろう。

■ プロジェクト構成の比較

Xcodeのプロジェクトが作成できたら左側のプロジェクトナビゲーターに注目してみよう。ここにはプロジェクトに含まれるファイルの一覧が表示される。今回作成したプロジェクトでは拡張子.swiftのファイルがいくつか存在すること以外はほぼObjective-Cでの構成と同じである。ここで同じテンプレートを使用した Objective-C プロジェクトのナビゲーターを比較するとこうなる。

ent77_img05.jpg

左がObjective-Cプロジェクト。右がSwiftプロジェクトのナビゲーターだ。左側の赤線で表している2種類のファイルがSwift側では消えている事がわかる。一つはクラス宣言を記述する拡張子.hのヘッダファイル。もう一つはmain関数を記述するmain.mファイル。いずれもC言語由来の要素であり、「Without C」を目指すSwiftではこれらは排除された。

Swiftでは拡張子.swiftファイルに直接クラス実装を記述できる。ヘッダファイルが不要という点は、単にファイル数が減るというメリット以外にも開発の効率アップが期待できる。ヘッダファイルで宣言して、実装ファイルに処理を記述して、またヘッダファイルに移動して、、、というファイルの往復が無くなるからだ。 main.mに関しては、main.swift というファイルを追加することでmain関数相当の処理は記述できる。デフォルトではこのファイルは省略され、@UIApplicationMain属性を宣言したクラスが暗黙的に使用される。

尚、これまでプロジェクトの初期構成で必ず存在していた.pchのプリコンパイルヘッダファイルも見当たらないが、C言語由来だからという理由ではなく、Objective-Cを選択したプロジェクトでもXcode6から不要となったようだ。なぜ不要になったのかという情報ソースは見つからなかったが、必要な場合は「Build Settings」の「Precompile Prefix Header」をYESに変更し「Prefix Header」に自前で用意した拡張子.pchのファイル名を指定すれば有効になる。

■ ソースコードの比較

次にソースコードの比較をしてみよう。まずはiOSアプリで必須となるUIApplicationのデリゲート「AppDelegate」クラスを比較してみる。下記の比較を見てみよう。

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

 return YES;

}

- (void)applicationWillResignActive:(UIApplication *)application {

}

- (void)applicationDidEnterBackground:(UIApplication *)application {

}

- (void)applicationWillEnterForeground:(UIApplication *)application {

}

- (void)applicationDidBecomeActive:(UIApplication *)application {

}

- (void)applicationWillTerminate:(UIApplication *)application {

}

@end

Objective-CでのAppDelegateクラス

class AppDelegate: UIResponder, UIApplicationDelegate {

 var window: UIWindow?

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions:[NSObject: AnyObject]?) -> Bool {

  return true

 }

 func applicationWillResignActive(application: UIApplication) {

 }

 func applicationDidEnterBackground(application: UIApplication) {

 }

 func applicationWillEnterForeground(application: UIApplication) {

 }

 func applicationDidBecomeActive(application: UIApplication) {

 }

 func applicationWillTerminate(application: UIApplication) {

 }

}

SwiftでのAppDelegateクラス

Swiftの文法を知らなくてもObjective-C の開発に慣れていれば、ほぼ何が書いてあるか理解できるだろう。なぜならクラス名、変数名、メソッド名が全く同じであるからだ。違うのはクラスやメソッドの宣言子や引数の受け方くらいである。

■ クラス宣言部の比較

ではクラスの宣言部分をさらに細かく比較してみよう。まずはObjective-Cで記述した場合のAppDelegateの宣言部分だが、ヘッダファイルAppDelegate.hにて下記の@interface宣言子を使用してクラスを宣言している。コロンの右側には継承する親クラス。山括弧<>部分ではプロトコルの実装宣言(Javaで言うところのInterface)だ。

@interface AppDelegate : UIResponder <UIApplicationDelegate>

~プロパティやメソッドの宣言~

@end

次にAppDelegate.mにて、クラスの実体を宣言する。

@implementation AppDelegate

~プロパティやメソッドの実装~

@end

これがSwiftではヘッダファイルが不要となるためクラスの定義のみとなる。

class AppDelegate: UIResponder, UIApplicationDelegate

{

  ~プロパティやメソッドの実装~

}

class宣言子の後にクラス名を置き、コロンに続いて親クラス名、その後のカンマに続いては実装するプロトコル名を記述する。宣言子と記号が変わったくらいで大きな変化は見られない。クラスの記述に関してはそういうものだというくらいの認識で問題ないだろう。

■ プロパティ変数宣言部の比較

次はプロパティ変数の宣言だ。せっかくなので別のファイルも覗いてみよう。Master-Detail遷移の中でテーブルビューでの一覧画面表示を実装したMasterViewControllerクラスを比較してみる。先程のAppDelegateに比べ、実装コードが多い分複雑な印象を受けるだろう。ただ、Objective-Cと行レベルで比較しても処理内容は全く同じなので特に臆することはない。

MasterViewControllerではテーブルビューで表示するオブジェクトの配列をプロパティ変数にセットしている。最初にObjective-Cでのプロパティ宣言を見てみよう。

@interface MasterViewController ()

@property NSMutableArray *objects;

@end

宣言子@propertyで型と変数名を宣言している。これがSwiftでは下記のようになる。

class MasterViewController: UITableViewController {

  var objects = NSMutableArray()

  ~メソッド定義~

}

Swiftではクラス内でvar宣言子を使用しプロパティ変数を定義する。JavaScriptでお馴染みでもあるvar宣言子はプロパティ変数だけでなく通常の変数にも使用する。また、変数宣言にはvar以外にもlet宣言子が存在し、var宣言した変数は再代入が可能。letは再代入が不可となる。変数の一貫性を担保する仕様の1つではあるが、let宣言した変数は定数のように値が普遍というわけではないことに注意しよう。Mutable変数などであれば当然要素は追加や削除される。あくまでも"再代入"不可というだけだ。

また上記のobjectsのようにプロパティ変数の初期値が定義できるようになった。これまでプロパティ変数の初期化のタイミングは意外と抜けがあったりとバグの温床になりがちであるため、宣言時から初期値が保証されるというのはとても安心である。

■ メソッド実装の比較

続いてはメソッドの実装部分。先ほどのMasterViewControllerクラスの中のtableView:numberOfRowsInSection:メソッドを比較しよう。
テーブルビューでの実装において行数を確定するために必須となるメソッドだ。

まずはObjective-Cでの記述。下記のように tableView:numberOfRowsInSection: というのがメソッド名を宣言している。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

 return self.objects.count;

}

これがSwiftだとこうなる。

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

 return objects.count

}

Swiftからは親クラスでのメソッドをオーバーライドするときは明示的にoverride修飾子を付けなければならなくなった。その後に続くfunc宣言子に続いてメソッド名を記述する。ここではメソッド名はtableView()とシンプルになり、これまでメソッド名の一部であった引数のラベルは外部引数名(external parameter name)として扱われる(この例では numberOfRowsInSection が外部引数名となる)。同様にtableView:indentationLevelForRowAtIndexPath:メソッドを宣言したい場合は同じtableView()メソッドとして宣言し、第二引数の外部引数名をindentationLevelForRowAtIndexPathとすれば良い。

override func tableView(tableView: UITableView, indentationLevelForRowAtIndexPath indexPath: Int) -> Int {

 return ""

}

この外部引数名は、メソッドの呼び出しの際にパラメータに付与するラベルのようなものであり、メソッド名の一部でもある。呼び出し時には外部引数名も含めて記述する。

self.tableView(self.tableView, numberOfRowsInSection:0);

また、外部というからには当然内部もあるが、内部引数名(Internal parameter name)は、引数を受け取った際にメソッド内で使用する変数名というだけであり、特に目新しいものではない。(この例ではtableViewとindexPathが内部引数名となる)

メソッド定義行の最後にあるアロー「->」の記述は、返り値の型宣言である。返り値が無い場合はアロー以降を省略する。

まとめるとSwiftでのメソッドの定義は次のようなフォーマットとなる。

func メソッド名(外部引数名 内部引数名: 型名, ...) -> 戻り値の型
         省略可            省略可

■ まとめ

今回はプロジェクトウィザードで作成した初期ファイルを比較することでSwiftの後方互換の高さを伺うことが出来たと思う。プロジェクトテンプレートを比較しやすい構成にすることでObjective-CからSwiftへの敷居を下げようというApple側の思惑もあるのかもしれない。しかしSwiftはObjective-C以上の機能を持っており、まだまだ多くの言語仕様が待っている。その一つ一つがObjective-Cの長い歴史の上に成り立ったものであり、開発の効率化に繋がる工夫がいくつも垣間見える。

Swiftは、敷居は低いがとても奥の深い言語でもあるので、プログラマとして習得の楽しみが多い言語と言えるだろう。せっかく習得するのだからiOSアプリだけでなく、是非Macアプリの開発にもチャレンジして頂きたい。

原稿:繁田卓二
qnote最高技術責任者。猫とビールをこよなく愛するゆるキャラ系プログラマ。
Webアプリ開発からMacアプリ開発を経て現在はiOSアプリ開発に没頭。弱点はAndroid。

この記事はどうでしたか?

おすすめの記事

キャリアを考える

BACK TO TOP ∧

FOLLOW