https://kotlinlang.org logo
#compose-ios
Title
# compose-ios
j

Joel Denke

02/26/2024, 10:18 AM
Has anyone been able to use things like GoogleSignIn or such without using Cocoapods in Compose iOS? Like adding as SPM and use code in Kotlin for iosMain.
p

Pablichjenkov

02/26/2024, 2:59 PM
I have done something similar with Firebase SDK. Now, is not so simple as adding the swift-package and use code in kotlin. You have to create manually the expect/actual interfaces,
expects
in kotlin and implementation
(actuals)
, in swift using the library.
j

Joel Denke

02/26/2024, 3:01 PM
I have expect / actual using GoogleSignIn for iOS and Androidx credentials for Android target. The main issue is I get so many problems with the Kotlin Cocoapods I want it gone. Wonder if theres another alternative, like embed the entire library or some easy plugin not sure. I need to ass the SPM in XCode anyway, to correctly link the things in my app.
One potential solution I was thinking about, which I had in the past. Is to make interface send into my core DI component, where need to send in Swift implementation of GOogleSignIn on the iOS side. Meaning I will not use the GoogleSignIn at Kotlin level at all anymore. But then going against my sturcture, avoid haivng native things in any platform, causing fragmentation.
p

Pablichjenkov

02/26/2024, 3:22 PM
Yes that is what I was talking about. I think that, until the library ecosystem grows at a certain level or until direct Interoperability between swift and kotlin is achieved. This will be the simplest alternative. Notice the Apple ecosystem is trying to move away from cocoapods too. Meaning less and less SDKs and libraries will be available in pods.
Hopefully more companies, including Google start releasing full KMP libraries.
j

Joel Denke

02/26/2024, 3:30 PM
I would say its the worst alternative, to put the things native in iOS, causing a lot of architecture downsides. I dont understand why Jetbrains started with cocoapods in Kotlin at all, it was bad even back then. But yeah Google already has full KMP libs. But even then not working, as some of them using Cocoapods plugin causing issues 😄
Transtivie deps in Gradle vs SPM/COcoapods seems to be uhm complex to overlap.
Like androidx lifecycle and viewmodel is now KMP, including datastore and collections and paging 🙂
p

Pablichjenkov

02/26/2024, 3:37 PM
Yep. Although I would say, you can still keep most of your code in kotlin side. In other words, you really don't need to implement anything on the swift side, other than a thin wrapper on top of the original API. You will still have your implementation in kotlin as if you imported the library using cocoapods.
j

Joel Denke

02/26/2024, 3:40 PM
Yeah probably worth it for now, but also want more stuff in Kotlin communicating with old school iOS libs 😛 Do you have any recommended pattern to make this interface as thin as possible can and get proper Kotlin autocomplete in XCode when write the Swift layer? 😄
p

Pablichjenkov

02/26/2024, 3:56 PM
In general the pattern I follow is, • creating an interface with the same API as the swift library. • Create another interface in kotlin, this is the abstraction the commonMain code depends on. • Then have a class in kotlin that glue this 2. Basically an adapter class, it implements the commonMain abstraction and receives as parameter the implementation of the swift library in step 1. You can take a look at this 2 classes for example: https://github.com/pablichjenkov/macao-marketplace/tree/dev/auth-firebase%2Fsrc%2FiosMain%2Fkotlin%2Fcom%2Fmacaosoftware%2Fplugin%2Faccount
interface FirebaseAuthKmpWrapper is the one in step 1. It is implemented in a separate project which is an SPM project, here: https://github.com/pablichjenkov/firebase-kmp/tree/main/FirebaseAuthKmp%2FSources
But you can just implement it in your same project for simplicity.
j

Joel Denke

02/26/2024, 3:59 PM
Cool, yeah I have something similar but more thin. The main complexity for me I need to call from @Composable lambda, to access COntext in Android properly 😛
p

Pablichjenkov

02/26/2024, 4:00 PM
Then
class FirebaseAccountPlugin
is what I mean in step 3 above.
j

Joel Denke

02/26/2024, 4:01 PM
In general I need to do something like this:
Copy code
class IosGoogleLoginLauncher2(
    private val loginRepository: LoginRepository,
    private val googleCredentialsProvider: GoogleCredentialsProvider
): GoogleLoginLauncher {

    @Composable
    override fun uiResultLauncher(
        result: (LoginResult) -> Unit,
    ): UiResultLauncher {
        val coroutineScope = rememberCoroutineScope()
        return UiResultLauncher {
            val credentials = googleCredentialsProvider.credentials()
            coroutineScope.launch {
                result(loginRepository.authorize(credentials))
            }
        }
    }
}
Copy code
interface GoogleCredentialsProvider {
    fun credentials(): Credentials
}
This is the interface I am now write only in Swift layer 🙂
p

Pablichjenkov

