Skip to content

Commit 744fce0

Browse files
committed
XcodesLoginkit with Example
1 parent ed6c768 commit 744fce0

30 files changed

+2666
-3
lines changed

LoginKitExample/LoginKitExample.xcodeproj/project.pbxproj

Lines changed: 591 additions & 0 deletions
Large diffs are not rendered by default.

LoginKitExample/LoginKitExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LoginKitExample/LoginKitExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//
2+
// AppState.swift
3+
// LoginKitExample
4+
//
5+
// Created by Matt Kiazyk on 2025-01-31.
6+
//
7+
8+
import Foundation
9+
import XcodesLoginKit
10+
import SwiftUI
11+
12+
@Observable
13+
class AppState {
14+
var authError: Error?
15+
var isProcessingAuthRequest = false
16+
var presentedSheet: XcodesSheet? = nil
17+
18+
@MainActor
19+
private let client = Client()
20+
21+
func signIn(username: String, password: String) {
22+
isProcessingAuthRequest = true
23+
24+
Task {
25+
do {
26+
let autheticationState = try await client.srpLogin(accountName: username, password: password)
27+
handleAuthenticationFlowCompletion(autheticationState)
28+
isProcessingAuthRequest = false
29+
}
30+
catch {
31+
print("ERROR LOGGING IN: \(error)")
32+
authError = error
33+
isProcessingAuthRequest = false
34+
}
35+
}
36+
}
37+
38+
private func handleAuthenticationFlowCompletion(_ authenticationState: AuthenticationState) {
39+
switch authenticationState {
40+
case .unauthenticated:
41+
authError = AuthenticationError.notAuthorized
42+
case let .waitingForSecondFactor(twoFactorOption, authOptionsResponse, appleSessionData):
43+
self.presentedSheet = .twoFactor(.init(
44+
option: twoFactorOption,
45+
authOptions: authOptionsResponse,
46+
sessionData: AppleSessionData(serviceKey: appleSessionData.serviceKey, sessionID: appleSessionData.sessionID, scnt: appleSessionData.scnt)
47+
))
48+
case .authenticated(let appleSession):
49+
print("SUCCESSFULLY LOGGED IN - WELCOME: \(appleSession.user.fullName)")
50+
self.presentedSheet = nil
51+
break
52+
case .notAppleDeveloper:
53+
authError = AuthenticationError.notDeveloperAppleId
54+
}
55+
}
56+
57+
58+
@MainActor
59+
func submitSecurityCode(_ code: SecurityCode, sessionData: AppleSessionData) {
60+
isProcessingAuthRequest = true
61+
62+
Task {
63+
do {
64+
let authenticationState = try await client.submitSecurityCode(code, sessionData: sessionData)
65+
self.handleAuthenticationFlowCompletion(authenticationState)
66+
self.isProcessingAuthRequest = false
67+
} catch {
68+
print("ERROR SUBMITTING SECURITYCODE: \(error)")
69+
authError = error
70+
self.isProcessingAuthRequest = false
71+
}
72+
73+
}
74+
}
75+
76+
func choosePhoneNumberForSMS(authOptions: AuthOptionsResponse, sessionData: AppleSessionData) {
77+
self.presentedSheet = .twoFactor(.init(
78+
option: .smsPendingChoice,
79+
authOptions: authOptions,
80+
sessionData: sessionData
81+
))
82+
}
83+
84+
func requestSMS(to trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber, authOptions: AuthOptionsResponse, sessionData: AppleSessionData) {
85+
isProcessingAuthRequest = true
86+
Task {
87+
do {
88+
let authenticationState = try await client.requestSMSSecurityCode(to: trustedPhoneNumber, authOptions: authOptions, sessionData: sessionData)
89+
self.handleAuthenticationFlowCompletion(authenticationState)
90+
self.isProcessingAuthRequest = false
91+
92+
} catch {
93+
print("ERROR Requesting SMS: \(error)")
94+
authError = error
95+
self.isProcessingAuthRequest = false
96+
}
97+
}
98+
}
99+
100+
func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) {
101+
isProcessingAuthRequest = true
102+
Task {
103+
do {
104+
let authenticationState = try await client.submitSecurityKeyPinCode(pinCode, sessionData: sessionData, authOptions: authOptions)
105+
self.handleAuthenticationFlowCompletion(authenticationState)
106+
self.isProcessingAuthRequest = false
107+
108+
} catch {
109+
print("ERROR Requesting SMS: \(error)")
110+
authError = error
111+
self.isProcessingAuthRequest = false
112+
}
113+
}
114+
}
115+
116+
func cancelSecurityKeyAssertationRequest() {
117+
Task {
118+
await client.cancelSecurityKeyAssertationRequest()
119+
}
120+
}
121+
}
122+
enum XcodesSheet: Identifiable {
123+
case twoFactor(SecondFactorData)
124+
case securityKeyTouchToConfirm
125+
126+
var id: Int { Kind(self).hashValue }
127+
128+
struct SecondFactorData {
129+
let option: TwoFactorOption
130+
let authOptions: AuthOptionsResponse
131+
let sessionData: AppleSessionData
132+
}
133+
134+
private enum Kind: Hashable {
135+
case signIn, twoFactor(TwoFactorOption), securityKeyTouchToConfirm
136+
137+
enum TwoFactorOption {
138+
case smsSent
139+
case codeSent
140+
case smsPendingChoice
141+
case securityKeyPin
142+
}
143+
144+
init(_ sheet: XcodesSheet) {
145+
switch sheet {
146+
case .twoFactor(let data):
147+
switch data.option {
148+
case .smsSent: self = .twoFactor(.smsSent)
149+
case .smsPendingChoice: self = .twoFactor(.smsPendingChoice)
150+
case .codeSent: self = .twoFactor(.codeSent)
151+
case .securityKey: self = .twoFactor(.securityKeyPin)
152+
}
153+
case .securityKeyTouchToConfirm: self = .securityKeyTouchToConfirm
154+
}
155+
}
156+
}
157+
}
158+
159+
extension Optional {
160+
public var isNotNil: Bool {
161+
get { self != nil }
162+
set { self = newValue ? self : nil }
163+
}
164+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "mac",
5+
"scale" : "1x",
6+
"size" : "16x16"
7+
},
8+
{
9+
"idiom" : "mac",
10+
"scale" : "2x",
11+
"size" : "16x16"
12+
},
13+
{
14+
"idiom" : "mac",
15+
"scale" : "1x",
16+
"size" : "32x32"
17+
},
18+
{
19+
"idiom" : "mac",
20+
"scale" : "2x",
21+
"size" : "32x32"
22+
},
23+
{
24+
"idiom" : "mac",
25+
"scale" : "1x",
26+
"size" : "128x128"
27+
},
28+
{
29+
"idiom" : "mac",
30+
"scale" : "2x",
31+
"size" : "128x128"
32+
},
33+
{
34+
"idiom" : "mac",
35+
"scale" : "1x",
36+
"size" : "256x256"
37+
},
38+
{
39+
"idiom" : "mac",
40+
"scale" : "2x",
41+
"size" : "256x256"
42+
},
43+
{
44+
"idiom" : "mac",
45+
"scale" : "1x",
46+
"size" : "512x512"
47+
},
48+
{
49+
"idiom" : "mac",
50+
"scale" : "2x",
51+
"size" : "512x512"
52+
}
53+
],
54+
"info" : {
55+
"author" : "xcode",
56+
"version" : 1
57+
}
58+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}

0 commit comments

Comments
 (0)