search
LoginSignup
4

More than 1 year has passed since last update.

posted at

updated at

モバイルチームの成長とKMM導入に向けて

はじめに

株式会社RevCommでモバイルアプリを担当している木下です。
この記事は 2020年のRevCommアドベントカレンダー 19日目の記事になります。18日目は @zomaphone さんの 【既存の解析システムに対して pytest-mock と pydantic を活用してクイックに総合テストを実装した話】 でした。

MiiTel Phone Mobile アプリについて

開発している MiiTel Phone Mobile アプリの主な機能はVoIPで、RevComm のメインプロダクトである MiiTel のオプションとしてご利用いただけます。当初は Cordova で開発されたβ版アプリが無料で提供されていましたが、アプリを起動していないと着信が受けられないなどの不都合があり、これらのユーザー体験を改善するためにネイティブ(Swift/Kotlin)で開発し直したものが現行のアプリとなります。

モバイルチームについて

モバイルアプリの開発を担うモバルチームの変遷を簡単に紹介します。

1. 一人チーム時代

約2年前にRevCommにジョインしてから、iOSとAndroidの両アプリを開発・リリースし、サポート対応や機能改善、OSアップデート対応などをほぼ一人でやってきました。アプリの実装だけでなく、モバイルアプリのための 通信サーバー の設定やサーバーアプリの改修なども対応してきました。

2. 新メンバー参入

今年の10月ついにモバイルチームに、新しいメンバーが2名加わりました。ともにiOSアプリとAndroidアプリの開発経験があり、どちらも安心して任せられるメンバーです。
私としては フルリモート・フルフレックスタイム制を導入しているRevComm で社員を迎え入れるのが初めてでした。組織として急成長してきたことや、リモートワークを推奨していることにより、コミュニケーションの不足や取りづらさの課題を以前から感じていたため、この点を特に留意し、新しいメンバーが発言・活動しやすい環境にしてプロダクトの成長に貢献できるようなチーム作りを目指しました。具体的な内容については、また機会があれば紹介したいと思います。

3. そしてさらなる成長に向けて

新たなメンバーが加わってチームとして機能するようになり、開発のスピードも上がってきました。これまでのワンオペでは機能を追加するとしても、iOSアプリの実装とリリースをしてから、Androidも同様の対応をするというフローとなり、リリースのタイミングがズレることがほとんどでしたが、メンバーが増えたことで両アプリ同時に新機能をリリースするといったことも可能になってきました。
そして、これからさらにスピードを上げるために導入を検討しているのが、マルチプラットフォーム開発です。
一つの機能を追加するのに、SwiftとKotlinで同様のロジックを実装しなくてはいけない、というのは非効率であると感じており、これを解消するためにもマルチプラットフォーム開発を導入したいと考えています。
React Native、Flutter、KMM(Kotlin Multiplatform Mobile) などありますが、既存のコードを生かしつつ、徐々に共通化が進められそう、かつ言語的に従来のモバイルエンジニアでもスムーズに開発できそうな、KMM が有力であると考えています。私自身 前職 ではAndroidアプリをメインでやっていたこともあり、KotlinでiOSアプリも開発できたら嬉しいなと考えていたところで、 Kotlin Multiplatform Mobile がアルファ段階に移行 というニュースを目にしたのをきっかけに、導入意欲が高まりました。

KMM(Kotlin Multiplatform Mobile) 入門

以前から Kotlin/Native という形でマルチプラットフォーム向けの開発環境は提供されていましたが、KMM ではiOSとAndroidのモバイルアプリに特化し Android Studio などの IDEに統合可能な環境となっており、より簡単に扱えるようになったと認識しています。
個人的にFlutterは少し触っている(Widgetの充実具合や高速な Hot reload は素晴らしいですよね)のですが、KMM は未経験でしたのでこの機会(Advent Calendar)に少し触ってみようと思い、 KMM Shared Module を作成して、AndroidアプリとiOSアプリのそれぞれでインポートして動作させる、ことをやってみようと思います。

1. 開発環境

公式サイト にも記載されていますが、次のような環境が必要となります。

  • Android Studio 4.1 以降
  • Xcode 11.3 以降
  • (AS) Kotlin plugin 1.4.20 以降
  • (AS) Kotlin Multiplatform Mobile plugin
  • JDK

2. Androidプロジェクト作成

KotlinNativeAndroid というプロジェクトを作成しました。
AndPrg.png

3. KMM Shared Module 作成

KotlinNativeAndroid を開いた状態でメニューの File > New > New Module... を選択し KMM Shared Module を選択してから Next をクリックします。
kmm-module.png

