Skip to content

Commit c54b610

Browse files
authored
Create button on Verification View to fetch age verification signal. (#461)
1 parent 51d362f commit c54b610

5 files changed

Lines changed: 137 additions & 34 deletions

File tree

Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
641495152C405E1400C9A613 /* VerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495142C405E1400C9A613 /* VerificationView.swift */; };
1212
641495172C405E3600C9A613 /* VerificationLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495162C405E3600C9A613 /* VerificationLoader.swift */; };
1313
6499D22A2C4B2F4200825B30 /* Verification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6499D2292C4B2F4200825B30 /* Verification.swift */; };
14+
6499D22C2C4B3B1500825B30 /* AgeVerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6499D22B2C4B3B1500825B30 /* AgeVerificationView.swift */; };
1415
7345AD032703D9470020AFB1 /* DaysUntilBirthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD022703D9470020AFB1 /* DaysUntilBirthday.swift */; };
1516
7345AD052703D9470020AFB1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD042703D9470020AFB1 /* ContentView.swift */; };
1617
7345AD072703D9480020AFB1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7345AD062703D9480020AFB1 /* Assets.xcassets */; };
@@ -61,6 +62,7 @@
6162
641495142C405E1400C9A613 /* VerificationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationView.swift; sourceTree = "<group>"; };
6263
641495162C405E3600C9A613 /* VerificationLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationLoader.swift; sourceTree = "<group>"; };
6364
6499D2292C4B2F4200825B30 /* Verification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Verification.swift; sourceTree = "<group>"; };
65+
6499D22B2C4B3B1500825B30 /* AgeVerificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgeVerificationView.swift; sourceTree = "<group>"; };
6466
7345ACFF2703D9470020AFB1 /* DaysUntilBirthday (iOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DaysUntilBirthday (iOS).app"; sourceTree = BUILT_PRODUCTS_DIR; };
6567
7345AD022703D9470020AFB1 /* DaysUntilBirthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaysUntilBirthday.swift; sourceTree = "<group>"; };
6668
7345AD042703D9470020AFB1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -211,6 +213,7 @@
211213
children = (
212214
7345AD042703D9470020AFB1 /* ContentView.swift */,
213215
739FCC45270E467600C92042 /* BirthdayView.swift */,
216+
6499D22B2C4B3B1500825B30 /* AgeVerificationView.swift */,
214217
7345AD112703D9C30020AFB1 /* SignInView.swift */,
215218
641495142C405E1400C9A613 /* VerificationView.swift */,
216219
7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
@@ -388,6 +391,7 @@
388391
files = (
389392
6499D22A2C4B2F4200825B30 /* Verification.swift in Sources */,
390393
739FCC48270E659A00C92042 /* Birthday.swift in Sources */,
394+
6499D22C2C4B3B1500825B30 /* AgeVerificationView.swift in Sources */,
391395
739FCC46270E467600C92042 /* BirthdayView.swift in Sources */,
392396
7345AD1B2703D9C30020AFB1 /* SignInView.swift in Sources */,
393397
7345AD212703D9C30020AFB1 /* GoogleSignInAuthenticator.swift in Sources */,

Samples/Swift/DaysUntilBirthday/Shared/Services/VerificationLoader.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -123,19 +123,19 @@ final class VerificationLoader: ObservableObject {
123123
func fetchAgeVerificationSignal(verifyResult: GIDVerifiedAccountDetailResult,
124124
completion: @escaping () -> Void) {
125125
self.verificationPublisher(verifyResult: verifyResult) { publisher in
126-
self.cancellable = publisher.sink { sinkCompletion in
127-
switch sinkCompletion {
128-
case .finished:
129-
break
130-
case .failure(let error):
131-
self.verification = Verification.noVerificationSignal
132-
print("Error retrieving age verification: \(error)")
126+
self.cancellable = publisher.sink(
127+
receiveCompletion: { sinkCompletion in
128+
if case .failure(let error) = sinkCompletion {
129+
self.verification = Verification.noVerificationSignal
130+
print("Error retrieving age verification: \(error)")
131+
completion()
132+
}
133+
},
134+
receiveValue: { verification in
135+
self.verification = verification
133136
completion()
134137
}
135-
} receiveValue: { verification in
136-
self.verification = verification
137-
completion()
138-
}
138+
)
139139
}
140140
}
141141
}

Samples/Swift/DaysUntilBirthday/Shared/ViewModels/VerifiedAgeViewModel.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ final class VerifiedAgeViewModel: ObservableObject {
2424
/// The user's account verification status.
2525
/// - note: This will publish updates when its value changes.
2626
@Published var verificationState: VerificationState
27+
/// The age verification signal telling whether the user's age is over 18 or pending.
28+
/// - note: This will publish updates when its value changes.
29+
@Published var ageVerificationSignal: AgeVerificationSignal
30+
/// Indicates whether the view to display the user's age is over 18 should be shown.
31+
/// - note: This will publish updates when its value changes.
32+
@Published var isShowingAgeVerificationSignal = false
33+
/// Indicates whether an alert should be displayed to inform the user that they are not verified as over 18.
34+
/// - note: This will publish updates when its value changes.
35+
@Published var isShowingAgeVerificationAlert = false
2736

2837
/// Minimum time to expiration for a restored access token (10 minutes).
2938
let kMinimumRestoredAccessTokenTimeToExpire: TimeInterval = 600.0
@@ -32,9 +41,20 @@ final class VerifiedAgeViewModel: ObservableObject {
3241
return VerificationLoader(verifiedViewAgeModel: self)
3342
}()
3443

44+
/// An enumeration representing possible states of an age verification signal.
45+
enum AgeVerificationSignal: String {
46+
/// The user's age has been verified to be over 18.
47+
case ageOver18Standard = "AGE_OVER_18_STANDARD"
48+
/// The user's age verification is pending.
49+
case agePending = "AGE_PENDING"
50+
/// Indicates there was no age verification signal found.
51+
case noAgeVerificationSignal = "Signal Unavailable"
52+
}
53+
3554
/// Creates an instance of this view model.
3655
init() {
3756
self.verificationState = .unverified
57+
self.ageVerificationSignal = .noAgeVerificationSignal
3858
}
3959

4060
/// Verifies the user's age is over 18.
@@ -51,6 +71,23 @@ final class VerifiedAgeViewModel: ObservableObject {
5171
loader.verifyUserAgeOver18()
5272
}
5373
}
74+
75+
/// Fetches the age verification signal representing whether the user's age is over 18 or pending.
76+
func fetchAgeVerificationSignal(verifyResult: GIDVerifiedAccountDetailResult) {
77+
loader.fetchAgeVerificationSignal(verifyResult: verifyResult) {
78+
let signal = self.loader.verification?.signal ?? ""
79+
self.ageVerificationSignal = AgeVerificationSignal(rawValue: signal) ??
80+
.noAgeVerificationSignal
81+
82+
if (self.ageVerificationSignal == .ageOver18Standard) {
83+
self.isShowingAgeVerificationSignal = true
84+
self.isShowingAgeVerificationAlert = false
85+
} else {
86+
self.isShowingAgeVerificationSignal = false
87+
self.isShowingAgeVerificationAlert = true
88+
}
89+
}
90+
}
5491
}
5592

5693
extension VerifiedAgeViewModel {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import SwiftUI
18+
import GoogleSignIn
19+
20+
struct AgeVerificationResultView: View {
21+
/// The age verification signal telling whether the user's age is over 18 or pending.
22+
let ageVerificationSignal: String
23+
24+
@Environment(\.presentationMode) var presentationMode
25+
26+
var body: some View {
27+
NavigationView {
28+
VStack {
29+
Text("Age Verification Signal: \(ageVerificationSignal)")
30+
Spacer()
31+
}
32+
.navigationTitle("User is verified over 18!")
33+
.toolbar {
34+
ToolbarItem(placement: .navigationBarTrailing) {
35+
Button("Close") {
36+
presentationMode.wrappedValue.dismiss()
37+
}
38+
}
39+
}
40+
}
41+
}
42+
}

Samples/Swift/DaysUntilBirthday/Shared/Views/VerificationView.swift

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,53 @@ struct VerificationView: View {
2323
var body: some View {
2424
switch verifiedAgeViewModel.verificationState {
2525
case .verified(let result):
26-
VStack(alignment:.leading) {
27-
Text("List of result object properties:")
28-
.font(.headline)
26+
Button("Fetch Age Verification Signal") {
27+
verifiedAgeViewModel.fetchAgeVerificationSignal(verifyResult: result)
28+
}
29+
.padding(10)
30+
.overlay(
31+
RoundedRectangle(cornerRadius: 30)
32+
.stroke(Color.gray)
33+
)
34+
.padding(.bottom)
35+
if #available(iOS 15, *) {
36+
VStack(alignment:.leading) {
37+
Text("List of result object properties:")
38+
.font(.headline)
2939

30-
HStack(alignment: .top) {
31-
Text("Access Token:")
32-
Text(result.accessToken?.tokenString ?? "Not available")
33-
}
34-
HStack(alignment: .top) {
35-
Text("Refresh Token:")
36-
Text(result.refreshToken?.tokenString ?? "Not available")
40+
HStack(alignment: .top) {
41+
Text("Access Token:")
42+
Text(result.accessToken?.tokenString ?? "Not available")
43+
}
44+
HStack(alignment: .top) {
45+
Text("Refresh Token:")
46+
Text(result.refreshToken?.tokenString ?? "Not available")
47+
}
48+
HStack {
49+
Text("Expiration:")
50+
if let expirationDate = result.accessToken?.expirationDate {
51+
Text(formatDateWithDateFormatter(expirationDate))
52+
} else {
53+
Text("Not available")
54+
}
55+
}
56+
Spacer()
3757
}
38-
HStack {
39-
Text("Expiration:")
40-
if let expirationDate = result.accessToken?.expirationDate {
41-
Text(formatDateWithDateFormatter(expirationDate))
42-
} else {
43-
Text("Not available")
58+
.navigationTitle("Verified Account!")
59+
.toolbar {
60+
ToolbarItemGroup(placement: .navigationBarTrailing) {
61+
Button(NSLocalizedString("Refresh", comment: "Refresh button"),
62+
action:{refresh(results: result)})
4463
}
4564
}
46-
Spacer()
47-
}
48-
.navigationTitle("Verified Account!")
49-
.toolbar {
50-
ToolbarItemGroup(placement: .navigationBarTrailing) {
51-
Button(NSLocalizedString("Refresh", comment: "Refresh button"),
52-
action:{refresh(results: result)})
65+
.sheet(isPresented: $verifiedAgeViewModel.isShowingAgeVerificationSignal) {
66+
AgeVerificationResultView(ageVerificationSignal: verifiedAgeViewModel.ageVerificationSignal.rawValue)
67+
}
68+
.alert("Oh no! User is not verified over 18.",
69+
isPresented: $verifiedAgeViewModel.isShowingAgeVerificationAlert) {
70+
Button("OK", role: .cancel) { }
71+
} message: {
72+
Text("Age Verification Signal: \(verifiedAgeViewModel.ageVerificationSignal.rawValue)")
5373
}
5474
}
5575
case .unverified:

0 commit comments

Comments
 (0)