Skip to content

Instantly share code, notes, and snippets.

@yannickpulver
Created May 8, 2024 09:03
Show Gist options
  • Save yannickpulver/f123ea210eef83757a0bc1bac9e83a28 to your computer and use it in GitHub Desktop.
Save yannickpulver/f123ea210eef83757a0bc1bac9e83a28 to your computer and use it in GitHub Desktop.
Supabase "Native Apple Sign In" Workaround for Compose Multiplatform
@main
struct iOSApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
init() {
// [...]
SupabaseHelper.shared.setSignInWithApple(factory: { () -> UIViewController in
let swiftUIView = SignInView()
return UIHostingController(rootView: swiftUIView)
})
}
}
@OptIn(ExperimentalForeignApi::class)
@Composable
actual fun SignInOptions(eventSink: (AuthScreenEvent) -> Unit) {
UIKitViewController(
factory = SupabaseHelper.signInWithApple,
modifier = Modifier.fillMaxWidth().height(40.dp)
)
}
@Suppress("unused") // Called from Swift
object SupabaseHelper : KoinComponent {
var signInWithApple: () -> UIViewController = { throw NotImplementedError() }
fun setSignInWithApple(factory: () -> UIViewController) {
signInWithApple = factory
}
fun importSession(
accessToken: String,
refreshToken: String,
providerRefreshToken: String? = null,
providerToken: String? = null,
expiresIn: Long,
tokenType: String,
type: String = "",
aud: String,
id: String
) {
val supabase = getKoin().get<SupabaseClient>()
val session = UserSession(
accessToken = accessToken,
refreshToken = refreshToken,
providerRefreshToken = providerRefreshToken,
providerToken = providerToken,
expiresIn = expiresIn,
tokenType = tokenType,
user = UserInfo(aud = aud, id = id),
type = type,
)
CoroutineScope(Dispatchers.IO).launch {
supabase.auth.importSession(session = session)
}
}
}
@Composable
expect fun SignInOptions(eventSink: (AuthScreenEvent) -> Unit)
import Foundation
import SwiftUI
import Supabase
import AuthenticationServices
import ComposeApp
struct SignInView: View {
let client = SupabaseClient(supabaseURL: URL(string: "url")!, supabaseKey: "key")
var body: some View {
SignInWithAppleButton { request in
request.requestedScopes = [.email, .fullName]
} onCompletion: { result in
Task {
do {
guard let credential = try result.get().credential as? ASAuthorizationAppleIDCredential
else {
return
}
guard let idToken = credential.identityToken
.flatMap({ String(data: $0, encoding: .utf8) })
else {
return
}
let session = try await client.auth.signInWithIdToken(
credentials: .init(
provider: .apple,
idToken: idToken
)
)
Dependencies.shared.importSession(accessToken: session.accessToken,
refreshToken: session.refreshToken,
providerRefreshToken: session.providerRefreshToken,
providerToken: session.providerToken,
expiresIn: Int64(session.expiresIn),
tokenType: session.tokenType,
type: session.tokenType,
aud: session.user.aud,
id: session.user.id.uuidString
)
dump(session)
} catch {
dump(error)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment