React Nativeの対応が追いついてないNative機能を使うには、自分でブリッジを実装する必要がある。
ただ公式のDocsはほぼObj-Cのことしか書いてなくて、Swiftでもできるよ!って一言くらいしかない。
もちろん調べてもろくな例が出てこない!
かといってObj-CよりSwiftの方が親しみやすい気がするなーということで・・・、四苦八苦しながらやり遂げたことをメモ。
アプリエンジニアではないので、非効率なコードや勘違いしてることを書いてる可能性もありです!
必要なファイル
いちおうドキュメントはコレ。
基本的な流れはココに書いてあるけど、Swift版の端折られ感よ!
要約すると、
- `XXX.swift`で実装する
- `XXX.m`でエクスポートする
- それらを認識させる`MyProject-Bridging-Header.h`を用意する
この3つが必要。
サンプルコード
端末内に同期してある音楽ファイルから、アルバムの一覧を取得するやつ。
それぞれのファイルを用意していく。
以下は`MyProject`というプロジェクトで、`XXX`というブリッジモジュールを作る前提です。
MyProject-Bridging-Header.h
`.swift`のファイルを追加したらXcodeが勝手に追加してくれるけど、してくれないなら自分で作る。
#ifndef MyProject_Bridging_Header_h #define MyProject_Bridging_Header_h #import "RCTBridgeModule.h" #endif
`RCTBridgeModule.h`をインポートしてるのが重要。
XXX.m
#import <Foundation/Foundation.h> #import "RCTBridgeModule.h" @interface RCT_EXTERN_MODULE(XXX, NSObject) RCT_EXTERN_METHOD(getAlbums: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) @end
`.swift`側で実装したものをエクスポートする記述たち。
`XXX`ってクラスと`getAlbums()`ってメソッドを定義してて、これは`Promise`を返すようになってる。
このObj-Cの文法はさっぱりわからん(なぜ第二引数からラベルがいるのかとか)けど、こう書けば動く。
さて次が本丸のSwift実装。
XXX.swift
import Foundation import MediaPlayer @objc(XXX) class XXX: NSObject { @objc func getAlbums(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { let albumsQuery: MPMediaQuery = MPMediaQuery.albumsQuery() // 略すが↑を使って必要なものを↓で`resolve`する resolve([]) } }
`@objc`ってのが重要らしい。
ここはただのSwiftなので、あれこれ調べながら頑張って実装する。
もちろんさっきエクスポートする時に指定したものと同じ名前じゃないとコンパイルエラーになる。
ハマりポイントとしては、React Native側に渡せる値の型。
ようはプリミティブな値しか渡せないのでstructの配列とか返しちゃうと盛大にエラーになるので注意。