Skip to content

Commit 7e68cae

Browse files
author
Guilherme Souza
authored
chore: Add example for native Sign in with Apple (#62)
* Adds signInWithApple method * Mark signInWithApple as Experimental * Add method for SIWA as a 2 steps process * Remove signInWithApple method and add example * Remove import not needed * Fix preview * Import GoTrue as Experimental
1 parent 5ddaad6 commit 7e68cae

File tree

6 files changed

+101
-1
lines changed

6 files changed

+101
-1
lines changed

Examples/Examples.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
798AD52129072C22001B4801 /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798AD52029072C22001B4801 /* AppView.swift */; };
1515
798AD5232907309C001B4801 /* SessionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798AD5222907309C001B4801 /* SessionView.swift */; };
1616
798AD525290731A0001B4801 /* AuthWithEmailAndPasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798AD524290731A0001B4801 /* AuthWithEmailAndPasswordView.swift */; };
17+
79C17A802A589EC000C67A61 /* SignInWithAppleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C17A7F2A589EC000C67A61 /* SignInWithAppleView.swift */; };
1718
/* End PBXBuildFile section */
1819

1920
/* Begin PBXFileReference section */
@@ -26,6 +27,7 @@
2627
798AD52029072C22001B4801 /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = "<group>"; };
2728
798AD5222907309C001B4801 /* SessionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionView.swift; sourceTree = "<group>"; };
2829
798AD524290731A0001B4801 /* AuthWithEmailAndPasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthWithEmailAndPasswordView.swift; sourceTree = "<group>"; };
30+
79C17A7F2A589EC000C67A61 /* SignInWithAppleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithAppleView.swift; sourceTree = "<group>"; };
2931
/* End PBXFileReference section */
3032

3133
/* Begin PBXFrameworksBuildPhase section */
@@ -83,6 +85,7 @@
8385
18DE589E286B2C77005F7FF2 /* ExamplesApp.swift */,
8486
1820720D286B449B0009D0BF /* Secrets.swift */,
8587
798AD5222907309C001B4801 /* SessionView.swift */,
88+
79C17A7F2A589EC000C67A61 /* SignInWithAppleView.swift */,
8689
);
8790
path = Sources;
8891
sourceTree = "<group>";
@@ -164,6 +167,7 @@
164167
798AD52129072C22001B4801 /* AppView.swift in Sources */,
165168
798AD525290731A0001B4801 /* AuthWithEmailAndPasswordView.swift in Sources */,
166169
798AD5232907309C001B4801 /* SessionView.swift in Sources */,
170+
79C17A802A589EC000C67A61 /* SignInWithAppleView.swift in Sources */,
167171
);
168172
runOnlyForDeploymentPostprocessing = 0;
169173
};

Examples/Shared/Examples.entitlements

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5+
<key>com.apple.developer.applesignin</key>
6+
<array>
7+
<string>Default</string>
8+
</array>
59
<key>com.apple.security.app-sandbox</key>
610
<true/>
711
<key>com.apple.security.network.client</key>

Examples/Shared/Sources/AppView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ struct AppView: View {
1616
NavigationLink("Auth with Email and Password") {
1717
AuthWithEmailAndPasswordView()
1818
}
19+
20+
NavigationLink("Sign in with Apple") {
21+
SignInWithAppleView()
22+
}
1923
}
2024
.listStyle(.plain)
2125
.navigationTitle("Examples")
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// SignInWithAppleView.swift
3+
// Examples
4+
//
5+
// Created by Guilherme Souza on 07/07/23.
6+
//
7+
8+
import AuthenticationServices
9+
import CryptoKit
10+
import SwiftUI
11+
@_spi(Experimental) import GoTrue
12+
13+
struct SignInWithAppleView: View {
14+
@Environment(\.goTrueClient) private var client
15+
@State var nonce: String?
16+
17+
var body: some View {
18+
SignInWithAppleButton { request in
19+
// self.nonce = sha256(randomString())
20+
// request.nonce = nonce
21+
request.requestedScopes = [.email, .fullName]
22+
} onCompletion: { result in
23+
Task {
24+
do {
25+
guard let credential = try result.get().credential as? ASAuthorizationAppleIDCredential
26+
else {
27+
return
28+
}
29+
30+
guard let idToken = credential.identityToken
31+
.flatMap({ String(data: $0, encoding: .utf8) })
32+
else {
33+
return
34+
}
35+
36+
try await client.signInWithIdToken(
37+
credentials: .init(
38+
provider: .apple,
39+
idToken: idToken/*,
40+
nonce: self.nonce*/
41+
)
42+
)
43+
} catch {
44+
dump(error)
45+
}
46+
}
47+
}
48+
.fixedSize()
49+
}
50+
}
51+
52+
func randomString(length: Int = 32) -> String {
53+
precondition(length > 0)
54+
var randomBytes = [UInt8](repeating: 0, count: length)
55+
let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)
56+
if errorCode != errSecSuccess {
57+
fatalError(
58+
"Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
59+
)
60+
}
61+
62+
let charset: [Character] =
63+
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
64+
65+
let nonce = randomBytes.map { byte in
66+
// Pick a random character from the set, wrapping around if needed.
67+
charset[Int(byte) % charset.count]
68+
}
69+
70+
return String(nonce)
71+
}
72+
73+
func sha256(_ input: String) -> String {
74+
let inputData = Data(input.utf8)
75+
let hashedData = SHA256.hash(data: inputData)
76+
let hashString = hashedData.compactMap {
77+
String(format: "%02x", $0)
78+
}.joined()
79+
80+
return hashString
81+
}
82+
83+
struct SignInWithApple_PreviewProvider: PreviewProvider {
84+
static var previews: some View {
85+
SignInWithAppleView()
86+
}
87+
}

Sources/GoTrue/GoTrueClient.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ public final class GoTrueClient {
167167

168168
/// Allows signing in with an ID token issued by certain supported providers.
169169
/// The ID token is verified for validity and a new session is established.
170+
@_spi(Experimental)
170171
@discardableResult
171172
public func signInWithIdToken(credentials: OpenIDConnectCredentials) async throws -> Session {
172173
try await _signIn(

Tests/GoTrueTests/GoTrueTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Mocker
22
import XCTest
33

4-
@testable import GoTrue
4+
@testable @_spi(Experimental) import GoTrue
55

66
final class InMemoryLocalStorage: GoTrueLocalStorage, @unchecked Sendable {
77
private let queue = DispatchQueue(label: "InMemoryLocalStorage")

0 commit comments

Comments
 (0)