Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VwG] Add verification flow example to DaysUntilBirthday Sample App. #452

Merged
merged 9 commits into from
Jul 16, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
4C6A2BA2321434ACBD2AF201 /* Pods_DaysUntilBirthdayForPod__macOS_.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 926A15D393D684C68E09FB0F /* Pods_DaysUntilBirthdayForPod__macOS_.framework */; };
641495072C3C90E100C9A613 /* VerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495062C3C90E100C9A613 /* VerificationView.swift */; };
641495082C3C90E100C9A613 /* VerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495062C3C90E100C9A613 /* VerificationView.swift */; };
6414950A2C3E094B00C9A613 /* VerifiedAgeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495092C3E094B00C9A613 /* VerifiedAgeViewModel.swift */; };
6414950C2C3E0C7A00C9A613 /* VerificationLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6414950B2C3E0C7A00C9A613 /* VerificationLoader.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 @@ -45,6 +49,9 @@
1A129363EBB5DF1F41FAAB14 /* Pods-DaysUntilBirthdayForPod(macOS).release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DaysUntilBirthdayForPod(macOS).release.xcconfig"; path = "Target Support Files/Pods-DaysUntilBirthdayForPod(macOS)/Pods-DaysUntilBirthdayForPod(macOS).release.xcconfig"; sourceTree = "<group>"; };
29BEB027A694593FA0450863 /* Pods-DaysUntilBirthdayForPod(iOS).debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DaysUntilBirthdayForPod(iOS).debug.xcconfig"; path = "Target Support Files/Pods-DaysUntilBirthdayForPod(iOS)/Pods-DaysUntilBirthdayForPod(iOS).debug.xcconfig"; sourceTree = "<group>"; };
5CF615341A61D92D89389D44 /* Pods-DaysUntilBirthdayForPod (macOS).release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DaysUntilBirthdayForPod (macOS).release.xcconfig"; path = "Target Support Files/Pods-DaysUntilBirthdayForPod (macOS)/Pods-DaysUntilBirthdayForPod (macOS).release.xcconfig"; sourceTree = "<group>"; };
641495062C3C90E100C9A613 /* VerificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerificationView.swift; sourceTree = "<group>"; };
641495092C3E094B00C9A613 /* VerifiedAgeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedAgeViewModel.swift; sourceTree = "<group>"; };
6414950B2C3E0C7A00C9A613 /* VerificationLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerificationLoader.swift; sourceTree = "<group>"; };
7345ACFF2703D9470020AFB1 /* DaysUntilBirthdayForPod (iOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DaysUntilBirthdayForPod (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 @@ -195,6 +202,7 @@
children = (
7345AD042703D9470020AFB1 /* ContentView.swift */,
739FCC45270E467600C92042 /* BirthdayView.swift */,
641495062C3C90E100C9A613 /* VerificationView.swift */,
7345AD112703D9C30020AFB1 /* SignInView.swift */,
7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
);
Expand All @@ -214,6 +222,7 @@
children = (
7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */,
736F49BB270E102C00580053 /* BirthdayViewModel.swift */,
641495092C3E094B00C9A613 /* VerifiedAgeViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand All @@ -222,6 +231,7 @@
isa = PBXGroup;
children = (
7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */,
6414950B2C3E0C7A00C9A613 /* VerificationLoader.swift */,
736F49B9270E05E200580053 /* BirthdayLoader.swift */,
7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */,
);
Expand Down Expand Up @@ -413,12 +423,15 @@
buildActionMask = 2147483647;
files = (
739FCC48270E659A00C92042 /* Birthday.swift in Sources */,
641495072C3C90E100C9A613 /* VerificationView.swift in Sources */,
739FCC46270E467600C92042 /* BirthdayView.swift in Sources */,
7345AD1B2703D9C30020AFB1 /* SignInView.swift in Sources */,
7345AD212703D9C30020AFB1 /* GoogleSignInAuthenticator.swift in Sources */,
6414950A2C3E094B00C9A613 /* VerifiedAgeViewModel.swift in Sources */,
7345AD232703D9C30020AFB1 /* UserProfileImageLoader.swift in Sources */,
7345AD1E2703D9C30020AFB1 /* UserProfileImageView.swift in Sources */,
736F49BC270E102C00580053 /* BirthdayViewModel.swift in Sources */,
6414950C2C3E0C7A00C9A613 /* VerificationLoader.swift in Sources */,
736F49BA270E05E200580053 /* BirthdayLoader.swift in Sources */,
7345AD242703D9C30020AFB1 /* UserProfileView.swift in Sources */,
7345AD202703D9C30020AFB1 /* AuthenticationViewModel.swift in Sources */,
Expand All @@ -432,6 +445,7 @@
buildActionMask = 2147483647;
files = (
73DB41912805FBFD0028B8D3 /* SignInView.swift in Sources */,
641495082C3C90E100C9A613 /* VerificationView.swift in Sources */,
73DB418B2805FBC40028B8D3 /* BirthdayLoader.swift in Sources */,
73DB418D2805FBD00028B8D3 /* AuthenticationViewModel.swift in Sources */,
73DB418F2805FBF50028B8D3 /* ContentView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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 Foundation
import GoogleSignIn

#if os(iOS)

/// An observable class for verifying via Google.
final class VerificationLoader: ObservableObject {
private var verifiedAgeViewModel: VerifiedAgeViewModel

/// Creates an instance of this loader.
/// - parameter verifiedAgeViewModel: The view model to use to set verification status on.
init(verifiedViewAgeModel: VerifiedAgeViewModel) {
self.verifiedAgeViewModel = verifiedViewAgeModel
}

/// Verifies the user's age based upon the selected account.
brnnmrls marked this conversation as resolved.
Show resolved Hide resolved
/// - note: Successful calls to this will set the `verifiedAgeViewModel`'s `verificationState` property.
brnnmrls marked this conversation as resolved.
Show resolved Hide resolved
func verifyAccountDetails() {
brnnmrls marked this conversation as resolved.
Show resolved Hide resolved
let accountDetails: [GIDVerifiableAccountDetail] = [
GIDVerifiableAccountDetail(accountDetailType:.ageOver18)
brnnmrls marked this conversation as resolved.
Show resolved Hide resolved
]

guard let rootViewController = UIApplication.shared.windows.first?.rootViewController else {
print("There is no root view controller!")
return
}

let verifyAccountDetail = GIDVerifyAccountDetail()
verifyAccountDetail.verifyAccountDetails(accountDetails, presenting: rootViewController) {
verifyResult, error in
guard let verifyResult = verifyResult else {
brnnmrls marked this conversation as resolved.
Show resolved Hide resolved
self.verifiedAgeViewModel.verificationState = .unverified
print("Error! \(String(describing: error))")
return
}
self.verifiedAgeViewModel.verificationState = .verified(verifyResult)
}
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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

#if os(iOS)

final class VerifiedAgeViewModel: ObservableObject {
brnnmrls marked this conversation as resolved.
Show resolved Hide resolved
/// The user's account verification status.
/// - note: This will publish updates when its value changes.
@Published var verificationState: VerificationState

private var loader: VerificationLoader {
mdmathias marked this conversation as resolved.
Show resolved Hide resolved
return VerificationLoader(verifiedViewAgeModel: self)
}

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

/// Verifies the user.
func verifyAccountDetails() {
brnnmrls marked this conversation as resolved.
Show resolved Hide resolved
switch self.verificationState {
case .unverified:
loader.verifyAccountDetails()
mdmathias marked this conversation as resolved.
Show resolved Hide resolved
case .verified:
return
}
}
}

extension VerifiedAgeViewModel {
/// An enumeration representing verified status.
enum VerificationState {
/// The user's account is verified and is the associated value of this case.
case verified(GIDVerifiedAccountDetailResult)
/// The user's account is not verified.
case unverified
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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 VerificationView: View {
@ObservedObject var verifiedAgeViewModel: VerifiedAgeViewModel

var body: some View {
switch verifiedAgeViewModel.verificationState {
case .verified(let result):
VStack(alignment:.leading) {
Text("List of result object properties:")
.font(.headline)

HStack(alignment: .top) {
Text("Access Token:")
Text(result.accessTokenString ?? "Not available")
}
HStack(alignment: .top) {
Text("Refresh Token:")
Text(result.refreshTokenString ?? "Not available")
}
HStack {
Text("Expiration:")
if let expirationDate = result.expirationDate {
Text(formatDateWithDateFormatter(expirationDate))
} else {
Text("Not available")
}
}
Spacer()
}
.navigationTitle("Verified Account!")
case .unverified:
ProgressView()
.navigationTitle(NSLocalizedString("Unverified account",
comment: "Unverified account label"))
}
}

func formatDateWithDateFormatter(_ date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .short
return dateFormatter.string(from: date)
}
}
9 changes: 9 additions & 0 deletions Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import GoogleSignIn

struct UserProfileView: View {
@EnvironmentObject var authViewModel: AuthenticationViewModel

@StateObject var verifiedAgeViewModel = VerifiedAgeViewModel()
@StateObject var birthdayViewModel = BirthdayViewModel()
private var user: GIDGoogleUser? {
return GIDSignIn.sharedInstance.currentUser
Expand Down Expand Up @@ -50,6 +52,13 @@ struct UserProfileView: View {
return
}
})
#if os(iOS)
NavigationLink(NSLocalizedString("Verify My Age", comment: "Verify Age"),
destination: VerificationView(verifiedAgeViewModel: verifiedAgeViewModel)
.onAppear {
verifiedAgeViewModel.verifyAccountDetails()
})
#endif
Spacer()
}
.toolbar {
Expand Down
Loading