2022년 10월 10일 월요일

Android KMM 간단히 알아보기

Android KMM 간단히 알아보기

KMM 간단히 알아보기

Android, IOS,웹 애시당초 같은 로직을 언어마다 따로 만든다는게 잘못된거 아닌가? 그냥 C++ 하나 정도면 사람이 프로그램하는게 전혀 문제가 없는데, 뭘또 계속 만드는지…여튼 이런 환경에서 Kotlin 이 어느정도 답을 제시하고 있다.

Kotlin MPP 프로젝트는 IntelliJ에서 해도 되고, 익숙한 Android Studio Plugin (Kotlin Multiplatform Mobile) 을 설치해서 해도 된다.

AndroidStudio 를 이용하면 바로 IOS 도 실행 해볼수 있다.(안되더라)

  1. 안드로이드스튜이도에서 신규작성을 할때, 맨마지막에 있는 KMM Application을 선택하자.

  2. 앱이름과 등등 이름을 입력하고 생성하자. 관련 라이브러리 받으라 더럽게 오래 걸린다.

https://github.com/sugoigroup/kotlin_mpp_example/commit/9ce2f2fd791270c86eeae6265837a9b49eb473f5

shared 의 build.gradle.kts를 보면

kotlin {
    android()
    ios { //ios가 sourceSet에서는 iosMain으로 지정
        binaries {
            framework {
                baseName = "shared" // ios프로젝트를 xcode에서 열어보면 shared란 framework 이름으로 추가되어있다. 
            }
        }
    }
    sourceSets {

        val iosMain by getting
    }

}
  1. Hello 공용모듈을 분석해보자

commonMain 아래에 있는것들이 말그대로 공용함수가 있는곳이다. 프로젝트 작성과 함께Greeting.kt, Platform.kt 가 있다.

kotlin 에서는

expect : 이 키워드를 붙인 클래스나 함수는 각 플랫폼에서 동일하게 사용될것 같은 (마치 인터페이스같은) 것이라는 의미이다.

actual : 앞서 expect로 외부에서 불러내면 , 각 플랫폼에따라서 상황에 맞는 동작을 해야되는데 actual 키워드를 클래스나 함수 앞에 붙여준다. 따라서 expect 가 붙어있는 것들은 각 플랫폼(iosMain, AndroidMain, jsMain등)에 aspect로 쌍을 이뤄서 써줘야한다.

그래서

--shared/commonMain/.../Greeting.kt 
//Greeting 클래스는 외부의 각플랫폼에서 불러낼수 있다. 단, 어떤 플랫폼에서 불러내든 동일한 처리와 결과같을 가지게 된다.
class Greeting {
    fun greeting(): String {
        return "Hello, ${Platform().platform}!" // Platform().platform 이라는 expect(실제의 구현체가 있을지 없을지 모르는 예상의) 키워드가 붙은 클래스를 불러냈다.
    }
}

--shared/commonMain/.../Platform.kt
// expect가 붙은 Platform 클래스는 platform(문자형변수)를 가지고있고, 외부에서 Greeing.greeting()을 불러실행할떄, Platform().platform 형식으로 값을 가져다가 출력한다. 이곳에는 단지 expect만 지정했기때문에 실제 구현체는 공용모듈안의 각 플랫폼에서 진행하자.
expect class Platform() {
    val platform: String
}

--shared/androidMain/Platform.kt
// exctual라인옆에 <E> 가 보인다. expect에 대응하는 안드로이드플랫폼용 actual을 구현하자. 
// actual이 조금 지저분하다. 여튼 안드로이드는 SDK_INT로 버젼을 확인하니 해당 코드를 넣어주었다
actual class Platform actual constructor() {
    actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}

