Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
641495132C405D0200C9A613 /* VerifiedAgeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495122C405D0200C9A613 /* VerifiedAgeViewModel.swift */; };
641495152C405E1400C9A613 /* VerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495142C405E1400C9A613 /* VerificationView.swift */; };
641495172C405E3600C9A613 /* VerificationLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641495162C405E3600C9A613 /* 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 @@ -53,6 +56,9 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
641495122C405D0200C9A613 /* VerifiedAgeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerifiedAgeViewModel.swift; sourceTree = "<group>"; };
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>"; };
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 @@ -204,6 +210,7 @@
7345AD042703D9470020AFB1 /* ContentView.swift */,
739FCC45270E467600C92042 /* BirthdayView.swift */,
7345AD112703D9C30020AFB1 /* SignInView.swift */,
641495142C405E1400C9A613 /* VerificationView.swift */,
7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
);
path = Views;
Expand All @@ -221,6 +228,7 @@
isa = PBXGroup;
children = (
7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */,
641495122C405D0200C9A613 /* VerifiedAgeViewModel.swift */,
736F49BB270E102C00580053 /* BirthdayViewModel.swift */,
);
path = ViewModels;
Expand All @@ -231,6 +239,7 @@
children = (
7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */,
736F49B9270E05E200580053 /* BirthdayLoader.swift */,
641495162C405E3600C9A613 /* VerificationLoader.swift */,
7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */,
);
path = Services;
Expand Down Expand Up @@ -383,9 +392,12 @@
736F49BC270E102C00580053 /* BirthdayViewModel.swift in Sources */,
736F49BA270E05E200580053 /* BirthdayLoader.swift in Sources */,
7345AD242703D9C30020AFB1 /* UserProfileView.swift in Sources */,
641495172C405E3600C9A613 /* VerificationLoader.swift in Sources */,
7345AD202703D9C30020AFB1 /* AuthenticationViewModel.swift in Sources */,
641495132C405D0200C9A613 /* VerifiedAgeViewModel.swift in Sources */,
7345AD052703D9470020AFB1 /* ContentView.swift in Sources */,
7345AD032703D9470020AFB1 /* DaysUntilBirthday.swift in Sources */,
641495152C405E1400C9A613 /* VerificationView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
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 */; };
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 +48,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 +201,7 @@
children = (
7345AD042703D9470020AFB1 /* ContentView.swift */,
739FCC45270E467600C92042 /* BirthdayView.swift */,
641495062C3C90E100C9A613 /* VerificationView.swift */,
7345AD112703D9C30020AFB1 /* SignInView.swift */,
7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
);
Expand All @@ -214,6 +221,7 @@
children = (
7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */,
736F49BB270E102C00580053 /* BirthdayViewModel.swift */,
641495092C3E094B00C9A613 /* VerifiedAgeViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand All @@ -222,6 +230,7 @@
isa = PBXGroup;
children = (
7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */,
6414950B2C3E0C7A00C9A613 /* VerificationLoader.swift */,
736F49B9270E05E200580053 /* BirthdayLoader.swift */,
7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */,
);
Expand Down Expand Up @@ -413,12 +422,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 +444,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,58 @@
/*
* 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 is over 18 based upon the selected account.
/// - note: Successful calls to this method will set the `verificationState` property of the
/// `verifiedAgeViewModel` instance passed to the initializer.
func verifyUserAgeOver18() {
let accountDetails: [GIDVerifiableAccountDetail] = [
GIDVerifiableAccountDetail(accountDetailType: .ageOver18)
]

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 else {
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,53 @@
/*
* 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)

/// A class conforming to `ObservableObject` used to represent a user's verification status.
final class VerifiedAgeViewModel: ObservableObject {
Comment thread
brnnmrls marked this conversation as resolved.
Resolved
/// The user's account verification status.
/// - note: This will publish updates when its value changes.
@Published var verificationState: VerificationState

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

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

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

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)
}
}
13 changes: 13 additions & 0 deletions Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import GoogleSignIn

struct UserProfileView: View {
@EnvironmentObject var authViewModel: AuthenticationViewModel
#if os(iOS)
@StateObject var verifiedAgeViewModel = VerifiedAgeViewModel()
#endif
@StateObject var birthdayViewModel = BirthdayViewModel()
private var user: GIDGoogleUser? {
return GIDSignIn.sharedInstance.currentUser
Expand Down Expand Up @@ -50,6 +53,16 @@ struct UserProfileView: View {
return
}
})
#if os(iOS)
NavigationLink(NSLocalizedString("Verify My Age", comment: "Verify Age"),
destination: VerificationView(verifiedAgeViewModel: verifiedAgeViewModel)
.onAppear {
verifiedAgeViewModel.verifyUserAgeOver18()
}
.onDisappear {
verifiedAgeViewModel.verificationState = .unverified
Comment thread
mdmathias marked this conversation as resolved.
Resolved
})
#endif
Spacer()
}
.toolbar {
Expand Down