Objective-CからSwiftへ移行する過渡期
数年間開発され続けているアプリにもそろそろSwiftが入ってきているのではないでしょうか。当然、Objective-Cで作ってきた資産を生かしながら開発することになると思います。
この時、Optionalの扱いに苦労するのではないかと思います。Objective-Cではレシーバがnil
だった場合にメッセージを送ってもクラッシュするようなことはありませんが、OptionalのあるSwiftからObjective-Cのコードを呼び出す場合はちょっと困ってしまいます。
SwiftとObjective-Cの互換性を強化するために、nullable
, nonnull
がObjective-Cに追加されています。
Swiftのコードを書いていく際に、これらの型修飾子を使ってObjective-C側も改善をしていくことでSwift導入がやりやすくなると思います。
nullable
nullable
はOptionalでありnil
を許容することを明示するための型修飾子です。
例えば、NSData
のinitWithContentsOfURL:
イニシャライザは以下のように定義されており、戻り値がnil
になり得ることを明示しています。
- (nullable instancetype)initWithContentsOfURL:(NSURL *)url;
これをSwiftで見てみると、以下のようにfailabel initializer、失敗する可能性のあるイニシャライザとなっていることが分かります。
public init?(contentsOfURL url: NSURL)
このようにインスタンス生成や引数、戻り値がnil
になり得る場合は、Objective-C側でnullable
を指定しておくことで、SwiftでOptionalとして扱うことが可能になります。
Objective-Cでnullable
を指定しない場合はImplicitly Unwrapped Optionalになります。以下のようなObjective-Cのメソッドで考えてみます。
- (UIImage*)createImage;
nullable
をつけずにSwiftから使おうとすると変換時にImplicitly Unwrapped Optionalになってしまいます。
func createImage() -> UIImage!
このメソッドの戻り値を使用しようとした際にnil
だった場合クラッシュしてしまうので、SwiftからこのObjective-Cのコードを使用する際に不安が付きまといます。もし、このような処理を見かけたときはnullable
を指定し、Swift側でOptionalとして利用できるようにするとSwiftからも安心して利用することができます。
- (nullable UIImage*)createImage;
以上のObjective-Cのコードを修正するとSwiftでは、
func createImage() -> UIImage?
と変換されます。
nonnull
nonnull
はOptionalではないことを明示するための型修飾子で、nonnull
を関数やメソッドの引数、戻り値に指定した場合はOptionalな変数を指定することはできなくなります。
NS_ASSUME_NONNULL_BEGIN
, NS_ASSUME_NONNULL_END
しかしUIKitのソースコードを見てみるとnonnull
が見当たりません。
例えばUIVisualEffectView
は以下のように定義されていますが、nonnull
はどこにも書かれていません。nullable
はしっかりと書かれています。
NS_CLASS_AVAILABLE_IOS(8_0) @interface UIVisualEffectView : UIView <NSSecureCoding>
@property (nonatomic, strong, readonly) UIView *contentView; // Do not add subviews directly to UIVisualEffectView, use this view instead.
@property (nonatomic, copy, nullable) UIVisualEffect *effect;
- (instancetype)initWithEffect:(nullable UIVisualEffect *)effect NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@end
しかし、Swiftの変換はしっかりと非Optionalになっています。
@available(iOS 8.0, *)
public class UIVisualEffectView : UIView, NSSecureCoding {
public var contentView: UIView { get } // Do not add subviews directly to UIVisualEffectView, use this view instead.
@NSCopying public var effect: UIVisualEffect?
public init(effect: UIVisualEffect?)
public init?(coder aDecoder: NSCoder)
}
つまりnonnull
の指定はしっかりと行われているということです。どうしてnonnull
を指定されていないにもかかわらず変換ができているのでしょうか?
調べてみると、NS_ASSUME_NONNULL_BEGIN
とNS_ASSUME_NONNULL_END
マクロが使われていました。
前述のUIVisualEffectView.hにもしっかりとこのマクロが書かれていました。
//
// UIVisualEffectView.h
// UIKit
//
// Copyright (c) 2014-2015 Apple Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
// ...中略
NS_CLASS_AVAILABLE_IOS(8_0) @interface UIVisualEffectView : UIView <NSSecureCoding>
@property (nonatomic, strong, readonly) UIView *contentView; // Do not add subviews directly to UIVisualEffectView, use this view instead.
@property (nonatomic, copy, nullable) UIVisualEffect *effect;
- (instancetype)initWithEffect:(nullable UIVisualEffect *)effect NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
FoundationやUIKitのソースを見てみると、このマクロが使われています。
確かに、nonnull
,nullable
をいちいち書いていくのは大変ですよね。Objective-Cでnonnull
,nullable
をつける必要がある場合は積極的にNS_ASSUME_NONNULL_BEGIN
とNS_ASSUME_NONNULL_END
を使い、nullable
だけを書いていくと良いのかもしれません。
注意
同じファイル内のメソッドやプロパティに一つでもnonnull
,nullable
をつけた場合、ファイル内のすべてのメソッドの引数、戻り値、プロパティに型修飾子をつけなければいけません。warnigが出ます。

