NullabilityとGenericsでObjective-CのコードをSwiftから使いやすくする

# 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`になり得ることを明示しています。

```objc:NSData.h
- (nullable instancetype)initWithContentsOfURL:(NSURL *)url;
```

これをSwiftで見てみると、以下のようにfailabel initializer、失敗する可能性のあるイニシャライザとなっていることが分かります。

```swift:NSData
public init?(contentsOfURL url: NSURL)
```

このようにインスタンス生成や引数、戻り値が`nil`になり得る場合は、Objective-C側で`nullable`を指定しておくことで、SwiftでOptionalとして扱うことが可能になります。

Objective-Cで`nullable`を指定しない場合はImplicitly Unwrapped Optionalになります。以下のようなObjective-Cのメソッドで考えてみます。

```objc
- (UIImage*)createImage;
```

`nullable`をつけずにSwiftから使おうとすると変換時にImplicitly Unwrapped Optionalになってしまいます。

```swift
func createImage() -> UIImage!
```
このメソッドの戻り値を使用しようとした際に`nil`だった場合クラッシュしてしまうので、SwiftからこのObjective-Cのコードを使用する際に不安が付きまといます。もし、このような処理を見かけたときは`nullable`を指定し、Swift側でOptionalとして利用できるようにするとSwiftからも安心して利用することができます。

```objc
- (nullable UIImage*)createImage;
```

以上のObjective-Cのコードを修正するとSwiftでは、

```swift
func createImage() -> UIImage?
```
と変換されます。

## `nonnull`

`nonnull`はOptionalではないことを明示するための型修飾子で、`nonnull`を関数やメソッドの引数、戻り値に指定した場合はOptionalな変数を指定することはできなくなります。

### `NS_ASSUME_NONNULL_BEGIN`, `NS_ASSUME_NONNULL_END`
しかしUIKitのソースコードを見てみると`nonnull`が見当たりません。

例えば`UIVisualEffectView`は以下のように定義されていますが、`nonnull`はどこにも書かれていません。`nullable`はしっかりと書かれています。

```objc:UIVisualEffectView.h
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になっています。

```swift
@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にもしっかりとこのマクロが書かれていました。

```objc: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が出ます。

<img width="567" alt="スクリーンショット 2016-02-28 20.05.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/45525/80dc9c9a-8f66-6898-4d7c-13602f382fa8.png">


## Lightweight Generics

SwiftからObjective-Cのコードを使おうとした時に、NSArrayからArrayの変換、NSDictionaryからDictionaryの変換では困りませんが、配列の要素の型がAnyObjectになってしまい困ることがあると思います。

このような場合、`guard`やOptional Bindingなどを使って安全にキャストしていくことになると思いますが、Objective-CのコードをGenericsを使って修正したほうが良さそうです。

UIViewは`subviews`という`NSArray`のプロパティを持っていますが、Genericsを使って`UIView`の配列であることを明示しています。

```objc:UIView.h
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *subviews;
```

`__kindof`はサブクラスも許容するアノテーションです。`subviews`はUIViewのサブクラスも許容することを示しています。

## Objective-CにNullabilityとGenericsを指定していく工程

Objective-Cで以下のように定義されているオブジェクトを見てみます。

```objc
@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になっていることが分かります。

```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)
}
```

### Objective-CのヘッダがSwiftでどのように表示されるかを確認する方法

Objective-CでNullabilityやGenericsの指定をする際に、jump barの左端にあるボタンをクリックすると出現する"Generated Interface"を使ってObjective-CのヘッダファイルがSwiftでどのようなインターフェースになるか確認することができます。

<img width="834" alt="スクリーンショット 2016-02-28 13.55.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/45525/13928c10-9ccd-c98e-2c65-1da032861f0c.png">


### `nullable`の設定

まずは`nullable`をつけてみましょう。こうなります。

```swift
@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で見てみると以下のようになります。

```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)
}
```

### `nonnull`の設定

とりあえずOptionalとして扱えるようになりました。しかし、すべてOptionalだといちいちOptional Bindingなど値を取り出さなければならないのでちょっと面倒です。

`nonnull`として扱える箇所がないか実装を見てみます。

```objc
- (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`にしてみましょう。

```objc
@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で見てみるとこのようになります。

```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`マクロを使うと以下のように書くことができます。

```objc
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`のプロパティに対しては指定が不要になります。

結果は先ほどと同様、以下の通りになります。

```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)
}
```


### ジェネリクスの設定

これでOptionalの設定は完了しましたが、Swiftから使う際に面倒な点が一点残っています。Generated Interfaceを見てみましょう。

```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)
}
```

`items`プロパティが`AnyObject`の配列になっています。いちいちキャストするのも面倒です。ここでは`items`に格納されているのは文字列だと仮定してジェネリクスの設定を行います。

```objc
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を見てみます。

```swift
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