Skip to content

Commit 0a17292

Browse files
refactor: facebook button and use LoginManager
1 parent d4d66a7 commit 0a17292

1 file changed

Lines changed: 199 additions & 0 deletions

File tree

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import AppTrackingTransparency
2+
import FacebookCore
3+
import FacebookLogin
4+
import FirebaseAuth
5+
import FirebaseAuthSwiftUI
6+
import SwiftUI
7+
8+
@MainActor
9+
public struct FacebookButtonView2 {
10+
@Environment(AuthService.self) private var authService
11+
@State private var errorMessage = ""
12+
@State private var limitedLogin: Bool = true
13+
@State private var showCanceledAlert = false
14+
@State private var showUserTrackingAlert = false
15+
16+
private var rawNonce: String
17+
private var shaNonce: String
18+
private let loginManager = LoginManager()
19+
20+
public init() {
21+
rawNonce = FacebookUtils.randomNonce()
22+
shaNonce = FacebookUtils.sha256Hash(of: rawNonce)
23+
}
24+
25+
private var trackingPreference: LoginTracking {
26+
// if not authorized, Facebook will default to limited login and classic login will fail
27+
let trackingStatus = ATTrackingManager.trackingAuthorizationStatus
28+
if trackingStatus != .authorized {
29+
return .limited
30+
}
31+
32+
return limitedLogin ? .limited : .enabled
33+
}
34+
35+
var configuration: LoginConfiguration? {
36+
if trackingPreference == .limited {
37+
return LoginConfiguration(
38+
permissions: [.publicProfile, .email],
39+
tracking: trackingPreference,
40+
nonce: shaNonce
41+
)
42+
}
43+
return LoginConfiguration(
44+
permissions: [.publicProfile, .email],
45+
tracking: trackingPreference
46+
)
47+
}
48+
49+
func invokeLoginMethod() {
50+
let validConfiguration = configuration
51+
if validConfiguration != nil {
52+
loginManager.logIn(
53+
configuration: validConfiguration
54+
) { result in
55+
switch result {
56+
case .cancelled:
57+
showCanceledAlert = true
58+
case let .failed(error):
59+
errorMessage = authService.string.localizedErrorMessage(for: error)
60+
case .success:
61+
if trackingPreference == .limited {
62+
Task {
63+
await limitedLogin()
64+
}
65+
} else {
66+
Task {
67+
await classicLogin()
68+
}
69+
}
70+
}
71+
}
72+
} else {
73+
errorMessage = "Facebook configuration is invalid."
74+
}
75+
}
76+
77+
private func classicLogin() async {
78+
do {
79+
if let token = AccessToken.current,
80+
!token.isExpired {
81+
let credential = FacebookAuthProvider
82+
.credential(withAccessToken: token.tokenString)
83+
try await authService.signIn(with: credential)
84+
} else {
85+
throw NSError(
86+
domain: "FacebookSwiftErrorDomain",
87+
code: 1,
88+
userInfo: [
89+
NSLocalizedDescriptionKey: "Access token has expired or not available. Please sign-in with Facebook before attempting to create a Facebook provider credential",
90+
]
91+
)
92+
}
93+
} catch {
94+
errorMessage = authService.string.localizedErrorMessage(
95+
for: error
96+
)
97+
}
98+
}
99+
100+
private func limitedLogin() async {
101+
do {
102+
if let idToken = AuthenticationToken.current {
103+
let credential = OAuthProvider.credential(withProviderID: kFacebookProviderId,
104+
idToken: idToken.tokenString,
105+
rawNonce: rawNonce)
106+
try await authService.signIn(with: credential)
107+
} else {
108+
throw NSError(
109+
domain: "FacebookSwiftErrorDomain",
110+
code: 2,
111+
userInfo: [
112+
NSLocalizedDescriptionKey: "Authentication is not available. Please sign-in with Facebook before attempting to create a Facebook provider credential",
113+
]
114+
)
115+
}
116+
} catch {
117+
errorMessage = authService.string.localizedErrorMessage(
118+
for: error
119+
)
120+
}
121+
}
122+
123+
private var limitedLoginBinding: Binding<Bool> {
124+
Binding(
125+
get: { self.limitedLogin },
126+
set: { newValue in
127+
let trackingStatus = ATTrackingManager.trackingAuthorizationStatus
128+
129+
if newValue == true, trackingStatus != .authorized {
130+
self.showUserTrackingAlert = true
131+
} else {
132+
self.limitedLogin = newValue
133+
}
134+
}
135+
)
136+
}
137+
138+
func requestTrackingPermission() {
139+
ATTrackingManager.requestTrackingAuthorization { status in
140+
switch status {
141+
case .authorized:
142+
print("Tracking authorized")
143+
case .denied, .restricted, .notDetermined:
144+
print("Tracking not authorized")
145+
@unknown default:
146+
print("Unknown status")
147+
}
148+
}
149+
}
150+
}
151+
152+
extension FacebookButtonView2: View {
153+
public var body: some View {
154+
Button(action: {
155+
invokeLoginMethod()
156+
}) {
157+
HStack {
158+
Image(systemName: "f.circle.fill")
159+
.font(.title)
160+
.foregroundColor(.white)
161+
Text("Continue with Facebook")
162+
.fontWeight(.semibold)
163+
.foregroundColor(.white)
164+
}
165+
.padding()
166+
.frame(maxWidth: .infinity)
167+
.background(Color.blue)
168+
.cornerRadius(8)
169+
}
170+
.alert(isPresented: $showCanceledAlert) {
171+
Alert(
172+
title: Text("Facebook login cancelled"),
173+
dismissButton: .default(Text("OK"))
174+
)
175+
}
176+
HStack {
177+
Text("Authorize User Tracking")
178+
.font(.footnote)
179+
.foregroundColor(.blue)
180+
.underline()
181+
.onTapGesture {
182+
requestTrackingPermission()
183+
}
184+
Toggle(isOn: limitedLoginBinding) {
185+
Text("Limited Login")
186+
.foregroundColor(.green)
187+
}
188+
.toggleStyle(SwitchToggleStyle(tint: .green))
189+
.alert(isPresented: $showUserTrackingAlert) {
190+
Alert(
191+
title: Text("Authorise User Tracking"),
192+
message: Text("For classic Facebook login, please authorize user tracking."),
193+
dismissButton: .default(Text("OK"))
194+
)
195+
}
196+
}
197+
Text(errorMessage).foregroundColor(.red)
198+
}
199+
}

0 commit comments

Comments
 (0)