Lightweight Generics
SwiftからObjective-Cのコードを使おうとした時に、NSArrayからArrayの変換、NSDictionaryからDictionaryの変換では困りませんが、配列の要素の型がAnyObjectになってしまい困ることがあると思います。
このような場合、guard
やOptional Bindingなどを使って安全にキャストしていくことになると思いますが、Objective-CのコードをGenericsを使って修正したほうが良さそうです。
UIViewはsubviews
というNSArray
のプロパティを持っていますが、Genericsを使ってUIView
の配列であることを明示しています。
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *subviews;
__kindof
はサブクラスも許容するアノテーションです。subviews
はUIViewのサブクラスも許容することを示しています。
Objective-CにNullabilityとGenericsを指定していく工程
Objective-Cで以下のように定義されているオブジェクトを見てみます。
@interface MNPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSUInteger age;
@property (nonatomic, copy) NSArray *items;
- (NSString*)hey;
- (instancetype)initWithName:(NSString*)name age:(NSUInteger)age;
@end
これをSwiftからも使いやすいようにNullabilityとGenericsを指定してみます。
何も手を加えない場合、Swiftからはこのように見えます。イニシャライザやプロパティに!
がついていてImplicitly Unwrapped Optionalになっていることが分かります。
public class MNPerson : NSObject {
public var name: String!
public var age: UInt
public var items: [AnyObject]!
public func hey() -> String!
public init!(name: String!, age: UInt)
}
Objective-CのヘッダがSwiftでどのように表示されるかを確認する方法
Objective-CでNullabilityやGenericsの指定をする際に、jump barの左端にあるボタンをクリックすると出現する"Generated Interface"を使ってObjective-CのヘッダファイルがSwiftでどのようなインターフェースになるか確認することができます。

