[KMP] viewmodel 사용하기

WonDDak·2024년 5월 20일
0

KMP- Kotlin MultiPlatform

목록 보기
7/12
post-thumbnail

androidx.lifecycle:lifecycle-viewmodel 2.8.0 버전이 정상적으로 출시되었습니다.

2.8.0 부터 정식적으로 Kotlin MutliPlatform을 지원합니다.

적용

간단하게 사용해봅시다.!
신규 프로젝트를 하나 만들어 주고 종속성을 추가해줍니다.

commonMain.dependencies {
	implementation(libs.androidx.lifecycle.viewmodel)
}
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version = "2.8.0" }

Shared

CommonMain에 다음과 같이 작성하였습니다.

class MainViewModel : ViewModel() {

    private var _showSplash = MutableStateFlow(true)

    val showSplash: StateFlow<Boolean>
        get() = _showSplash

    fun hideSplash(withDelay: Long) {
        viewModelScope.launch {
            delay(withDelay)
            _showSplash.value = false
            print("스플래쉬 종료")
        }
    }
}

Android

4초뒤에 허접한 스플래쉬가 사라지도록 해보겠습니다..

class MainActivity : ComponentActivity() {
    private val mainViewModel : MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    val showSplash by mainViewModel.showSplash.collectAsState()
                    Box {
                        GreetingView(Greeting().greet())
                        if (showSplash) {
                            LaunchedEffect(true) {
                                mainViewModel.hideSplash(4000L)
                            }
                            Column(
                                modifier = Modifier
                                    .fillMaxSize()
                                    .background(androidx.compose.ui.graphics.Color.White),
                                verticalArrangement = Arrangement.Center
                            ) {
                                Text(text = "허접한 스플래쉬")
                            }
                        }
                    }
                }
            }
        }
    }
}

aos
잘 작동하는군요

IOS

이번에는 IOS에서 해보겠습니다.

struct ContentView: View {
    let greet = Greeting().greet()
    let mainViewModel : MainViewModel = MainViewModel()
    
    @State private var showSplash :Bool = true
    var body: some View {
        ZStack {
            Text(greet)
            if(showSplash) {
                VStack{
                    Text("허접한 스플래쉬")
                        .frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .center)
                }
                .frame(maxWidth: .infinity,maxHeight: .infinity)
                .background(.white)
                .onAppear{
                    mainViewModel.hideSplash(withDelay: Int64(4000))
                }
            }
        }
        .onAppear{
            mainViewModel.showSplash.collect(
                collector: Collector<Bool> { value in
                    showSplash = value
                }
            ) { error in
                
            }
        }
    }
}

class Collector<T> : Kotlinx_coroutines_coreFlowCollector {
    let callback:(T) -> Void

    init(callback: @escaping (T) -> Void) {
        self.callback = callback
    }
    
    func emit(value: Any?, completionHandler: @escaping (Error?) -> Void) {
        callback(value as! T)
        completionHandler(nil)
    }
}

ios
잘 작동하는 군요

다만 StateFlow를 바로 못쓰니 개선이좀 필요해 보입니다.. 매번 모든뷰에 저렇게 작업 해 주기는 힘들자나요~

개선

좀 개선해봅시다.

Shared

기존에 작성된 MainViewModel을 BaseViewModel로 바꾸고 open으로 바꾸어 상속 받을수 있게 해봅시다.

open class BaseViewModel : ViewModel() {
...
}

Android

아래와 같이 수정하면 동일한 코드가 됩니다.

class MainViewModel : BaseViewModel()

Ios

아래와 같이 수정했습니다.

class MainViewModel : ObservableObject {
    private let base : BaseViewModel = BaseViewModel()
    
    @Published var showSplash :Bool = true
    
    init() {
        base.showSplash.collect(
            collector: Collector<Bool> { value in
                self.showSplash = value
            }
        ) { error in
        }
    }
    
    func hideSplash(withDelay: Int64) {
        base.hideSplash(withDelay: withDelay)
    }
}

struct ContentView: View {
    let greet = Greeting().greet()
    @StateObject var mainViewModel : MainViewModel = MainViewModel()
    
    var body: some View {
        ZStack {
            Text(greet)
            if(mainViewModel.showSplash) {
                VStack{
                    Text("허접한 스플래쉬")
                        .frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .center)
                }
                .frame(maxWidth: .infinity,maxHeight: .infinity)
                .background(.white)
                .onAppear{
                    mainViewModel.hideSplash(withDelay: 4000)
                }
            }
        }
    }
}

똑같이 작동하지만 viewmodel이 완전히 분리되었습니다.

profile
안녕하세요. 원딱입니다.

0개의 댓글