次の画面で Generate packFoxXcode Gradle task をチェックしてから Finish をクリックします。(他はデフォルトのままとしました)
kmm-module2.png

次のようなファイルが作成されました。
kmm-module3.png

以下のような内容となっており、OSのバージョンを含む文字列を返すメソッドがサンプルとして作成されていることが分かります。

commonMain/.../Greeting.kt
package com.example.kmmsharedmodule

class Greeting {
    fun greeting(): String {
        return "Hello, ${Platform().platform}!"
    }
}
commonMain/.../Platform.kt
package com.example.kmmsharedmodule

expect class Platform() {
    val platform: String
}
androidMain/.../Platform.kt
package com.example.kmmsharedmodule

actual class Platform actual constructor() {
    actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}
iosMain/.../Platform.kt
package com.example.kmmsharedmodule

import platform.UIKit.UIDevice

actual class Platform actual constructor() {
    actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

KotlinNativeAndroid プロジェクト内のファイルも自動で変更されます。

settings.gradle
include ':kmmsharedmodule' <- 追加
gradle.properties
kotlin.mpp.enableGranularSourceSetsMetadata=true <- 追加
kotlin.native.enableDependencyPropagation=false <- 追加

4. Androidプロジェクト修正

app/build.gradle
dependencies {
    implementation project(':kmmsharedmodule') <- 追加
}

KMM Shared Module から文字列を取得して、画面に表示する処理を実装します。

MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = ActivityMainBinding.inflate(layoutInflater)
        binding.textView.text = Greeting().greeting()
        setContentView(binding.root)
    }
}

ここで実行してみると次のようなエラーが発生しました。

Manifest merger failed : uses-sdk:minSdkVersion 23 cannot be smaller than version 24 declared in library [:kmmsharedmodule] /Users/tkinoshita/kotlin-native-example/KotlinNativeAndroid/kmmsharedmodule/build/intermediates/library_manifest/debug/AndroidManifest.xml as the library might be using APIs not available in 23
    Suggestion: use a compatible library with a minSdk of at most 23,
        or increase this project's minSdk version to at least 24,
        or use tools:overrideLibrary="com.example.kmmsharedmodule" to force usage (may lead to runtime failures)

Moduleの minSdkVersion がアプリよりも高いことが原因のようですので、Module の minSdkVersion をデフォルト値の 24 から、アプリの設定と同じ 23 に変更します。また compileSdkVersion と targetSdkVersion もアプリに合わせました。

build.gradle.kts
android {
    compileSdkVersion(30)
    defaultConfig {
        minSdkVersion(23)
        targetSdkVersion(30)
    }

5. Androidアプリ実行結果

正常に実行されると次のような表示となります。

6. Framework 出力

続いてiOSアプリでもModuleを利用していきますが、iOSアプリ(Xcode)で利用するためには、Framework 形式でライブラリを出力する必要があります。
build.gradle.kts の下の方に Framework を出力するためのタスクが記述されているので、これを利用します。
Terminal で次のコマンドを実行します。

./gradlew :kmmsharedmodule:build

成功すると、 kmmsharedmodule/build/xcode-frameworks 内に kmmsharedmodule.framework フォルダが作成され Framework が出力されます。

7. Xcodeプロジェクト作成

次に iOSアプリのプロジェクトを作成していきます。
Xcodeで KotlinNativeiOS という名前の App プロジェクトを新規作成します。
iosPrg.png

8. Xcodeプロジェクト修正

Build SettingsFramework Search Paths に Framework のパスを追加します。ここでは $(SRCROOT)/../KotlinNativeAndroid/kmmsharedmodule/build/xcode-frameworks を設定しています。
スクリーンショット 2020-12-18 6.26.36.png

アプリに Framework を組み込むための設定を追加します。
スクリーンショット 2020-12-18 6.30.17.png

KMM Shared Module から文字列を取得して、画面に表示する処理を実装します。

ViewController.swift
import UIKit
import kmmsharedmodule

class ViewController: UIViewController {

    @IBOutlet weak var textLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        textLabel.text = Greeting().greeting()
    }
}

9. iOSアプリ実行結果

正常に実行されると次のような表示となります。

10. ソースコード一式

こちらのリポジトリ で公開しています。

おわりに

KMM Shared Moduleの作成から AndroidアプリとiOSアプリで利用する流れが把握でき、おおよその感触がつかめました。どこまでロジックが共通化できるかはまだ未知ですが、期待していたとおり簡単にモジュールの作成と利用ができましたので、これから積極的に採用していきたいと思います。

明日はモバイルバックエンド担当の @rhoboro さんです!!
実は @rhoboro さんもモバイルチームの一員としてバックエンドの開発を担当されています!

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4