はじめに
Xcode 7(iOS 9.0 / OS X 10.11 / watchOS 2.0, Apple LLVM version 7.0.0)では、SwiftからObjective-Cを使いやすくするための以下の4つのマクロが追加されている。
NS_SWIFT_UNAVAILABLE(_msg)
NS_REFINED_FOR_SWIFT
NS_SWIFT_NAME(_name)
NS_SWIFT_NOTHROW
NS_SWIFT_UNAVAILABLE(_msg)
Foundation/NSObjCRuntime.h
#define NS_SWIFT_UNAVAILABLE(_msg) CF_SWIFT_UNAVAILABLE(_msg)
CoreFoundation/CFAvailability.h
#if __has_feature(attribute_availability_swift)
#define CF_SWIFT_UNAVAILABLE(_msg) __attribute__((availability(swift, unavailable, message=_msg)))
#else
#define CF_SWIFT_UNAVAILABLE(_msg)
#endif
いくつかのObjective-CインターフェイスはSwiftインターフェイスとして公開されるのにふさわしくない、または必要でないかもしれない。Objective-Cの宣言がSwiftによってインポートされないようにするために、NS_SWIFT_UNAVAILABLE(_msg)
を利用すること。APIコンシューマに存在するかもしれない代案を指示するメッセージを引数に渡す。
例えば、キーと値のペアの可変長引数を取る簡易イニシャライザを提供するObjective-Cクラスは、Swiftコンシューマに代わりに辞書リテラルを利用ことを忠告する:
+ (instancetype)collectionWithKeysAndValues:(id)firstKey, ... NS_SWIFT_UNAVAILABLE("Use a dictionary literal instead");
Swiftコードから+collectionWithKeysAndValues:
を呼び出そうとするとコンパイルエラーになる。
NS_REFINED_FOR_SWIFT
Foundation/NSObjCRuntime.h
#define NS_REFINED_FOR_SWIFT CF_REFINED_FOR_SWIFT
CoreFoundation/CFBase.h
#if __has_attribute(swift_private)
# define CF_REFINED_FOR_SWIFT __attribute__((swift_private))
#else
# define CF_REFINED_FOR_SWIFT
#endif
NS_REFINED_FOR_SWIFT
マクロは、元の実装を有効にしたまま、Swiftで同じAPIの改良版を提供するためにObjective-Cの宣言を移動するために使用することができる。(例えば、Class
を取るObjective-CのAPIはSwiftでより的確なパラメータ型を提供することができる。)
NS_REFINED_FOR_SWIFT
マクロは異なる宣言で動作が異なる:
-
初期化メソッドは、最初の外部パラメータ名の前に付加された“__”を持つSwiftのイニシャライザとしてインポートされる。
- (instancetype)initWithClassName:(NSString *)name NS_REFINED_FOR_SWIFT;
init(__className: String)
-
他のメソッドは、そのベース名の前に“__”が付加されてインポートされる。
- (NSString *)displayNameForMode:(DisplayMode)mode NS_REFINED_FOR_SWIFT;
func __displayNameForMode(mode: DisplayMode) -> String
-
サブスクリプトメソッドは他のメソッドと同様に扱われ、サブスクリプトとしてインポートされない。
-
他の宣言は、その名前の前に付加される“__”を持つ。
@property DisplayMode mode NS_REFINED_FOR_SWIFT;
var __mode: DisplayMode { get set }
NS_SWIFT_NAME(_name)
Foundation/NSObjCRuntime.h
#define NS_REFINED_FOR_SWIFT CF_REFINED_FOR_SWIFT
CoreFoundation/CFBase.h
#if __has_attribute(swift_name)
# define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
#else
# define CF_SWIFT_NAME(_name)
#endif
NS_SWIFT_NAME
マクロは、その定数がSwiftにきれいにマップされない列挙型のインポートを制御するために使用することができる。例えば:
typedef NS_ENUM(NSInteger, DisplayMode) {
DisplayMode256Colors NS_SWIFT_NAME(With256Colors),
DisplayModeThousandsOfColors,
DisplayModeMillionsOfColors
};
これは以下のようにSwiftにインポートされる:
@objc enum DisplayMode : Int {
case With256Colors
case ThousandsOfColors
case MillionsOfColors
}
このマクロは、ファクトリメソッドがイニシャライザとしてインポートされているかどうかを制御するためにも使用することができる。例えば:
@interface MyController : UIViewController
+ (instancetype)standardControllerForURLKind:(URLKind)kind NS_SWIFT_NAME(init(URLKind:));
@end
これは、自動ファクトリメソッドのインポートの規則に従っていない場合でも、以下のようにSwiftにインポートされる:
class MyController : UIViewController {
init(URLKind kind: URLKind)
}
NS_SWIFT_NOTHROW
Foundation/NSObjCRuntime.h
#if __has_attribute(swift_error)
#define NS_SWIFT_NOTHROW __attribute__((swift_error(none)))
#else
#define NS_SWIFT_NOTHROW
#endif
スローするメソッドとしてSwiftによってインポートされないようにするためにNSError
を生成するObjective-Cメソッドの宣言には、NS_SWIFT_NOTHROW
マクロを利用すること。
例:
Foundation/NSURL.h
- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error NS_SWIFT_NOTHROW NS_AVAILABLE(10_6, 4_0);