世界のやまさ

SEKAI NO YAMASA

Objective-Cからswiftに書き直すときの対処まとめ

Microsoft Azure Mobile Servicesのサンプルアプリswiftで書き直して、Pull Request を行いました

残念ながらこの Pull Request は取り込まれなかった(私が行った二日後ぐらいに、別のPull Requestで同等の機能を中の人が実装したから。「ちょっ、おまwwwww」っていう感じだけど。)のですが、そのときにいろいろとノウハウが貯まったので対処をまとめました。

dyld: Library not loaded: @rpath/libswift_stdlib_core.dylib というエラーメッセージが出てアプリが起動しない

手始めにmain.mを削除してとQSAppDelegate.swiftに置き換えたらこれが出てハマりました。 Xcodeを再起動したら直りました。

'AnyObject[]' does not have a member named 'mutableCopy'

NSArrayをmutableCopyしてNSMutableArrayに入れようとしたらエラー。キャストではなく、NSMutableArrayのコンストラクタで返すようにしたらうまくいったっぽい。

// 修正前
var items = results.mutableCopy() as NSMutableArray

// 修正後
var items = NSMutableArray(array:results)

Ambiguous use of 'NSNotFound'

iPhone5sでビルドしていたときは良かったけど、iPhone5iPhone4sでビルドしたらなぜか上記エラーでビルドが通らなくなった。多分、#ifでプラットフォームごとに読み込むヘッダーを変えているからだと思われる。Foundation.NSNotFoundとすることで解消した。

// 修正前
if (index != NSNotFound) { //... }

// 修正後
if (index != Foundation.NSNotFound) { //... }

ブロックのメソッド定義と呼び出し方

これは慣れるとそうでもないんだけど、慣れるまでわけわからなかった。

空のブロックの場合

空の場合はそんなに違いないですかね。

objective-c

// 定義
typedef void (^QSCompletionBlock) ();
- (void)refreshDataOnSuccess:(QSCompletionBlock)completion;

// 呼び出し方
[self.todoService refreshDataOnSuccess:^
{
    if (self.useRefreshControl == YES) {
        [self.refreshControl endRefreshing];
    }
    [self.tableView reloadData];
}];

swift

// 定義
func refreshDataOnSuccess(completion:()->Void)

// 呼び出し方
todoService.refreshDataOnSuccess({() in
    if (self.useRefreshControl == true) {
        self.refreshControl.endRefreshing()
    }
    self.tableView.reloadData()
})

引数が一つあるブロックの場合

swiftでのポイントは呼び出し時のブロックに型を指定しないことですかね。 swift での NSUInteger は UInt ではなく、Int で良いみたいです。UInt も用意されているようだけど。

Swift bridges NSUInteger and NSInteger to Int. Both of these types come over as Int in Foundation APIs.

Working with Cocoa Data Typesより

objective-c

// 定義
typedef void (^QSCompletionWithIndexBlock) (NSUInteger index);
- (void)addItem:(NSDictionary *)item
     completion:(QSCompletionWithIndexBlock)completion;

// 呼び出し方
[self.todoService addItem:item completion:^(NSUInteger index)
{
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
    [view insertRowsAtIndexPaths:@[ indexPath ]
        withRowAnimation:UITableViewRowAnimationTop];
}];

swift

// 定義
func addItem(item:NSDictionary, completion:(Int)->Void)

// 呼び出し方
todoService.addItem(item, completion:{index in
            let indexPath = NSIndexPath(forRow:index, inSection:0)
            view.insertRowsAtIndexPaths([indexPath], withRowAnimation:UITableViewRowAnimation.Top)
        })

引数が3つあるブロックの場合

引数1つの時とあんまり変わらないです。

objective-c

// 定義
typedef void (^MSReadQueryBlock)(NSArray *items,
                                 NSInteger totalCount,
                                 NSError *error);
-(void)readWithPredicate:(NSPredicate *) predicate
      completion:(MSReadQueryBlock)completion;

// 呼び出し方
[self.table readWithPredicate:predicate completion:^(NSArray *results, NSInteger totalCount, NSError *error)
{
    [self logErrorIfNotNil:error];
    
    items = [results mutableCopy];
    
    // Let the caller know that we finished
    completion();
}];

swift

// 定義
... 省略 ...

// 呼び出し方
table.readWithPredicate(predicate, completion:{results, totalCount, error in
self.logErrorIfNotNil(error)

self.items = NSMutableArray(array:results)

// Let the caller know that we finished
completion();
})

以上、swift対応で私が苦労した点でした。Pull Request はまたチャレンジしたいと思いますw