--shared/iosMain/Platform.kt
// ios코드가 재밌다. UIDevice클래스(랩퍼겠지?)를 이용하여 버젼을 출력하는 코드를 넣었다..
actual class Platform actual constructor() {
    actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

이로써 공통 모듈은 준비가 되었다.

이제 각 플랫폼에서 공통모듈을 불러내서 Greeting함수만 실행하면 각 플랫폼의OS버젼이 출력된다.

안드로이드의 gradle.build 에 공용모듈의 의존성을 추가(implementation(project(":shared"))한다음소스에서 클래스와 함수 불러내면 된다.

--androidApp/.../MainActivity.kt
호출:Greeting().greeting();
결과:"Hello, Android 28!"

결국 shared모듈을 의존성에 추가하면, 공통 Greeting클래스라 expect된 Platform().platform을 호출하고 androidMain플랫폼에 actual하여 적어준대로 "Android ${android.os.Build.VERSION.SDK_INT}"를 출력하게 된다.

안드로이드와 비슷하게, ios프로젝트 내부에 공용모듈인 shared.framework을 추가되었고, swift언어로 클래스와 함수를 불러냈다.

--iosApp/.../ContentView.swift

호출:Greeting().greeting();
결과:"Hello, ios ....!"

마찬가지로, expect된 Platform().platform을 iosMain 에서 “UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion” 식으로 출력했다.

  1. 그럼 나도 추가해보자. 일단 멀티플랫폼에서 사용할만한게…환전?! 환전예상가격을 조회하는 어플을 만든다 할때, 미국달러를 나라별 환전금액으로 변환해주는 로직을 안드로이드와 아이폰 양쪽에서 만들필요없이 공통로직에서 처리하면 좋을것 같다.

https://github.com/sugoigroup/kotlin_mpp_example/commit/5c7956fa68dd2fb6dc93ad278d99804bb5eae352

--commonMain/.../ExchangeMoney.kt
외부에서 사용할 클래스와 함수를 expect 정의
expect class ExchangeMoney(baseDollor: Double) {
    fun toYen(): Double
    fun toWon(): Double
}

--commonMain/.../ExchangeMoneyCalcurator.kt
각 플랫폼에서 expect함수를 통해 actual 선언된 함수를 이용할때, 플랫폼에 상관없이 똑같이 처리할 로직을 넣음
class ExchangeMoneyCalcurator(val baseDollor: Double, val country: String) {
    fun calc(): Double {
        val rate: Double = when (country) {
            "YEN" -> 0.91
            "WON" -> 0.95
            else -> 1.0
        }
        return baseDollor * rate
    }

}

--androidMain/.../ExchangeMoney.kt
expect 함수들을 actual로 구현. 단 공통로직은 ExchangeMonetCalcurator를 호출하여 구현
actual class ExchangeMoney actual actual class ExchangeMoney actual constructor(val baseDollor: Double) {
    actual fun toYen(): Double {
        return ExchangeMoneyCalcurator(baseDollor, "YEN").calc()
    }
    actual fun toWon(): Double {
        return ExchangeMoneyCalcurator(baseDollor, "WON").calc()
    }
}


--iosMain/.../ExchangeMoney.kt
actual class ExchangeMoney actual constructor(val baseDollor: Double) {
    actual fun toYen(): Double {
       return ExchangeMoneyCalcurator(baseDollor, "YEN").calc()
    }
    actual fun toWon(): Double {
        return ExchangeMoneyCalcurator(baseDollor, "WON").calc()
    }
}

Android 에서 사용해보자.

--androidApp/.../MainActivity.kt

        tv.text = ("3 달라는 한국돈으로 " + ExchangeMoney(3.0).toWon())

iOS 에서 사용해보자.

--iodApp/.../ContentView.swift
        Text("3 달라는 엔화로" + String(ExchangeMoney(baseDollor: 3.0).toYen()))

이밖에 멀티플랫폼에서 사용할만한 라이브러리가 있다.

https://github.com/AAkira/Kotlin-Multiplatform-Libraries

GraghSQL, SQLDelight, Realm, Koin, LiveData, coroutine, uuid, firebase, moko-widget 등 디비와 통신, 로직에 사용할 만한 라이브러리 들이 있다

0 comments:

댓글 쓰기