02/26/2024, 4:02 PM
I see, Humm that sounds tricky, anything that uses Composable. I try to reduce these wrappers to basic swift types that map easily to kotlin type. I try to avoid
suspend
use in the interface exported to swift too, to avoid pitfalls. Better do all that in kotlin land
You are good, just create the mappings to the Credentials model in swift
j

Joel Denke

02/26/2024, 4:04 PM
Yeah the main problem here is androidx credential manager requires you to send in Activity Context on the fly, enforce me to use @Composable for both Android and iOS, but would like to aovid it.
I am then decouple the @Composable thing within hidden layer so iOS code not need to implement it, only need to implment GoogleCredentialsProvider
p

Pablichjenkov

02/26/2024, 4:05 PM
Ah I see 🙈, I mean creating these wrappers is a nightmare sometimes. By the time these SDKs were created the least concern they had, was using them in a common code friendly to other platforms
j

Joel Denke

02/26/2024, 4:06 PM
Yeah I solved this easier when using cocoapods, hence I dont want to go back this road again 😄 As I know hor horrible this is.
p

Pablichjenkov

02/26/2024, 4:06 PM
But yes you can abstract context away I guess
j

Joel Denke

02/26/2024, 4:07 PM
I wish androidx credentialmanager allowed you to use Application Contex,t then would not be a problem 😛
Then I can inject context from DI layer instead.
l

louiscad

02/26/2024, 4:07 PM
🎉 1
Google needs to support that out of the box. Please vote and share 🙂
j

Joel Denke

02/26/2024, 4:10 PM
@louiscad Nice is this on Googles radar or what kind of ticket is this? Looks like a random forum post to me? https://firebase.uservoice.com/forums/948424-general/suggestions/46591717-support-kotlin-multiplatform My main thing I want to have is google play service plugin KMP agnostic, how to fetch the service account data basically 😛 The rest is basic OAuth really, and some additional extra layers from GOogle to make things both easier and hrader. Also wish list is they delete usage of Guava and start using pure Coroutines for their API:s for everything.
l

louiscad

02/26/2024, 4:10 PM
This is Firebase's forum. Got that link from a Firebase Product Manager
j

Joel Denke

02/26/2024, 4:11 PM
Right, and they claimed they will be support if enough upvoting this or?
l

louiscad

02/26/2024, 4:11 PM
I said "probably"
If you don't want to vote, don't vote 😛
Personally, I did, because I want to weigh in their priorities
👍 1
j

Joel Denke

02/26/2024, 4:12 PM
I want to have this ofc, just wonder whats the most efficent way of solving this. If I recall correct this https://github.com/GitLiveApp/firebase-kotlin-sdk was already a way of Google Firebase devs doing some extra effort?
That library is solve many things, but not the complex parts 😛
@Pablichjenkov Btw this is how I do it today in Kotlin if helps anyone:
Copy code
class IosGoogleLoginLauncher(
    private val loginRepository: LoginRepository,
): GoogleLoginLauncher {

    @Composable
    override fun uiResultLauncher(
        result: (LoginResult) -> Unit,
    ): UiResultLauncher {
        val uiController = LocalUIViewController.current
        val coroutineScope = rememberCoroutineScope()
        return UiResultLauncher {
            val clientID = Firebase.app.ios.options.clientID.orEmpty()
            val config = GIDConfiguration(clientID = clientID)
            GIDSignIn.sharedInstance.configuration = config
            GIDSignIn.sharedInstance.signInWithPresentingViewController(uiController) { result, error ->
                val idToken = result?.user?.idToken?.tokenString
                val accessToken = result?.user?.refreshToken?.tokenString
                if (idToken != null) {
                    coroutineScope.launch {
                        result(loginRepository.authorize(
                            Credentials(
                            idToken = idToken,
                            accessToken = accessToken
                        )
                        ))
                    }
                } else {
                    result(LoginResult.Failed(IllegalArgumentException("No id token found, required to login")))
                }
            }
        }
    }
}
p

Pablichjenkov

02/26/2024, 8:11 PM
I see, you mean using cocoapods right? - bc I see you are accessing the libraries directly from kotlin. I mean in general cocoapods is not bad, is just that the iOS community is moving towards SPM
The transition is still going on and believe it or not most big projects out there still depend on cocoapods but when you talk to some iOS dev they will tell the future is definitely SPM
j

Joel Denke

02/27/2024, 5:55 AM
Yeah sharing cocoapods version if someone else curious. Hopefully getting rid of cocoapods soon or some kind of improvement. I like that you can write Kotlin code and interop to things , I prefer that all day long before Swift interop to Kotlin reversed. It increasing code sharing a lot.
e

Eury Perez

03/11/2024, 9:53 AM
I did it and it was working until it stopped working because of XCode linking issues (that’s been a nightmare). Check this to see the repo url and the issue in case anyone can help: https://kotlinlang.slack.com/archives/C0346LWVBJ4/p1710091126616709
5 Views