Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
641495152C405E1400C9A613 /* VerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495142C405E1400C9A613 /* VerificationView.swift */; };
641495172C405E3600C9A613 /* VerificationLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495162C405E3600C9A613 /* VerificationLoader.swift */; };
6499D22A2C4B2F4200825B30 /* Verification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6499D2292C4B2F4200825B30 /* Verification.swift */; };
6499D22C2C4B3B1500825B30 /* AgeVerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6499D22B2C4B3B1500825B30 /* AgeVerificationView.swift */; };
7345AD032703D9470020AFB1 /* DaysUntilBirthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD022703D9470020AFB1 /* DaysUntilBirthday.swift */; };
7345AD052703D9470020AFB1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD042703D9470020AFB1 /* ContentView.swift */; };
7345AD072703D9480020AFB1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7345AD062703D9480020AFB1 /* Assets.xcassets */; };
Expand Down Expand Up @@ -61,6 +62,7 @@
641495142C405E1400C9A613 /* VerificationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationView.swift; sourceTree = "<group>"; };
641495162C405E3600C9A613 /* VerificationLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationLoader.swift; sourceTree = "<group>"; };
6499D2292C4B2F4200825B30 /* Verification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Verification.swift; sourceTree = "<group>"; };
6499D22B2C4B3B1500825B30 /* AgeVerificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgeVerificationView.swift; sourceTree = "<group>"; };
7345ACFF2703D9470020AFB1 /* DaysUntilBirthday (iOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DaysUntilBirthday (iOS).app"; sourceTree = BUILT_PRODUCTS_DIR; };
7345AD022703D9470020AFB1 /* DaysUntilBirthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaysUntilBirthday.swift; sourceTree = "<group>"; };
7345AD042703D9470020AFB1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -211,6 +213,7 @@
children = (
7345AD042703D9470020AFB1 /* ContentView.swift */,
739FCC45270E467600C92042 /* BirthdayView.swift */,
6499D22B2C4B3B1500825B30 /* AgeVerificationView.swift */,
7345AD112703D9C30020AFB1 /* SignInView.swift */,
641495142C405E1400C9A613 /* VerificationView.swift */,
7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
Expand Down Expand Up @@ -388,6 +391,7 @@
files = (
6499D22A2C4B2F4200825B30 /* Verification.swift in Sources */,
739FCC48270E659A00C92042 /* Birthday.swift in Sources */,
6499D22C2C4B3B1500825B30 /* AgeVerificationView.swift in Sources */,
739FCC46270E467600C92042 /* BirthdayView.swift in Sources */,
7345AD1B2703D9C30020AFB1 /* SignInView.swift in Sources */,
7345AD212703D9C30020AFB1 /* GoogleSignInAuthenticator.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,19 +123,19 @@ final class VerificationLoader: ObservableObject {
func fetchAgeVerificationSignal(verifyResult: GIDVerifiedAccountDetailResult,
completion: @escaping () -> Void) {
self.verificationPublisher(verifyResult: verifyResult) { publisher in
self.cancellable = publisher.sink { sinkCompletion in
switch sinkCompletion {
case .finished:
break
case .failure(let error):
self.verification = Verification.noVerificationSignal
print("Error retrieving age verification: \(error)")
self.cancellable = publisher.sink(
receiveCompletion: { sinkCompletion in
if case .failure(let error) = sinkCompletion {
self.verification = Verification.noVerificationSignal
print("Error retrieving age verification: \(error)")
completion()
}
},
receiveValue: { verification in
self.verification = verification
completion()
}
} receiveValue: { verification in
self.verification = verification
completion()
}
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,57 @@ final class VerifiedAgeViewModel: ObservableObject {
/// The user's account verification status.
/// - note: This will publish updates when its value changes.
@Published var verificationState: VerificationState
/// The age verification signal telling whether the user's age is over 18 or pending.
/// - note: This will publish updates when its value changes.
@Published var ageVerificationSignal: AgeVerificationSignal
/// Indicates whether the view to display the user's age is over 18 should be shown.
/// - note: This will publish updates when its value changes.
@Published var isShowingAgeVerificationSignal = false
/// Indicates whether an alert should be displayed to inform the user that they are not verified as over 18.
/// - note: This will publish updates when its value changes.
@Published var isShowingAgeVerificationAlert = false

private lazy var loader: VerificationLoader = {
return VerificationLoader(verifiedViewAgeModel: self)
}()

/// An enumeration representing possible states of an age verification signal.
enum AgeVerificationSignal: String {
/// The user's age has been verified to be over 18.
case ageOver18Standard = "AGE_OVER_18_STANDARD"
/// The user's age verification is pending.
case agePending = "AGE_PENDING"
/// Indicates there was no age verification signal found.
case noAgeVerificationSignal = "Signal Unavailable"
}

/// Creates an instance of this view model.
init() {
self.verificationState = .unverified
self.ageVerificationSignal = .noAgeVerificationSignal
}

/// Verifies the user's age is over 18.
func verifyUserAgeOver18() {
loader.verifyUserAgeOver18()
}

/// Fetches the age verification signal representing whether the user's age is over 18 or pending.
func fetchAgeVerificationSignal(verifyResult: GIDVerifiedAccountDetailResult) {
loader.fetchAgeVerificationSignal(verifyResult: verifyResult) {
let signal = self.loader.verification?.signal ?? ""
self.ageVerificationSignal = AgeVerificationSignal(rawValue: signal) ??
.noAgeVerificationSignal

if (self.ageVerificationSignal == .ageOver18Standard) {
self.isShowingAgeVerificationSignal = true
self.isShowingAgeVerificationAlert = false
} else {
self.isShowingAgeVerificationSignal = false
self.isShowingAgeVerificationAlert = true
}
}
}
}

extension VerifiedAgeViewModel {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import SwiftUI
import GoogleSignIn

struct AgeVerificationResultView: View {
/// The age verification signal telling whether the user's age is over 18 or pending.
let ageVerificationSignal: String

@Environment(\.presentationMode) var presentationMode

var body: some View {
NavigationView {
VStack {
Text("Age Verification Signal: \(ageVerificationSignal)")
Spacer()
}
.navigationTitle("User is verified over 18!")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Close") {
presentationMode.wrappedValue.dismiss()
}
}
}
}
}
}
66 changes: 43 additions & 23 deletions Samples/Swift/DaysUntilBirthday/Shared/Views/VerificationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,53 @@ struct VerificationView: View {
var body: some View {
switch verifiedAgeViewModel.verificationState {
case .verified(let result):
VStack(alignment:.leading) {
Text("List of result object properties:")
.font(.headline)
Button("Fetch Age Verification Signal") {
verifiedAgeViewModel.fetchAgeVerificationSignal(verifyResult: result)
}
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 30)
.stroke(Color.gray)
)
.padding(.bottom)
if #available(iOS 15, *) {
VStack(alignment:.leading) {
Text("List of result object properties:")
.font(.headline)

HStack(alignment: .top) {
Text("Access Token:")
Text(result.accessToken?.tokenString ?? "Not available")
}
HStack(alignment: .top) {
Text("Refresh Token:")
Text(result.refreshToken?.tokenString ?? "Not available")
HStack(alignment: .top) {
Text("Access Token:")
Text(result.accessToken?.tokenString ?? "Not available")
}
HStack(alignment: .top) {
Text("Refresh Token:")
Text(result.refreshToken?.tokenString ?? "Not available")
}
HStack {
Text("Expiration:")
if let expirationDate = result.accessToken?.expirationDate {
Text(formatDateWithDateFormatter(expirationDate))
} else {
Text("Not available")
}
}
Spacer()
}
HStack {
Text("Expiration:")
if let expirationDate = result.accessToken?.expirationDate {
Text(formatDateWithDateFormatter(expirationDate))
} else {
Text("Not available")
.navigationTitle("Verified Account!")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(NSLocalizedString("Refresh", comment: "Refresh button"),
action:{refresh(results: result)})
}
}
Spacer()
}
.navigationTitle("Verified Account!")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(NSLocalizedString("Refresh", comment: "Refresh button"),
action:{refresh(results: result)})
.sheet(isPresented: $verifiedAgeViewModel.isShowingAgeVerificationSignal) {
AgeVerificationResultView(ageVerificationSignal: verifiedAgeViewModel.ageVerificationSignal.rawValue)
}
.alert("Oh no! User is not verified over 18.",
isPresented: $verifiedAgeViewModel.isShowingAgeVerificationAlert) {
Button("OK", role: .cancel) { }
} message: {
Text("Age Verification Signal: \(verifiedAgeViewModel.ageVerificationSignal.rawValue)")
}
}
case .unverified:
Expand Down