最近Objective-Cで書かれたプロジェクトにSwift (1.2)を突っ込んでいっています。
知見が溜まってきたので、共有しようと思います。
なおSwiftとObjective-Cの互換性については下記などに情報があります。
Objc側で.hで宣言した定数が見えない
下記のようにヘッダで宣言しても、ビルドのlinkerでコケます。
// Foo.h
static NSString * kSomeConstant = @"hello";
@interface Foo : NSObject
@end
下記のようにしましょう。
// Foo.h
extern NSString * const kSomeConstant
@interface Foo : NSObject
@end
// Foo.m
NSString * const kSomeConstant = @"hello";
@implementation Foo : NSObject
@end
objc側のdefineマクロが見えない
これはまあ、そうですよね、という。
objcからCommand + Clickで宣言部にジャンプすると、{Product}-Swift.h に飛んで、実装が見えない。
これは元の.swiftに飛んでくれてもいいんじゃないかな!と思ったり。
#if DEBUG が効かない?
# if DEBUG
NSLog(s)
# endif
という感じのコードを移植して
# if DEBUG
println(s)
# endif
のようにしたのですが、ログが出力されませんでした。
よく考えたらOther Swift Flagに何も値を入れてないだけでした。
インラインdefine関数定義が移植できない
結構みなさんロガーにこういうのを使ってることもあるかと思いますが、
# ifdef DEBUG
# define DEBUGLOG(...) NSLog(__VA_ARGS__)
# else
# define DEBUGLOG(...)
# endif
これはSwiftから全く見えません。
でも、例えばprintlnとかはインライン定義で使われていますよね。
@inline(never) func println<T>(value: T)
もしやこれを真似して定義すればそのまま置き換えられる?と思って試してみました。
@inline(never) func DEBUGLOG(s:String) {
#if DEBUG
println(s)
#endif
}
Swift側はこれでよかったんですが、Objective-Cから使う時にC99のエラーが出ました。
よくわからんですが、ひとまずインラインで同じものを作るのは諦めて、こんな感じで対応しました。
struct Logger {
static func debug(s:String) {
// このDEBUGはマクロではなく、Other Swift Flagの設定値であることに注意!
#if DEBUG
println(s)
#endif
}
static func info(s:String) {
println(s)
}
}
ロガーについてはいろいろオープンソースのライブラリも出ているので、導入を検討してもいいかもしれません。
この辺りはSwiftで書かれています。
- https://github.com/DaveWoodCom/XCGLogger
- https://github.com/hubertr/Swell
- https://github.com/matteocrippa/awesome-swift#logging
いずれにしてもマクロが使えないため、info,debug,error,warningなど出し分けるにはplist等で設定してね、ということになっているようです。
ちなみに、NSLogよりprintlnの方が高速なようです。
メソッドチェーン
Objective-Cはnilに対して寛容な仕様なので、例えばメソッドチェーンの途中でオプショナルが返っているところがある場合、if let
でオプショナルを展開してあげないとダメです。
例えば、こんな感じですね。
if let url = NSBundle.mainBundle().appStoreReceiptURL?.path {}
以上です!