Skip to content

iOS action handling not always working with Firebase calls #267

Open
@kostapostolakis

Description

@kostapostolakis

I have the NewGameViewModel in commonMain Shared code of KMM.

interface NewGameIntents {
    fun updateUserName(userName: String)
    fun startNewGameButtonTapped()
    fun newGameCreated(newGameID: String)
    fun newGameCreationFailed()
}

sealed interface NewGameActions {
    data class CreateNewGameAction(val userName: String): NewGameActions
    data class GoToMainScreenAction(val gameID: String, val userName: String) : NewGameActions
}

class NewGameViewModel: BaseViewModel(), NewGameIntents {
    private val _actions = Channel<NewGameActions>()
    val actions: CFlow<NewGameActions> get() = _actions.receiveAsFlow().cFlow()

    private val _userName: CMutableStateFlow<String> = MutableStateFlow("").cMutableStateFlow()
    val userName: CStateFlow<String> = _userName.cStateFlow()

    init {
        val previousName = SettingsHelper().lastGameName ?: SettingsHelper().userName
        previousName?.let {
            updateUserName(it)
        }
    }

    override fun updateUserName(userName: String) {
        _userName.value = userName
    }

    override fun startNewGameButtonTapped() {
        updateUserName(_userName.value.trim())
        createNewGame()
    }

    override fun newGameCreated(newGameID: String) {
        showLoading(false)
        viewModelScope.launch {
            _actions.send(NewGameActions.GoToMainScreenAction(newGameID, _userName.value))
        }
    }

    override fun newGameCreationFailed() {
        showLoading(false)
        showErrorAlert()
    }

    private fun createNewGame() {
        showLoading(true)
        viewModelScope.launch {
            _actions.send(NewGameActions.CreateNewGameAction(_userName.value))
        }
    }
}

I have also the NewGameScreen in iOS:

struct NewGameScreen: View {
    
    @ObservedObject var newGameViewModel: NewGameViewModel = NewGameViewModel()
    
    var body: some View {        
        ScrollView {
            VStack(spacing: 0) {
                CustomTextField(
                    value: newGameViewModel.state(\.userName),
                    label: getKmmString(stringResource: \.your_name),
                    onTextChange: { text in
                        newGameViewModel.updateUserName(userName: text)
                    }
                )
                
                Spacer().frame(height: 32)
                CustomButton(
                    text: getKmmString(stringResource: \.start_game),
                    onClick: {
                        newGameViewModel.startNewGameButtonTapped()
                    }
                )
            }
            .padding(16)
        }
        .navigationBarBackButtonHidden(true)
        .onReceive(createPublisher(newGameViewModel.actions)) { action in
            handleNewGameAction(action: action)
        }
    }
    
    private func handleNewGameAction(action: NewGameActions) {
        switch action {
        case let action as NewGameActionsCreateNewGameAction: do {
            let newGameMap = getMapFromFunction(name: action.userName) // Create map here
            
            var documentReference: DocumentReference? = nil
            documentReference = Firestore.firestore()
                .collection(Game.companion.COLLECTION_GAMES)
                .addDocument(data: newGameMap) { error in
                    if let error {
                        newGameViewModel.doNewGameCreationFailed()
                    } else if let documentId = documentReference?.documentID, !documentId.isEmpty {
                        newGameViewModel.doNewGameCreated(newGameID: documentId)
                    } else {
                        newGameViewModel.doNewGameCreationFailed()
                    }
                }
        }
            
        case let action as NewGameActionsGoToMainScreenAction: do {
            closeScreen()
            PalermoHelper().goToMainGameScreen(gameID: action.gameID, name: action.userName)
        }
            
        default: return
        }
    }
}

When I tap the button, startNewGameButtonTapped() function of viewModel is called and then the action CreateNewGameAction should be handled by iOS.

Sometimes it works and sometimes not. Maybe it is a lifecycle issue, but I am not sure and I do not know how to solve it. Do you have any ideas?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions