- 投稿日:2020-10-09T22:45:25+09:00
【ReactNative】SwiftでNativeModuleを書く
はじめに
iOSのネイティブAPIにアクセスするために、ReactNatveには
Native Module
というAPIが用意されています。
今回は、Swift
でネイティブモジュールを実装する方法をまとめます。ReactNativeプロジェクトの作成
まだプロジェクトがない場合は、作成します。
npx react-native init myAppXCodeでSwiftファイルを作成する
ios/myApp.xcodeproj
にあるプロジェクトをXCodeで開きます。
NativeModules
フォルダを作成します。(任意)
NativeModules
フォルダ内に.swift
ファイルを作成します。
Objective-C Bridging Header
を作成する
.swift
ファイルを作成すると、XCodeからObjective-C Bridging Header
を作成するかどうか聞かれるの、作成する。このファイルは、名前の通りSwiftファイルと
Objective-C
ファイルをブリッジするものです。
ファイル名は変えてはいけません。以下のように、
Objective-C Bridging Header
に追記します。myApp-Bridging-Header.h// myApp-Bridging-Header.h #import"React/RCTBridgeModule.h"メソッドを実装する
手始めに、最も簡単なネイティブモジュールを実装してみましょう。
カウンターの値を変化させます。Counter.swiftimport Foundation@objc(Counter) class Counter: NSObject { private var count = 0 @objc func increment() { count += 1 print("count is \(count)") } }メソッドをReactNativeから扱えるようにする
実装したメソッドをReactNativeから扱えるように
Objective-C
のファイルを作成します。先ほど作成したSwiftファイルと同じ名前で、同ディレクトリに作成します。
作成したファイルには以下を追記します。
Counter.m@interface RCT_EXTERN_MODULE(Counter, NSObject) RCT_EXTERN_METHOD(increment) @endRCT_EXTERN_METHODについて
RCT_EXTERN_METHOD
に記述したメソッドは、ReactNativeで使用することができます。メソッドに引数がない場合は、以下のように記述します。
RCT_EXTERN_METHOD(methodName)引数がある場合は、以下のように記述します。
RCT_EXTERN_METHOD( methodName: (paramType1)internalParamName1 )例えば、
increment
メソッドに引数が必要な場合swift
ファイルは以下のようになります。@objc func increment(_ num: Int) { ... }対応する
RCT_EXTERN_METHOD
は以下のようになります。RCT_EXTERN_METHOD( increment: (Int)num )ReactNativeから呼び出す
NativeModules
をインポートし、クラス名.メソッド
で呼び出すことができます。import { NativeModules } from 'react-native' NativeModules.Counter.increment(1));Event Emitterを扱う
iOSネイティブ側で起こるイベントをSunscribeしたい時には、
RCTEventEmitter
を使用します。
Objective-C
のファイルに、以下を記述します。#import "React/RCTBridgeModule.h" #import "React/RCTEventEmitter.h" @interface RCT_EXTERN_MODULE(Counter, RCTEventEmitter)さらに、ブリッジファイルにも追記します。
CounterApp-Bridging-Header.h#import "React/RCTBridgeModule.h" #import "React/RCTEventEmitter.h"
Swiftファイルには以下を実装します。
sendEvent
: イベント名と、その内容を記述supportedEvents
: ReactNative側でリスナーを貼る時のイベント名を記述@objc(Counter) class Counter: RCTEventEmitter { @objc func increment() { count += 1 print("count is \(count)") sendEvent(withName: "onIncrement", body: ["count": count]) } override func supportedEvents() -> [String]! { return ["onIncrement"] } }ReactNative側で、イベントリスナーで待ち受けます。
import { NativeModules, NativeEventEmitter } from 'react-native' const CounterEvents = new NativeEventEmitter(NativeModules.Counter); CounterEvents.addListener( "onIncrement", (res) => console.log(res) ); NativeModules.Counter.increment();[警告] Module requires main queue setupへの対処
以下のような警告が出ることがあります。
これは、モジュールの処理をメインスレッドで行うか、バックグラウンドで行うか設定しなさいという警告です。
以下のように記述し、警告を消すことができます。@objc static func requiresMainQueueSetup() -> Bool { return true }
true
を返す: メインスレッドで処理false
を返す: バックグラウンドで処理まとめ
ネイティブモジュールをSwiftで書く方法をまとめました。
- 投稿日:2020-10-09T22:09:39+09:00
GroupedのUITableViewで一番上の余白を消したい(swift)
はじめに
下記の画像のように
style
がgrouped
のUITableView
で条件によって一番上の余白の表示・非表示を切り替えたいときに試行錯誤したのでやり方をメモ。
余白表示 余白非表示 今回は画像のようにヘッダー表示時は余白をなくして、ヘッダー非表示の場合は余白ありにしたかった。。。
だめだったパターン
とりあえずだめだったパターン。。。下記のように
isHeaderShown
がtrue
のときにセクション0にヘッダーを設定して高さを設定してみた。extension TableViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { guard section == 0 else { return UITableView.automaticDimension } return isHeaderShown ? 50 : UITableView.automaticDimension } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { guard section == 0 else { return nil } return isHeaderShown ? HeaderView() : nil } }結果
初回表示だけいけてるけど2回目以降がおかしい。。。
いけてそうなパターン1
上の方法 + 下記のようにテーブル更新時に
tableHeaderView
をいじってみました。tableView.tableHeaderView = isHeaderShown ? UIView() : nil tableView.reloadData()結果
いけてそう
いけてそうなパターン2
上の方法でもいけてそうですがそもそも一番上にしかヘッダー設定しないなら sectionHeader に View を設定する必要はないと思い最終的には下記のようにしました。
extension TableViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { guard section == 0 else { return UITableView.automaticDimension } return isHeaderShown ? CGFloat.leastNormalMagnitude : UITableView.automaticDimension } } // 更新時の処理 tableView.tableHeaderView = isHeaderShown ? HeaderView(frame: .init(origin: .zero, size: .init(width: 0, height: 50))) : nil tableView.reloadData()結果はパターン1と同じだったのでたぶんいけてそう
おわりに
こんなレイアウトにしたくなるのはレアだと思いますがどなたかの参考になれば幸いです。他にいい方法ご存知でしたらぜひ教えて下さい
今後は
UITableView
よりUICollectionView
使っていった方がいいよって言うのも聞くしもうあんまりテーブルをごちゃごちゃするのはよくないのかもしれない。。。
- 投稿日:2020-10-09T20:36:29+09:00
Xcode12でAppStoreConnectへアプリをアップロードするとITMS-90562: Invalid Bundleでビルドが無効になる問題
手元で解決した方法
PodfileからQuickを削除してXCTest使うように変更したら直りました。
問題
今携わってるプロジェクトではスキームを分けて、AppStoreConnectでalpha, staging, productionの3つの環境のアプリを配布しているのですが、Xcode12にアップデートしてアプリを配布しようとしたらalpha, staging環境のアプリでなぜか表題のエラーが起こるようになりAppleからメールが届くようになりました。
ITMS-90562: Invalid Bundle - The app submission can not be successfully recompiled from bitcode due to missing symbols during linking. You can try to reproduce and diagnose such issues locally by following the instructions from: https://developer.apple.com/library/archive/technotes/tn2432/_index.htmlそれとテストもよく分からないエラーを出して失敗するように。
▸ Processing Info.plist ▸ Running script '[CP] Check Pods Manifest.lock' ▸ Processing Info.plist ▸ Running script '[CP] Check Pods Manifest.lock' ❌ error: Illegal instruction: 4 (in target 'XXXXXTests' from project 'XXXXX') ▸ Linking XXXXXUITests ▸ Generating 'XXXXXUITests.xctest.dSYM' ▸ Running script '[CP] Embed Pods Frameworks' Testing failed: Illegal instruction: 4 Testing cancelled because the build failed. ** TEST FAILED ** The following build commands failed: CompileSwift normal x86_64 CompileSwiftSources normal x86_64 com.apple.xcode.tools.swift.compiler (2 failures) [19:14:01]: Exit status: 65 [!] Error building the application. See the log above.色々試していたのですが、結局解決方法が分からずとりあえずテストで失敗していたのでQuickを使用している部分のテストを削除してみたらテストが通るようになり、そのままXCTestに書き直して再度AppStoreConnectへアップロードしたら表題の問題が起こらないようになりました。
その他
Xcode12でググったりTwitterで検索しても、ワーニングの件や、シミュレータ向けのビルドからarm64アーキテクチャ外す必要があるなどに言及している記事やつぶやきしかひっかからず、困っていました。
一応フォーラムに同じような問題が上がっているのは確認できたのですが、解決はしてなさそう。
あまり起こっていない問題なのかも。
- 投稿日:2020-10-09T20:36:29+09:00
Xcode12でAppStoreConnectへアプリをアップロードするとITMS-90562でビルドが無効になる問題
手元で解決した方法
PodfileからQuickを削除してXCTest使うように変更したら直りました。
問題
今携わってるプロジェクトではスキームを分けて、AppStoreConnectでalpha, staging, productionの3つの環境のアプリを配布しているのですが、Xcode12にアップデートしてアプリを配布しようとしたらalpha, staging環境のアプリでなぜか表題のエラーが起こるようになりAppleからメールが届くようになりました。
ITMS-90562: Invalid Bundle - The app submission can not be successfully recompiled from bitcode due to missing symbols during linking. You can try to reproduce and diagnose such issues locally by following the instructions from: https://developer.apple.com/library/archive/technotes/tn2432/_index.htmlそれとテストもよく分からないエラーを出して失敗するように。。。
▸ Processing Info.plist ▸ Running script '[CP] Check Pods Manifest.lock' ▸ Processing Info.plist ▸ Running script '[CP] Check Pods Manifest.lock' ❌ error: Illegal instruction: 4 (in target 'XXXXXTests' from project 'XXXXX') ▸ Linking XXXXXUITests ▸ Generating 'XXXXXUITests.xctest.dSYM' ▸ Running script '[CP] Embed Pods Frameworks' Testing failed: Illegal instruction: 4 Testing cancelled because the build failed. ** TEST FAILED ** The following build commands failed: CompileSwift normal x86_64 CompileSwiftSources normal x86_64 com.apple.xcode.tools.swift.compiler (2 failures) [19:14:01]: Exit status: 65 [!] Error building the application. See the log above.色々試していたのですが、結局解決方法が分からずとりあえずテストで失敗していたのでQuickを使用している部分のテストを削除してみたらテストが通るようになり、そのままXCTestに書き直して再度AppStoreConnectへアップロードしたら表題の問題が起こらないようになりました。
その他
Xcode12でググったりTwitterで検索しても、ワーニングの件や、シミュレータ向けのビルドからarm64アーキテクチャ外す必要があるなどに言及している記事やつぶやきしかひっかからず、困っていました。
一応フォーラムに同じような問題が上がっているのは確認できたのですが、解決はしてなさそう。
- 投稿日:2020-10-09T20:18:15+09:00
Xcode12で実機デバッグのRunningが遅い
- 投稿日:2020-10-09T16:09:19+09:00
iosでinputタグがボコってみえるのを直す
- 投稿日:2020-10-09T15:10:23+09:00
Array(Element)型を理解しよう!
Array(Element)型について学習したので、アウトプットしていきます
※以下の内容は、学習内容のアウトプット用のため、誤りがある場合があります。予めご了承くださいArray(Element)型とは?
Array(Element)型を一言でいうと、配列を表す型のことです。
例えば
Array(Element)型は[1,2,3]のように配列リテラルを用いて表現することができますqiita.rbvar1.let a = [a,b,c] 2.let b = [1,2,3]Array(Element)型を用いることで、便利なのが
Array(Element)型の値に対して、要素の更新、追加、結合、削除を行えることです。それでは一つずつ深堀りしていきましょう!
要素の更新
qiita.rbvar//0 1 2 1.var numbers = [1,2,3] 2.numbers[1] = [4] // 1番目である2の更新を行っている numbers// [1,4,3]要素の追加
末尾に要素を追加するにはappend(_:)メソッドを使用します
次の例は、[String]型の"d"を追加していいますqiita.rbvar1.let strings = ["a","b","c"] 2.strings append(d)//["a","b","c","d"]また、任意の場所に追加することができます。
任意の場所に要素を追加するには、insert(_: at:1)、メソッドを使用します。次の例では、nsert(_: at:1)***、メソッドをし使用し、2番目に"b"を追加しています。
qiita.rbvar1.let strings = ["a","c","d"] 2.strings insert("b",at1)//["a","b","c","d"]要素の結合
要素の結合は、+演算子でArray(Element)型の値を結合することができます。
qiita.rbvar1.let strings1 = ["あ","い","う"]//[String型] 2.let c =["え","お"]//[String型] let result = strings1+strings1//["あ","い","う","え","お"]要素の削除
要素の削除には、任意の場所を削除するremove(at:)、最後の要素を削除するremoveLast()、全ての要素を削除するremoveAll()メソッドの3つが用意されています。
qiita.rbvarvar strings = ["あ","い","う","え","お"] strings.remove(at:2) strings//["あ","い","え","お"]//"う"が削除 strings.removeLast() strings//["あ","い","え"]//末尾の"お"が削除 strings.removeAll() strings//()
- 投稿日:2020-10-09T13:56:36+09:00
簡単にiOSアプリのアイコンを設定する方法
アイコンの設定
アプリが完成した!と思ったらアイコンの設定をし忘れてた!とやる気を削がれたことはありませんか?この記事では、アプリのアイコンを一瞬で設定する方法をまとめたいと思います。
有名な内容なので、そんなの知ってるよ!と思われた方はそっとブラウザバックをしてください。アイコンを作ろう!
この記事ではアイコンの作り方自体は書きませんが、僕が普段使っているおすすめのツールを紹介したいと思います。
有料のものばかりになってしまいますが、許して、、、Sketch
モックアップを作る際やアイコンを作るとき、ロゴを作る時など基本的にSketchを使って作業しています。Adobe CC
かの有名なAdobe Creative Cloudです。Sketchでは厳しいことなどはイラストレーターやフォトショップを使って作業しています。それでは本題
ここでようやく本題のアプリのアイコンを一瞬で設定する方法を説明したいと思います。
まず、作成したアプリのアイコンを1024*1024のpngやjpgなどに書き出してください。こちらのサイトにアクセスして作成した写真を以下の部分にドラックアンドドロップしてください。
該当するアプリが対応するプラットフォーム(iPhone、iPadなど)にチェックが入っていることを確認して
Generate
を押してダウンロードしてダウンロードされたZipを解凍してください。解凍できたら、中身がこのようになっていることを確認してください。
Xcodeプロジェクトに実際に設定していく
まず、Xcodeプロジェクトを開いて左側にあるファイル一覧から
Assets.xcassets
を右クリックしてShow in Finder
を選択して下さい。
この時点で一度Xcodeを終了しておくと無難です。このような画面は表示されましたか?
このような画面が表示されたら、
AppIcon.appiconset
をフォルダーごと削除しましょう!(⌘+Delete)
削除ができたら、先ほどダウンロードしたフォルダーの中にあるAssets.xcassets
の中のAppIcon.appiconset
を今削除したフォルダーがあったAssets.xcassets
にドラック&ドロップしましょう!ここまで完了したら、Xcodeのプロジェクトを再度開いてアイコンが設定されているか確認しましょう!
以上で完了です!お疲れ様でした!
- 投稿日:2020-10-09T12:51:39+09:00
Optional(Wrapped)型を理解しよう!
Optional型について学習したので、アウトプットしていきます
※以下の内容は、学習内容のアウトプット用のため、誤りがある場合があります。予めご了承くださいOptional(Wrapped)型とは?
Optional(Wrapped)型を一言でいうと、値があるか空かのいずれを表す型です。
基本的に、swiftの変数や定数は基本的にnilを許容しないのですが、そのnilを用いる場合には
Optional(Wrapped)型を使用します!例えば
qiita.rbvar1.var n: Int 2. 3.print(n)//エラー上記のように、存在していない値を出力しようとするとエラーが起こります。
そこで登場するのが、Optional(Wrapped)型ですqiita.rbvar1.var n: Optional<Int> 2. 3.print(n)//nil上記のように、Optional(Wrapped)型を使うとエラーにならず、nilが出力がされます。
このように、「『Optional型』でデータをラップしておくと値が存在しない場合は『nil』を返すようになる」という挙動になるためエラーを回避することができます。これが『Optional型』の基本的な使い方になります.
Optional(Wrapped)型のアンラップとは?
Optional(Wrapped)型は値を持っていない可能性があるため、Wrapped型の変数や定数と同じように扱うことができない。
例えば、Int?型どうしの四則演算はエラーになります。qiita.rbvar1.let a: Int? = 1 2.let b: Int? = 1 3.a+b//エラーこのエラーを回避するために、アンラップを行います。
アンラップの方法は以下の3つです。○ オプショナルバインディング
○ ??演算子
○ 強制アンラップ一つずつ深堀りしていきましょう!
オプショナルバインディングとは?
qiita.rbvarif let 定数名 = Optional(Wrapped)型{ //値が存在する場合に実行される文 }上記の文のように、if-let文を用いてWrapped型の値をもつ場合は{}の文が実行されます。
次の例では、定数Aに値が存在するため、String型の定数aに値が代入され、実行文が実行されます!qiita.rbvar1.let A: Optional("1") //Int型 2.if let A { print(type(of:1)) //実行結果:Int }??演算子とは?
次の例では、??演算子の左辺にString型の値"a"を持った、String型?の定数optionalStringを右辺にString型の値"b"を指定し、結果として左辺の値"a"を取得しています。
qiita.rbvar1.let optionalString:String? = "a" 2.if String = optionalString ?? "b" //実行結果 a強制アンラップとは?
強制アンラップは、Optional(Wrapped)型からWrapped型の値を強制的に取り出す方法です。
強制アンラップを行うには、!演算子を使用します。
上記では、Wrapped型の変数や定数と同じように扱うことができないと述べましたが、強制アンラップ使うことによって取り出すことができます。qiita.rbvarlet a : Int? = 1 let b : Int? = 1 a!+b! = //2
- 投稿日:2020-10-09T09:19:42+09:00
【ios/firebase】初めてのtwitter認証
やりたきこと
firebaseを使ったiosアプリでtwitter認証してみる
誰向け?
つい数時間前の自分
・WEBバックエンド開発を主な生業
・iosアプリ開発は未経験
・twitter利用経験なし(アカウントはさっき作った)
・公式ドキュメントの手順見ても、どこに追記するのかすら分からんそんな数時間前の自分に向けてやさしく解説してみる。
環境
Xcode Version 10.1
CocoaPods Version 1.9.3
Swift Version 4.2.1早速開発
公式 「はじめに」 -> 1 の設定
Firebase/Authをinstalltarget 'app' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for app pod 'Firebase/Auth' <---追加 endpod updateFirebaseのAuthenticationのtwitter認証の設定を行う
公式 「はじめに」 -> 2,3,4 の設定
URL Typesの設定
公式 「Firebase iOS SDK でログインフローを処理するには:」 -> 1の設定
実装
「twitterでSign in」ボタンだけ配置して認証/認可を確認
実装内容は公式丸パクリ!
ViewController.swiftimport UIKit import Firebase class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func twitterSignInButton(_ sender: Any) { var provider = OAuthProvider(providerID: "twitter.com") provider.getCredentialWith(nil) { credential, error in if error != nil { // Handle error. } if credential != nil { Auth().signIn(with: credential) { authResult, error in if error != nil { // Handle error. } // User is signed in. } } } } }・・・すると、ビルドエラーになります
この記事を参考に解決
diffimport UIKit import Firebase +import FirebaseAuth class ViewController: UIViewController { + var twitterProvider : OAuthProvider? + override func viewDidLoad() { super.viewDidLoad() + self.twitterProvider = OAuthProvider(providerID:"twitter.com"); } @IBAction func twitterSignInButton(_ sender: Any) { - var provider = OAuthProvider(providerID: "twitter.com") - provider.getCredentialWith(nil) { credential, error in @IBAction func twitterSignInButton(_ sender: Any) { + self.twitterProvider?.getCredentialWith(_: nil){ (credential, error) in if error != nil { // Handle error. } - if credential != nil { - Auth().signIn(with: credential) { authResult, error in + if let credential = credential { + Auth.auth().signIn(with: credential) { (authResult, error) in if error != nil { // Handle error. }動いた!
動作確認
Sigin Inしても何も実装していないので、最初の画面が出てきちゃいます。
認証されたかはFirebaseの画面で確認
大丈夫そう!
最後に
以前、WEBサービスのtwitter認証周りをやったことがあり、
結構大変だった記憶があるのですが、Firebase使うと簡単にできました。
小規模アプリなら活用する機会多いかもしれないです。
これからアプリのほう拡張していきたいと思います。
- 投稿日:2020-10-09T08:29:51+09:00
Kotlin Multiplatform Mobile がアルファ段階に移行したので、チュートリアルを試す
背景
最近、Kotlin Multiplatform Mobile(KMM)がアルファ段階に移行したようです。
Kotlin Multiplatform Mobile がアルファ段階に移行 – Kotlin Blog | JetBrains
KMM とは、JetBrains が提供するクロスプラットフォーム対応のモバイル開発用 SDK です。
Kotlin のマルチプラットフォーム対応能力を駆使し、モバイルアプリケーションの構築体験を可能な限り楽しく効率的にするように設計されたさまざまなツールや機能を含んでいます。見ていて面白そうだったので、試しにチュートリアルをやってみました。
※2020 年 10 月時点の情報なので、今後のアップデートにより変わる可能性があります。開発環境
開発環境は以下の通りです。
- Android Studio 4.1 RC 3 以降
- Xcode 11.3 以降
- Kotlin 1.4.0 以降
- Java 1.8.0_73 以降
- macOS Catalina バージョン 10.15.6
環境構築
以下をベースに進めていきます。
Getting started - Help | Kotlin Multiplatform Mobile Docs
Android Studio version 4.1 RC 3 以降をインストール
Android Studio Previewから、4.1 RC 3 か 4.2 CANARY 13 のいずれかをインストールします。
どちらでも大丈夫です。Kotlin のバージョンを 1.4.0 以降にアップデート
Configure
->Plugins
から、Kotlin を 1.4.0 以降にアップデートします。Kotlin Multiplatform Mobile をインストール
Configure
->Plugins
から、Kotlin Multiplatform Mobile をインストールします。インストール後、Android Studio を再起動します。
はじめてのマルチプラットフォームアプリ作成
以下をベースに進めていきます。
Create your first multiplatform application - Help | Kotlin Multiplatform Mobile Docs
プロジェクト作成
Create New Project
から新規でプロジェクトを作成します。
Select a Project Template
より、KMM Application
を選択し、Next
をクリックします。各項目を以下の通り入力します。
項目 名称 Name KMM Application Package name com.example.kmmapplication Save location 任意のフォルダ
Configure Activity
ではデフォルトのままFinish
をクリックします。ビルド
早速ビルドしてみます。
まずは Android から。
androidApp
を選択してから、ビルド対象の端末を設定してビルドします。すると、以下のように起動します。
次に iOS を試してみます。
iosApp
を選択し、Edit Confiugrations...
をクリックします。
Execution target
で端末を設定し、ビルドします。すると、以下のように起動します。
テストコード
各プラットフォームごとにテストコードが用意されており、それぞれ以下のようにファイルを開いて実行できます。
Android のほうは、
shared/src/androidTest/kotlin/com.example.kmmapplication.shared
のandroidTest.kt
にあります。package com.example.kmmapplication.shared import org.junit.Assert.assertTrue import org.junit.Test class AndroidGreetingTest { @Test fun testExample() { assertTrue("Check Android is mentioned", Greeting().greeting().contains("Android")) } }iOS のほうは、
shared/src/iosTest/kotlin/com.example.kmmapplication.shared
のiosTest.kt
にあります。package com.example.kmmapplication.shared import kotlin.test.Test import kotlin.test.assertTrue class IosGreetingTest { @Test fun testExample() { assertTrue(Greeting().greeting().contains("iOS"), "Check iOS is mentioned") } }どちらも
Greeting().greeting()
が出力する文字列にプラットフォーム名が含まれているかのテストをしています。
shared/src/commonTest/kotlin
という共通用のテストフォルダも用意されているようですが、デフォルトでは何も入っていませんでした。アプリケーションの更新
画面上では「Hello, [プラットフォーム名とバージョン]」という文字列が表示されていました。この文字列を生成しているクラスが
shared/src/commonMain/kotlin/com.example.kmmapplication.shared
のGreeting.kt
にあります。package com.example.kmmapplication.shared class Greeting { fun greeting(): String { return "Hello, ${Platform().platform}!" } }このディレクトリには、Android と iOS の両方のプラットフォームの共有コードが保存されます。共有コードに変更を加えると、両方のアプリケーションに変更が表示されます。
試しに以下のように変更してみます。class Greeting { fun greeting(): String { return "Guess what it is! > ${Platform().platform.reversed()}!" } }変更後、各プラットフォームでビルドしてみます。
いずれも変更が反映されていることを確認しました。
Android iOS 最後に、Platform クラスについても確認してみます。
commonMain/kotlin/com.example.kmmapplication.shared
のPlatform.kt
です。package com.example.kmmapplication.shared expect class Platform() { val platform: String }
expect
キーワードはマルチプラットフォーム専用のインタフェースにて使われるキーワードで、あるプラットフォームのクラスを使いたい場合に使うキーワードです。
androidMain
とiosMain
にもそれぞれPlatform.kt
があります。Android のほうは、
androidMain/kotlin/com.example.kmmapplication.shared
のPlatform.kt
です。package com.example.kmmapplication.shared actual class Platform actual constructor() { actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}" }iOS のほうは、
iosMain/kotlin/com.example.kmmapplication.shared
のPlatform.kt
です。package com.example.kmmapplication.shared import platform.UIKit.UIDevice actual class Platform actual constructor() { actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion }それぞれ
actual
キーワードが使われています。プラットフォームごとに、expect
クラスをactual
で実装する必要があります。
interface
とそれをimplement
するclass
の関係に似ています。まとめ
Kotlin Multiplatform Mobile(KMM)がアルファ段階に移行したことを受けて、試しにチュートリアルをやってみました。
どうやら共通化するのはロジックのみで、UI は各プラットフォームごとに独自で実装する必要がありそうです。
さらに以下のようなハンズオンも用意されているので、時間があるときにまた試してみます。Hands-on: Networking and Data storage - Help | Kotlin Multiplatform Mobile Docs
参考 URL