Skip to content

Commit

Permalink
Add SignInWithApple example
Browse files Browse the repository at this point in the history
  • Loading branch information
grdsdev committed Dec 16, 2023
1 parent 466c397 commit e7680db
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 31 deletions.
8 changes: 6 additions & 2 deletions Examples/Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
793895D22954AC000044F2B8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 793895D12954AC000044F2B8 /* Preview Assets.xcassets */; };
793E03092B2CED5D00AC7DED /* Contants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E03082B2CED5D00AC7DED /* Contants.swift */; };
793E030B2B2CEDDA00AC7DED /* ActionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E030A2B2CEDDA00AC7DED /* ActionState.swift */; };
793E030D2B2DAB5700AC7DED /* SignInWithApple.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E030C2B2DAB5700AC7DED /* SignInWithApple.swift */; };
794EF1222955F26A008C9526 /* AddTodoListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794EF1212955F26A008C9526 /* AddTodoListView.swift */; };
794EF1242955F3DE008C9526 /* TodoListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794EF1232955F3DE008C9526 /* TodoListRow.swift */; };
7956405E2954ADE00088A06F /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7956405D2954ADE00088A06F /* Secrets.swift */; };
Expand Down Expand Up @@ -63,6 +64,7 @@
793895D12954AC000044F2B8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
793E03082B2CED5D00AC7DED /* Contants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contants.swift; sourceTree = "<group>"; };
793E030A2B2CEDDA00AC7DED /* ActionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionState.swift; sourceTree = "<group>"; };
793E030C2B2DAB5700AC7DED /* SignInWithApple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithApple.swift; sourceTree = "<group>"; };
794EF1212955F26A008C9526 /* AddTodoListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTodoListView.swift; sourceTree = "<group>"; };
794EF1232955F3DE008C9526 /* TodoListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoListRow.swift; sourceTree = "<group>"; };
7956405D2954ADE00088A06F /* Secrets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Secrets.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -211,6 +213,7 @@
79AF04802B2CE261008761AD /* AuthView.swift */,
79AF047E2B2CE207008761AD /* AuthWithEmailAndPassword.swift */,
79AF04832B2CE408008761AD /* AuthWithMagicLink.swift */,
793E030C2B2DAB5700AC7DED /* SignInWithApple.swift */,
);
path = Auth;
sourceTree = "<group>";
Expand Down Expand Up @@ -414,6 +417,7 @@
79AF047F2B2CE207008761AD /* AuthWithEmailAndPassword.swift in Sources */,
795640622955AD2B0088A06F /* HomeView.swift in Sources */,
793895CA2954ABFF0044F2B8 /* ExamplesApp.swift in Sources */,
793E030D2B2DAB5700AC7DED /* SignInWithApple.swift in Sources */,
793E030B2B2CEDDA00AC7DED /* ActionState.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -656,7 +660,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Examples/Info.plist;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.supabase.Examples;
PRODUCT_BUNDLE_IDENTIFIER = "com.supabase.swift-examples";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
};
Expand All @@ -676,7 +680,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Examples/Info.plist;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.supabase.Examples;
PRODUCT_BUNDLE_IDENTIFIER = "com.supabase.swift-examples";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
};
Expand Down
11 changes: 0 additions & 11 deletions Examples/Examples/ActionState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,3 @@ enum ActionState<Success, Failure: Error> {
case inFlight
case result(Result<Success, Failure>)
}

extension Result where Failure == Error {
init(catching operation: () async throws -> Success) async {
do {
let value = try await operation()
self = .success(value)
} catch {
self = .failure(error)
}
}
}
3 changes: 3 additions & 0 deletions Examples/Examples/Auth/AuthView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ struct AuthView: View {
enum Option: CaseIterable {
case emailAndPassword
case magicLink
case signInWithApple

var title: String {
switch self {
case .emailAndPassword: "Auth with Email & Password"
case .magicLink: "Auth with Magic Link"
case .signInWithApple: "Sign in with Apple"
}
}
}
Expand All @@ -39,6 +41,7 @@ extension AuthView.Option: View {
switch self {
case .emailAndPassword: AuthWithEmailAndPassword()
case .magicLink: AuthWithMagicLink()
case .signInWithApple: SignInWithApple()
}
}
}
Expand Down
26 changes: 16 additions & 10 deletions Examples/Examples/Auth/AuthWithEmailAndPassword.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ struct AuthWithEmailAndPassword: View {
}

enum Result {
case failure(Error)
case needsEmailConfirmation
}

Expand All @@ -22,7 +21,7 @@ struct AuthWithEmailAndPassword: View {
@State var email = ""
@State var password = ""
@State var mode: Mode = .signIn
@State var result: Result?
@State var actionState = ActionState<Result, Error>.idle

var body: some View {
Form {
Expand All @@ -41,13 +40,20 @@ struct AuthWithEmailAndPassword: View {
await primaryActionButtonTapped()
}
}
}

if case let .failure(error) = result {
ErrorText(error)
}
if case let .result(.failure(error)) = actionState {
ErrorText(error)
}

if mode == .signUp, case .needsEmailConfirmation = result {
switch actionState {
case .idle:
EmptyView()
case .inFlight:
ProgressView()
case let .result(.failure(error)):
ErrorText(error)
case .result(.success(.needsEmailConfirmation)):
Section {
Text("Check you inbox.")

Expand All @@ -65,7 +71,7 @@ struct AuthWithEmailAndPassword: View {
) {
withAnimation {
mode = mode == .signIn ? .signUp : .signIn
result = nil
actionState = .idle
}
}
}
Expand All @@ -74,7 +80,7 @@ struct AuthWithEmailAndPassword: View {

func primaryActionButtonTapped() async {
do {
result = nil
actionState = .inFlight
switch mode {
case .signIn:
try await supabase.auth.signIn(email: email, password: password)
Expand All @@ -86,12 +92,12 @@ struct AuthWithEmailAndPassword: View {
)

if case .user = response {
result = .needsEmailConfirmation
actionState = .result(.success(.needsEmailConfirmation))
}
}
} catch {
withAnimation {
result = .failure(error)
actionState = .result(.failure(error))
}
}
}
Expand Down
72 changes: 72 additions & 0 deletions Examples/Examples/Auth/SignInWithApple.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// SignInWithApple.swift
// Examples
//
// Created by Guilherme Souza on 16/12/23.
//

import AuthenticationServices
import SwiftUI

struct SignInWithApple: View {
@State private var actionState = ActionState<Void, Error>.idle

var body: some View {
VStack {
SignInWithAppleButton { request in
request.requestedScopes = [.email]
} onCompletion: { result in
switch result {
case let .failure(error):
debug("signInWithApple failed: \(error)")

case let .success(authorization):
guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential
else {
debug(
"Invalid credential, expected \(ASAuthorizationAppleIDCredential.self) but got a \(type(of: authorization.credential))"
)
return
}

guard let identityToken = credential.identityToken.flatMap({ String(
data: $0,
encoding: .utf8
) }) else {
debug("Invalid identity token")
return
}

Task {
await signInWithApple(using: identityToken)
}
}
}
.fixedSize()

switch actionState {
case .idle, .result(.success):
EmptyView()
case .inFlight:
ProgressView()
case let .result(.failure(error)):
ErrorText(error)
}
}
}

private func signInWithApple(using idToken: String) async {
actionState = .inFlight
let result = await Result {
_ = try await supabase.auth.signInWithIdToken(credentials: .init(
provider: .apple,
idToken: idToken
))
}
actionState = .result(result)
}
}

#Preview {
SignInWithApple()
}
2 changes: 1 addition & 1 deletion Examples/Examples/Contants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
import Foundation

enum Constants {
static let redirectToURL = URL(string: "com.supabase.Examples://")!
static let redirectToURL = URL(string: "com.supabase.swift-examples://")!
}
12 changes: 8 additions & 4 deletions Examples/Examples/Examples.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Examples/Examples/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.supabase.Examples</string>
<string>com.supabase.swift-examples</string>
</array>
</dict>
</array>
Expand Down
4 changes: 2 additions & 2 deletions Sources/Auth/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public enum Provider: String, Codable, CaseIterable, Sendable {
public struct OpenIDConnectCredentials: Codable, Hashable, Sendable {
/// Provider name or OIDC `iss` value identifying which provider should be used to verify the
/// provided token. Supported names: `google`, `apple`, `azure`, `facebook`.
public var provider: Provider?
public var provider: Provider

/// OIDC ID token issued by the specified provider. The `iss` claim in the ID token must match the
/// supplied provider. Some ID tokens contain an `at_hash` which require that you provide an
Expand All @@ -258,7 +258,7 @@ public struct OpenIDConnectCredentials: Codable, Hashable, Sendable {
public var gotrueMetaSecurity: AuthMetaSecurity?

public init(
provider: Provider? = nil,
provider: Provider,
idToken: String,
accessToken: String? = nil,
nonce: String? = nil,
Expand Down

0 comments on commit e7680db

Please sign in to comment.