nullable
の設定
まずはnullable
をつけてみましょう。こうなります。
@interface MNPerson : NSObject
@property (nullable, nonatomic, copy) NSString *name;
@property (nonatomic) NSUInteger age;
@property (nullable, nonatomic, copy) NSArray *items;
- (nullable NSString*)hey;
- (nullable instancetype)initWithName:(nullable NSString*)name age:(NSUInteger)age;
@end
Nullabilityはポインタ型にのみ指定するものなので、プリミティブな値、NSUInteger
などにはnullable
をつける必要はありません。
これをGenerated Interfaceで見てみると以下のようになります。
public class MNPerson : NSObject {
public var name: String?
public var age: UInt
public var items: [AnyObject]?
public func hey() -> String?
public init?(name: String?, age: UInt)
}
nonnull
の設定
とりあえずOptionalとして扱えるようになりました。しかし、すべてOptionalだといちいちOptional Bindingなど値を取り出さなければならないのでちょっと面倒です。
nonnull
として扱える箇所がないか実装を見てみます。
- (instancetype)initWithName:(NSString*)name age:(NSUInteger)age {
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
- (NSString*)hey {
return @"hey";
}
イニシャライザでインスタンス変数の_name
と_age
に値がセットされています。hey
メソッドも失敗の可能性はないので、これらに該当するプロパティやメソッドの引数をnonnull
にしてみましょう。
@interface MNPerson : NSObject
@property (nonnull, nonatomic, copy) NSString *name;
@property (nonatomic) NSUInteger age;
@property (nullable, nonatomic, copy) NSArray *items;
- (nonnull NSString*)hey;
- (nonnull instancetype)initWithName:(nonnull NSString*)name age:(NSUInteger)age;
@end
Swiftで見てみるとこのようになります。
public class MNPerson : NSObject {
public var name: String
public var age: UInt
public var items: [AnyObject]?
public func hey() -> String?
public init(name: String, age: UInt)
NS_ASSUME_NONNULL_BEGIN
,NS_ASSUME_NONNULL_END
マクロを使うと以下のように書くことができます。
NS_ASSUME_NONNULL_BEGIN
@interface MNPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSUInteger age;
@property (nullable, nonatomic, copy) NSArray *items;
- (NSString*)hey;
- (instancetype)initWithName:(NSString*)name age:(NSUInteger)age;
@end
NS_ASSUME_NONNULL_END
nil
の可能性がある箇所のみnullable
を指定する必要がありますがnonnull
のプロパティに対しては指定が不要になります。
結果は先ほどと同様、以下の通りになります。
public class MNPerson : NSObject {
public var name: String
public var age: UInt
public var items: [AnyObject]?
public func hey() -> String
public init(name: String, age: UInt)
}
ジェネリクスの設定
これでOptionalの設定は完了しましたが、Swiftから使う際に面倒な点が一点残っています。Generated Interfaceを見てみましょう。
public class MNPerson : NSObject {
public var name: String
public var age: UInt
public var items: [AnyObject]?
public func hey() -> String
public init(name: String, age: UInt)
}
items
プロパティがAnyObject
の配列になっています。いちいちキャストするのも面倒です。ここではitems
に格納されているのは文字列だと仮定してジェネリクスの設定を行います。
NS_ASSUME_NONNULL_BEGIN
@interface MNPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSUInteger age;
@property (nullable, nonatomic, copy) NSArray<NSString*> *items;
- (NSString*)hey;
- (instancetype)initWithName:(NSString*)name age:(NSUInteger)age;
@end
NS_ASSUME_NONNULL_END
items
プロパティに対してNSString
を指定しました。Generated Interfaceを見てみます。
public class MNPerson : NSObject {
public var name: String
public var age: UInt
public var items: [String]?
public func hey() -> String
public init(name: String, age: UInt)
}
以上のように[String]
となっていることが分かります。
このように、実装を確認しながらSwiftから利用しやすいようにしていくことが可能です。
まとめ
コード量的にはそこまでガッツリと書き直さなくてもSwiftから扱いやすいようにインターフェースを修正していくことが可能です。
実装が把握できている場合は、このようなNullabilityやGenericsを指定することで既存のObjective-CのコードをSwiftから使いやすくできることは間違いないと思います。
ただし、これらの置き換えは以外と簡単にできるものではない印象です。理由としては、一つだけnullable
やnonnull
を指定することはできなかったり、それなりに大きなクラスになってくるとnullable
なのかnonnull
なのか簡単に判断できないことが多くなる印象です。
Objective-Cの場合は基本的にはnullable
になると思うのですが、Swiftで利用する際にOptionalとして扱わなければならないため、単純にnullable
に置き換えることに大きなメリットは感じません。
それでもOptionalとして扱えるようになることはメリットだと思いますし、、AnyObject
からのキャストが減ることはSwiftのコードを書いていく上でメリットになると思います。
参考
- https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
- https://developer.apple.com/swift/blog/?id=31
- https://developer.apple.com/videos/play/wwdc2015-401/
- https://developer.apple.com/swift/blog/?id=25
- http://www.slideshare.net/GoichiHirakawa/new-objectivec-features-for-swift-20
- http://qiita.com/hironytic/items/16920fb0a5c8d8127e10
- http://qiita.com/yimajo/items/e31496e575fe576649d3