Browse Source

Modify sample app `DaysUntilBirthday` to be multiplatform (#111)

* Run DaysUntilBirthday sample app on MacOS

1. add a macOS target DaysUntilBirthdayOnMac
2. Modify ContentView and GoogleSignInButtonWrapper to add a sign-in button on the app.
3. Modify AuthenticationViewModel and GoogleSignInAuthenticator and demonstrate sign in and restore previous sign in works correctly.

* Add UserProfile view and sign-out/disconnect button.

1. Add GoogleSignInButtonMac
2. Modify UserProfileView to work on mac.

* Add birthday reminder

Users can request birthday read scope and fetch their birthdays to display the days until birthdays.

* Add specific views for the sample app on macOS

1. GoogleSignInButtonOnMac
2. UserProfileViewOnMac

* Reorganize file structure.

* Minor structure changes

- Rename the "Sources" folder to "Shared" to align with Xcode's multiplatform app template.
- Rename the target names as "DaysUntilBirthday (iOS)" and "DaysUntilBirthday (macOS)".
- Move the "GoogleSignIn-iOS" local package resource out of "Sources" and into the top level of the project resource tree.

* Move views from shared folder to iOS and macOS folder.

* Modify DaysUntilBirthdayForPod to work for both iOS and macOS.

* Minor improvement
pinlu 4 năm trước cách đây
mục cha
commit
b3f4de8dee
48 tập tin đã thay đổi với 1059 bổ sung215 xóa
  1. 256 30
      Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/project.pbxproj
  2. 78 0
      Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/xcshareddata/xcschemes/DaysUntilBirthday(iOS).xcscheme
  3. 78 0
      Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/xcshareddata/xcschemes/DaysUntilBirthday(macOS).xcscheme
  4. 417 149
      Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayForPod.xcodeproj/project.pbxproj
  5. 8 4
      Samples/Swift/DaysUntilBirthday/Podfile
  6. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AccentColor.colorset/Contents.json
  7. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json
  8. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/Icon.png
  9. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt.png
  10. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png
  11. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png
  12. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png
  13. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt.png
  14. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png
  15. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png
  16. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png
  17. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt.png
  18. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png
  19. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png
  20. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png
  21. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png
  22. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png
  23. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_76pt.png
  24. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png
  25. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png
  26. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/Contents.json
  27. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/PlaceholderAvatar.imageset/Contents.json
  28. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/PlaceholderAvatar.imageset/PlaceholderAvatar.png
  29. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/DaysUntilBirthday.swift
  30. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Models/Birthday.swift
  31. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Services/BirthdayLoader.swift
  32. 61 16
      Samples/Swift/DaysUntilBirthday/Shared/Services/GoogleSignInAuthenticator.swift
  33. 12 2
      Samples/Swift/DaysUntilBirthday/Shared/Services/UserProfileImageLoader.swift
  34. 9 8
      Samples/Swift/DaysUntilBirthday/Shared/ViewModels/AuthenticationViewModel.swift
  35. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/ViewModels/BirthdayViewModel.swift
  36. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Views/BirthdayView.swift
  37. 2 0
      Samples/Swift/DaysUntilBirthday/Shared/Views/ContentView.swift
  38. 0 0
      Samples/Swift/DaysUntilBirthday/Shared/Views/SignInView.swift
  39. 10 0
      Samples/Swift/DaysUntilBirthday/Shared/Views/UserProfileImageView.swift
  40. 1 2
      Samples/Swift/DaysUntilBirthday/iOS/GoogleSignInButtonWrapper.swift
  41. 0 0
      Samples/Swift/DaysUntilBirthday/iOS/Info.plist
  42. 0 0
      Samples/Swift/DaysUntilBirthday/iOS/Preview Content/Preview Assets.xcassets/Contents.json
  43. 2 4
      Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift
  44. 12 0
      Samples/Swift/DaysUntilBirthday/macOS/DaysUntilBirthdayOnMac.entitlements
  45. 17 0
      Samples/Swift/DaysUntilBirthday/macOS/GoogleSignInButtonWrapper.swift
  46. 19 0
      Samples/Swift/DaysUntilBirthday/macOS/Info.plist
  47. 6 0
      Samples/Swift/DaysUntilBirthday/macOS/Preview Content/Preview Assets.xcassets/Contents.json
  48. 71 0
      Samples/Swift/DaysUntilBirthday/macOS/UserProfileView.swift

+ 256 - 30
Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/project.pbxproj

@@ -23,10 +23,26 @@
 		736F49BC270E102C00580053 /* BirthdayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736F49BB270E102C00580053 /* BirthdayViewModel.swift */; };
 		739FCC46270E467600C92042 /* BirthdayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739FCC45270E467600C92042 /* BirthdayView.swift */; };
 		739FCC48270E659A00C92042 /* Birthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739FCC47270E659A00C92042 /* Birthday.swift */; };
+		FE71738A27ECFAF600910319 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FE71738927ECFAF600910319 /* Preview Assets.xcassets */; };
+		FE71739127ECFB3300910319 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = FE71739027ECFB3300910319 /* GoogleSignIn */; };
+		FE71739227ED135C00910319 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */; };
+		FE71739327ED138B00910319 /* GoogleSignInAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */; };
+		FE71739427ED15F700910319 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD042703D9470020AFB1 /* ContentView.swift */; };
+		FE71739527ED17C200910319 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD112703D9C30020AFB1 /* SignInView.swift */; };
+		FE71739827EE2DE000910319 /* DaysUntilBirthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD022703D9470020AFB1 /* DaysUntilBirthday.swift */; };
+		FE71739C27F248E100910319 /* UserProfileImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */; };
+		FE71739D27F248E600910319 /* UserProfileImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */; };
+		FE71739E27F2525900910319 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7345AD062703D9480020AFB1 /* Assets.xcassets */; };
+		FE71739F27F3AA9E00910319 /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736F49B9270E05E200580053 /* BirthdayLoader.swift */; };
+		FE7173A027F3AAA400910319 /* Birthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739FCC47270E659A00C92042 /* Birthday.swift */; };
+		FE7173A127F3AB0900910319 /* BirthdayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736F49BB270E102C00580053 /* BirthdayViewModel.swift */; };
+		FE7173A227F3AB5000910319 /* BirthdayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739FCC45270E467600C92042 /* BirthdayView.swift */; };
+		FE7173A427F5106900910319 /* GoogleSignInButtonWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7173A327F5106900910319 /* GoogleSignInButtonWrapper.swift */; };
+		FE7173A627F5110F00910319 /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE7173A527F5110F00910319 /* UserProfileView.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
-		7345ACFF2703D9470020AFB1 /* DaysUntilBirthday.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DaysUntilBirthday.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		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>"; };
 		7345AD062703D9480020AFB1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -43,6 +59,13 @@
 		736F49BB270E102C00580053 /* BirthdayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayViewModel.swift; sourceTree = "<group>"; };
 		739FCC45270E467600C92042 /* BirthdayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayView.swift; sourceTree = "<group>"; };
 		739FCC47270E659A00C92042 /* Birthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Birthday.swift; sourceTree = "<group>"; };
+		FE2F2ABC2800D9C1005EA17F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		FE71737B27ECFA5600910319 /* GoogleSignIn-iOS */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "GoogleSignIn-iOS"; path = ../../..; sourceTree = "<group>"; };
+		FE71738027ECFAF400910319 /* DaysUntilBirthday(macOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DaysUntilBirthday(macOS).app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		FE71738927ECFAF600910319 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
+		FE71738B27ECFAF600910319 /* DaysUntilBirthdayOnMac.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DaysUntilBirthdayOnMac.entitlements; sourceTree = "<group>"; };
+		FE7173A327F5106900910319 /* GoogleSignInButtonWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSignInButtonWrapper.swift; sourceTree = "<group>"; };
+		FE7173A527F5110F00910319 /* UserProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -54,46 +77,49 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		FE71737D27ECFAF400910319 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				FE71739127ECFB3300910319 /* GoogleSignIn in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
 		7345ACF62703D9470020AFB1 = {
 			isa = PBXGroup;
 			children = (
-				7345AD012703D9470020AFB1 /* Sources */,
+				FE71737B27ECFA5600910319 /* GoogleSignIn-iOS */,
+				7345AD012703D9470020AFB1 /* Shared */,
+				FE7173A927F656AF00910319 /* iOS */,
+				FE71738127ECFAF400910319 /* macOS */,
 				7345AD002703D9470020AFB1 /* Products */,
+				FE71738F27ECFB3300910319 /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
 		7345AD002703D9470020AFB1 /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				7345ACFF2703D9470020AFB1 /* DaysUntilBirthday.app */,
+				7345ACFF2703D9470020AFB1 /* DaysUntilBirthday(iOS).app */,
+				FE71738027ECFAF400910319 /* DaysUntilBirthday(macOS).app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
-		7345AD012703D9470020AFB1 /* Sources */ = {
+		7345AD012703D9470020AFB1 /* Shared */ = {
 			isa = PBXGroup;
 			children = (
 				7345AD022703D9470020AFB1 /* DaysUntilBirthday.swift */,
-				7345AD042703D9470020AFB1 /* ContentView.swift */,
-				7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */,
-				736F49B9270E05E200580053 /* BirthdayLoader.swift */,
-				736F49BB270E102C00580053 /* BirthdayViewModel.swift */,
-				739FCC47270E659A00C92042 /* Birthday.swift */,
-				739FCC45270E467600C92042 /* BirthdayView.swift */,
-				7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */,
-				7345AD132703D9C30020AFB1 /* GoogleSignInButtonWrapper.swift */,
-				7345AD112703D9C30020AFB1 /* SignInView.swift */,
-				7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */,
-				7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
-				7345AD1A2703D9C30020AFB1 /* UserProfileView.swift */,
+				FE7173AF27F6633900910319 /* Services */,
+				FE7173AE27F6632E00910319 /* ViewModels */,
+				FE7173AD27F65B8500910319 /* Models */,
+				FE7173AA27F6578E00910319 /* Views */,
 				7345AD062703D9480020AFB1 /* Assets.xcassets */,
-				7345AD0B2703D9480020AFB1 /* Info.plist */,
-				7345AD082703D9480020AFB1 /* Preview Content */,
 			);
-			path = Sources;
+			path = Shared;
 			sourceTree = "<group>";
 		};
 		7345AD082703D9480020AFB1 /* Preview Content */ = {
@@ -104,12 +130,88 @@
 			path = "Preview Content";
 			sourceTree = "<group>";
 		};
+		FE71738127ECFAF400910319 /* macOS */ = {
+			isa = PBXGroup;
+			children = (
+				FE7173A327F5106900910319 /* GoogleSignInButtonWrapper.swift */,
+				FE7173A527F5110F00910319 /* UserProfileView.swift */,
+				FE2F2ABC2800D9C1005EA17F /* Info.plist */,
+				FE71738B27ECFAF600910319 /* DaysUntilBirthdayOnMac.entitlements */,
+				FE71738827ECFAF600910319 /* Preview Content */,
+			);
+			path = macOS;
+			sourceTree = "<group>";
+		};
+		FE71738827ECFAF600910319 /* Preview Content */ = {
+			isa = PBXGroup;
+			children = (
+				FE71738927ECFAF600910319 /* Preview Assets.xcassets */,
+			);
+			path = "Preview Content";
+			sourceTree = "<group>";
+		};
+		FE71738F27ECFB3300910319 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		FE7173A927F656AF00910319 /* iOS */ = {
+			isa = PBXGroup;
+			children = (
+				7345AD132703D9C30020AFB1 /* GoogleSignInButtonWrapper.swift */,
+				7345AD1A2703D9C30020AFB1 /* UserProfileView.swift */,
+				7345AD0B2703D9480020AFB1 /* Info.plist */,
+				7345AD082703D9480020AFB1 /* Preview Content */,
+			);
+			path = iOS;
+			sourceTree = "<group>";
+		};
+		FE7173AA27F6578E00910319 /* Views */ = {
+			isa = PBXGroup;
+			children = (
+				7345AD042703D9470020AFB1 /* ContentView.swift */,
+				739FCC45270E467600C92042 /* BirthdayView.swift */,
+				7345AD112703D9C30020AFB1 /* SignInView.swift */,
+				7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
+			);
+			path = Views;
+			sourceTree = "<group>";
+		};
+		FE7173AD27F65B8500910319 /* Models */ = {
+			isa = PBXGroup;
+			children = (
+				739FCC47270E659A00C92042 /* Birthday.swift */,
+			);
+			path = Models;
+			sourceTree = "<group>";
+		};
+		FE7173AE27F6632E00910319 /* ViewModels */ = {
+			isa = PBXGroup;
+			children = (
+				7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */,
+				736F49BB270E102C00580053 /* BirthdayViewModel.swift */,
+			);
+			path = ViewModels;
+			sourceTree = "<group>";
+		};
+		FE7173AF27F6633900910319 /* Services */ = {
+			isa = PBXGroup;
+			children = (
+				7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */,
+				736F49B9270E05E200580053 /* BirthdayLoader.swift */,
+				7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */,
+			);
+			path = Services;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		7345ACFE2703D9470020AFB1 /* DaysUntilBirthday */ = {
+		7345ACFE2703D9470020AFB1 /* DaysUntilBirthday(iOS) */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday" */;
+			buildConfigurationList = 7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday(iOS)" */;
 			buildPhases = (
 				7345ACFB2703D9470020AFB1 /* Sources */,
 				7345ACFC2703D9470020AFB1 /* Frameworks */,
@@ -119,12 +221,32 @@
 			);
 			dependencies = (
 			);
-			name = DaysUntilBirthday;
+			name = "DaysUntilBirthday(iOS)";
 			packageProductDependencies = (
 				7345AD262703DA3E0020AFB1 /* GoogleSignIn */,
 			);
 			productName = "GSI-Swift-Sample";
-			productReference = 7345ACFF2703D9470020AFB1 /* DaysUntilBirthday.app */;
+			productReference = 7345ACFF2703D9470020AFB1 /* DaysUntilBirthday(iOS).app */;
+			productType = "com.apple.product-type.application";
+		};
+		FE71737F27ECFAF400910319 /* DaysUntilBirthday(macOS) */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = FE71738C27ECFAF600910319 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday(macOS)" */;
+			buildPhases = (
+				FE71737C27ECFAF400910319 /* Sources */,
+				FE71737D27ECFAF400910319 /* Frameworks */,
+				FE71737E27ECFAF400910319 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "DaysUntilBirthday(macOS)";
+			packageProductDependencies = (
+				FE71739027ECFB3300910319 /* GoogleSignIn */,
+			);
+			productName = DaysUntilBirthdayOnMac;
+			productReference = FE71738027ECFAF400910319 /* DaysUntilBirthday(macOS).app */;
 			productType = "com.apple.product-type.application";
 		};
 /* End PBXNativeTarget section */
@@ -133,12 +255,15 @@
 		7345ACF72703D9470020AFB1 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastSwiftUpdateCheck = 1250;
+				LastSwiftUpdateCheck = 1310;
 				LastUpgradeCheck = 1250;
 				TargetAttributes = {
 					7345ACFE2703D9470020AFB1 = {
 						CreatedOnToolsVersion = 12.5.1;
 					};
+					FE71737F27ECFAF400910319 = {
+						CreatedOnToolsVersion = 13.1;
+					};
 				};
 			};
 			buildConfigurationList = 7345ACFA2703D9470020AFB1 /* Build configuration list for PBXProject "DaysUntilBirthday" */;
@@ -157,7 +282,8 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				7345ACFE2703D9470020AFB1 /* DaysUntilBirthday */,
+				7345ACFE2703D9470020AFB1 /* DaysUntilBirthday(iOS) */,
+				FE71737F27ECFAF400910319 /* DaysUntilBirthday(macOS) */,
 			);
 		};
 /* End PBXProject section */
@@ -172,6 +298,15 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		FE71737E27ECFAF400910319 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				FE71738A27ECFAF600910319 /* Preview Assets.xcassets in Resources */,
+				FE71739E27F2525900910319 /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -195,6 +330,26 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		FE71737C27ECFAF400910319 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				FE71739F27F3AA9E00910319 /* BirthdayLoader.swift in Sources */,
+				FE71739C27F248E100910319 /* UserProfileImageLoader.swift in Sources */,
+				FE7173A027F3AAA400910319 /* Birthday.swift in Sources */,
+				FE71739827EE2DE000910319 /* DaysUntilBirthday.swift in Sources */,
+				FE71739227ED135C00910319 /* AuthenticationViewModel.swift in Sources */,
+				FE7173A427F5106900910319 /* GoogleSignInButtonWrapper.swift in Sources */,
+				FE7173A227F3AB5000910319 /* BirthdayView.swift in Sources */,
+				FE71739427ED15F700910319 /* ContentView.swift in Sources */,
+				FE71739327ED138B00910319 /* GoogleSignInAuthenticator.swift in Sources */,
+				FE7173A127F3AB0900910319 /* BirthdayViewModel.swift in Sources */,
+				FE71739D27F248E600910319 /* UserProfileImageView.swift in Sources */,
+				FE7173A627F5110F00910319 /* UserProfileView.swift in Sources */,
+				FE71739527ED17C200910319 /* SignInView.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
@@ -324,9 +479,9 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				DEVELOPMENT_ASSET_PATHS = "\"iOS/Preview Content\"";
 				ENABLE_PREVIEWS = YES;
-				INFOPLIST_FILE = Sources/Info.plist;
+				INFOPLIST_FILE = iOS/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
@@ -345,9 +500,9 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				DEVELOPMENT_ASSET_PATHS = "\"iOS/Preview Content\"";
 				ENABLE_PREVIEWS = YES;
-				INFOPLIST_FILE = Sources/Info.plist;
+				INFOPLIST_FILE = iOS/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
@@ -360,6 +515,64 @@
 			};
 			name = Release;
 		};
+		FE71738D27ECFAF600910319 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+				CODE_SIGN_ENTITLEMENTS = macOS/DaysUntilBirthdayOnMac.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_ASSET_PATHS = "\"macOS/Preview Content\"";
+				ENABLE_PREVIEWS = YES;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = macOS/Info.plist;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 12.0;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+			};
+			name = Debug;
+		};
+		FE71738E27ECFAF600910319 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+				CODE_SIGN_ENTITLEMENTS = macOS/DaysUntilBirthdayOnMac.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_ASSET_PATHS = "\"macOS/Preview Content\"";
+				ENABLE_PREVIEWS = YES;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = macOS/Info.plist;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 12.0;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -372,7 +585,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday" */ = {
+		7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday(iOS)" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				7345AD0F2703D9480020AFB1 /* Debug */,
@@ -381,6 +594,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		FE71738C27ECFAF600910319 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday(macOS)" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				FE71738D27ECFAF600910319 /* Debug */,
+				FE71738E27ECFAF600910319 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 
 /* Begin XCRemoteSwiftPackageReference section */
@@ -400,6 +622,10 @@
 			package = 7345AD252703DA3E0020AFB1 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */;
 			productName = GoogleSignIn;
 		};
+		FE71739027ECFB3300910319 /* GoogleSignIn */ = {
+			isa = XCSwiftPackageProductDependency;
+			productName = GoogleSignIn;
+		};
 /* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = 7345ACF72703D9470020AFB1 /* Project object */;

+ 78 - 0
Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/xcshareddata/xcschemes/DaysUntilBirthday(iOS).xcscheme

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1310"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "7345ACFE2703D9470020AFB1"
+               BuildableName = "DaysUntilBirthday(iOS).app"
+               BlueprintName = "DaysUntilBirthday(iOS)"
+               ReferencedContainer = "container:DaysUntilBirthday.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7345ACFE2703D9470020AFB1"
+            BuildableName = "DaysUntilBirthday(iOS).app"
+            BlueprintName = "DaysUntilBirthday(iOS)"
+            ReferencedContainer = "container:DaysUntilBirthday.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7345ACFE2703D9470020AFB1"
+            BuildableName = "DaysUntilBirthday(iOS).app"
+            BlueprintName = "DaysUntilBirthday(iOS)"
+            ReferencedContainer = "container:DaysUntilBirthday.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 78 - 0
Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/xcshareddata/xcschemes/DaysUntilBirthday(macOS).xcscheme

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1310"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FE71737F27ECFAF400910319"
+               BuildableName = "DaysUntilBirthday(macOS).app"
+               BlueprintName = "DaysUntilBirthday(macOS)"
+               ReferencedContainer = "container:DaysUntilBirthday.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "FE71737F27ECFAF400910319"
+            BuildableName = "DaysUntilBirthday(macOS).app"
+            BlueprintName = "DaysUntilBirthday(macOS)"
+            ReferencedContainer = "container:DaysUntilBirthday.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "FE71737F27ECFAF400910319"
+            BuildableName = "DaysUntilBirthday(macOS).app"
+            BlueprintName = "DaysUntilBirthday(macOS)"
+            ReferencedContainer = "container:DaysUntilBirthday.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 417 - 149
Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayForPod.xcodeproj/project.pbxproj

@@ -3,198 +3,322 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 50;
+	objectVersion = 55;
 	objects = {
 
 /* Begin PBXBuildFile section */
-		6D820AE53F1E83F8D828D207 /* libPods-DaysUntilBirthdayForPod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D224E5A98AD4723D3CDC222 /* libPods-DaysUntilBirthdayForPod.a */; };
-		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 */; };
-		7345AD0A2703D9480020AFB1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7345AD092703D9480020AFB1 /* Preview Assets.xcassets */; };
-		7345AD1B2703D9C30020AFB1 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD112703D9C30020AFB1 /* SignInView.swift */; };
-		7345AD1D2703D9C30020AFB1 /* GoogleSignInButtonWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD132703D9C30020AFB1 /* GoogleSignInButtonWrapper.swift */; };
-		7345AD1E2703D9C30020AFB1 /* UserProfileImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */; };
-		7345AD202703D9C30020AFB1 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */; };
-		7345AD212703D9C30020AFB1 /* GoogleSignInAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */; };
-		7345AD232703D9C30020AFB1 /* UserProfileImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */; };
-		7345AD242703D9C30020AFB1 /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345AD1A2703D9C30020AFB1 /* UserProfileView.swift */; };
-		736F49BA270E05E200580053 /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736F49B9270E05E200580053 /* BirthdayLoader.swift */; };
-		736F49BC270E102C00580053 /* BirthdayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736F49BB270E102C00580053 /* BirthdayViewModel.swift */; };
-		739FCC46270E467600C92042 /* BirthdayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739FCC45270E467600C92042 /* BirthdayView.swift */; };
-		739FCC48270E659A00C92042 /* Birthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739FCC47270E659A00C92042 /* Birthday.swift */; };
+		A0027D4EA9FC9C3320D2CB6C /* libPods-DaysUntilBirthdayForPod (iOS).a in Frameworks */ = {isa = PBXBuildFile; fileRef = 61F4ED33051343907BD064C7 /* libPods-DaysUntilBirthdayForPod (iOS).a */; };
+		FC5D406670D7A548AFC4E510 /* libPods-DaysUntilBirthdayForPod (macOS).a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E7FF39A72A801922A879753 /* libPods-DaysUntilBirthdayForPod (macOS).a */; };
+		FE2ED0762800FF54006007BA /* DaysUntilBirthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0702800FF54006007BA /* DaysUntilBirthday.swift */; };
+		FE2ED0772800FF54006007BA /* DaysUntilBirthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0702800FF54006007BA /* DaysUntilBirthday.swift */; };
+		FE2ED0782800FF54006007BA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FE2ED0712800FF54006007BA /* Assets.xcassets */; };
+		FE2ED0792800FF54006007BA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FE2ED0712800FF54006007BA /* Assets.xcassets */; };
+		FE2ED0872800FF74006007BA /* Preview Content in Resources */ = {isa = PBXBuildFile; fileRef = FE2ED0822800FF74006007BA /* Preview Content */; };
+		FE2ED0882800FF74006007BA /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = FE2ED0832800FF74006007BA /* Info.plist */; };
+		FE2ED0892800FF74006007BA /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0842800FF74006007BA /* UserProfileView.swift */; };
+		FE2ED08A2800FF74006007BA /* GoogleSignInButtonWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0852800FF74006007BA /* GoogleSignInButtonWrapper.swift */; };
+		FE2ED09428010015006007BA /* GoogleSignInButtonWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED09028010015006007BA /* GoogleSignInButtonWrapper.swift */; };
+		FE2ED09528010015006007BA /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED09128010015006007BA /* UserProfileView.swift */; };
+		FE2ED09628010015006007BA /* Preview Content in Resources */ = {isa = PBXBuildFile; fileRef = FE2ED09228010015006007BA /* Preview Content */; };
+		FE2ED09828010096006007BA /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = FE2ED09728010096006007BA /* Podfile */; };
+		FE2ED09928010096006007BA /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = FE2ED09728010096006007BA /* Podfile */; };
+		FE2ED0AA28010508006007BA /* BirthdayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED09D28010507006007BA /* BirthdayViewModel.swift */; };
+		FE2ED0AB28010508006007BA /* BirthdayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED09D28010507006007BA /* BirthdayViewModel.swift */; };
+		FE2ED0AC28010508006007BA /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED09E28010507006007BA /* AuthenticationViewModel.swift */; };
+		FE2ED0AD28010508006007BA /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED09E28010507006007BA /* AuthenticationViewModel.swift */; };
+		FE2ED0AE28010508006007BA /* Birthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A028010507006007BA /* Birthday.swift */; };
+		FE2ED0AF28010508006007BA /* Birthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A028010507006007BA /* Birthday.swift */; };
+		FE2ED0B028010508006007BA /* UserProfileImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A228010507006007BA /* UserProfileImageLoader.swift */; };
+		FE2ED0B128010508006007BA /* UserProfileImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A228010507006007BA /* UserProfileImageLoader.swift */; };
+		FE2ED0B228010508006007BA /* GoogleSignInAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A328010507006007BA /* GoogleSignInAuthenticator.swift */; };
+		FE2ED0B328010508006007BA /* GoogleSignInAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A328010507006007BA /* GoogleSignInAuthenticator.swift */; };
+		FE2ED0B428010508006007BA /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A428010507006007BA /* BirthdayLoader.swift */; };
+		FE2ED0B528010508006007BA /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A428010507006007BA /* BirthdayLoader.swift */; };
+		FE2ED0B628010508006007BA /* BirthdayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A628010508006007BA /* BirthdayView.swift */; };
+		FE2ED0B728010508006007BA /* BirthdayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A628010508006007BA /* BirthdayView.swift */; };
+		FE2ED0B828010508006007BA /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A728010508006007BA /* SignInView.swift */; };
+		FE2ED0B928010508006007BA /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A728010508006007BA /* SignInView.swift */; };
+		FE2ED0BA28010508006007BA /* UserProfileImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A828010508006007BA /* UserProfileImageView.swift */; };
+		FE2ED0BB28010508006007BA /* UserProfileImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A828010508006007BA /* UserProfileImageView.swift */; };
+		FE2ED0BC28010508006007BA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A928010508006007BA /* ContentView.swift */; };
+		FE2ED0BD28010508006007BA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2ED0A928010508006007BA /* ContentView.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
-		7345ACFF2703D9470020AFB1 /* DaysUntilBirthdayForPod.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DaysUntilBirthdayForPod.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>"; };
-		7345AD062703D9480020AFB1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
-		7345AD092703D9480020AFB1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
-		7345AD0B2703D9480020AFB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		7345AD112703D9C30020AFB1 /* SignInView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; };
-		7345AD132703D9C30020AFB1 /* GoogleSignInButtonWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleSignInButtonWrapper.swift; sourceTree = "<group>"; };
-		7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfileImageView.swift; sourceTree = "<group>"; };
-		7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
-		7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleSignInAuthenticator.swift; sourceTree = "<group>"; };
-		7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfileImageLoader.swift; sourceTree = "<group>"; };
-		7345AD1A2703D9C30020AFB1 /* UserProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = "<group>"; };
-		736F49B9270E05E200580053 /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = "<group>"; };
-		736F49BB270E102C00580053 /* BirthdayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayViewModel.swift; sourceTree = "<group>"; };
-		739FCC45270E467600C92042 /* BirthdayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayView.swift; sourceTree = "<group>"; };
-		739FCC47270E659A00C92042 /* Birthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Birthday.swift; sourceTree = "<group>"; };
-		9D224E5A98AD4723D3CDC222 /* libPods-DaysUntilBirthdayForPod.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DaysUntilBirthdayForPod.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		CE1D65F7694C48ED17C48C7A /* Pods-DaysUntilBirthdayForPod.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DaysUntilBirthdayForPod.release.xcconfig"; path = "Target Support Files/Pods-DaysUntilBirthdayForPod/Pods-DaysUntilBirthdayForPod.release.xcconfig"; sourceTree = "<group>"; };
-		EEB3D2312581D01EAB21E02A /* Pods-DaysUntilBirthdayForPod.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DaysUntilBirthdayForPod.debug.xcconfig"; path = "Target Support Files/Pods-DaysUntilBirthdayForPod/Pods-DaysUntilBirthdayForPod.debug.xcconfig"; sourceTree = "<group>"; };
+		0B08EF288921DB186EA58624 /* 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>"; };
+		11B3DE96F8E9E52403BA1C30 /* Pods-DaysUntilBirthdayForPod (iOS).release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DaysUntilBirthdayForPod (iOS).release.xcconfig"; path = "Target Support Files/Pods-DaysUntilBirthdayForPod (iOS)/Pods-DaysUntilBirthdayForPod (iOS).release.xcconfig"; sourceTree = "<group>"; };
+		54C0CE39088F529ED3F7DB46 /* 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>"; };
+		61F4ED33051343907BD064C7 /* libPods-DaysUntilBirthdayForPod (iOS).a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DaysUntilBirthdayForPod (iOS).a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		6E7FF39A72A801922A879753 /* libPods-DaysUntilBirthdayForPod (macOS).a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DaysUntilBirthdayForPod (macOS).a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		752CE6A223C1E34534585077 /* Pods-DaysUntilBirthdayForPod (macOS).debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DaysUntilBirthdayForPod (macOS).debug.xcconfig"; path = "Target Support Files/Pods-DaysUntilBirthdayForPod (macOS)/Pods-DaysUntilBirthdayForPod (macOS).debug.xcconfig"; sourceTree = "<group>"; };
+		FE2ED0592800FE0F006007BA /* DaysUntilBirthdayForPod.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DaysUntilBirthdayForPod.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		FE2ED05F2800FE0F006007BA /* DaysUntilBirthdayForPod.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DaysUntilBirthdayForPod.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		FE2ED0702800FF54006007BA /* DaysUntilBirthday.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DaysUntilBirthday.swift; sourceTree = "<group>"; };
+		FE2ED0712800FF54006007BA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		FE2ED0822800FF74006007BA /* Preview Content */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Preview Content"; sourceTree = "<group>"; };
+		FE2ED0832800FF74006007BA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		FE2ED0842800FF74006007BA /* UserProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = "<group>"; };
+		FE2ED0852800FF74006007BA /* GoogleSignInButtonWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleSignInButtonWrapper.swift; sourceTree = "<group>"; };
+		FE2ED0862800FF74006007BA /* DaysUntilBirthdayOnMac.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = DaysUntilBirthdayOnMac.entitlements; sourceTree = "<group>"; };
+		FE2ED08F28010015006007BA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = "<group>"; };
+		FE2ED09028010015006007BA /* GoogleSignInButtonWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GoogleSignInButtonWrapper.swift; path = iOS/GoogleSignInButtonWrapper.swift; sourceTree = "<group>"; };
+		FE2ED09128010015006007BA /* UserProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UserProfileView.swift; path = iOS/UserProfileView.swift; sourceTree = "<group>"; };
+		FE2ED09228010015006007BA /* Preview Content */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Preview Content"; path = "iOS/Preview Content"; sourceTree = "<group>"; };
+		FE2ED09728010096006007BA /* Podfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+		FE2ED09D28010507006007BA /* BirthdayViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BirthdayViewModel.swift; sourceTree = "<group>"; };
+		FE2ED09E28010507006007BA /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
+		FE2ED0A028010507006007BA /* Birthday.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Birthday.swift; sourceTree = "<group>"; };
+		FE2ED0A228010507006007BA /* UserProfileImageLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfileImageLoader.swift; sourceTree = "<group>"; };
+		FE2ED0A328010507006007BA /* GoogleSignInAuthenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleSignInAuthenticator.swift; sourceTree = "<group>"; };
+		FE2ED0A428010507006007BA /* BirthdayLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = "<group>"; };
+		FE2ED0A628010508006007BA /* BirthdayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BirthdayView.swift; sourceTree = "<group>"; };
+		FE2ED0A728010508006007BA /* SignInView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; };
+		FE2ED0A828010508006007BA /* UserProfileImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfileImageView.swift; sourceTree = "<group>"; };
+		FE2ED0A928010508006007BA /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
-		7345ACFC2703D9470020AFB1 /* Frameworks */ = {
+		FE2ED0562800FE0F006007BA /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				6D820AE53F1E83F8D828D207 /* libPods-DaysUntilBirthdayForPod.a in Frameworks */,
+				A0027D4EA9FC9C3320D2CB6C /* libPods-DaysUntilBirthdayForPod (iOS).a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		FE2ED05C2800FE0F006007BA /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				FC5D406670D7A548AFC4E510 /* libPods-DaysUntilBirthdayForPod (macOS).a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
-		7345ACF62703D9470020AFB1 = {
+		2D587383CDC18F70FA2636BF /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				61F4ED33051343907BD064C7 /* libPods-DaysUntilBirthdayForPod (iOS).a */,
+				6E7FF39A72A801922A879753 /* libPods-DaysUntilBirthdayForPod (macOS).a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		606AFE1D7495419E24EF0F63 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				0B08EF288921DB186EA58624 /* Pods-DaysUntilBirthdayForPod (iOS).debug.xcconfig */,
+				11B3DE96F8E9E52403BA1C30 /* Pods-DaysUntilBirthdayForPod (iOS).release.xcconfig */,
+				752CE6A223C1E34534585077 /* Pods-DaysUntilBirthdayForPod (macOS).debug.xcconfig */,
+				54C0CE39088F529ED3F7DB46 /* Pods-DaysUntilBirthdayForPod (macOS).release.xcconfig */,
+			);
+			path = Pods;
+			sourceTree = "<group>";
+		};
+		FE2ED04C2800FE0D006007BA = {
 			isa = PBXGroup;
 			children = (
-				7345AD012703D9470020AFB1 /* Sources */,
-				7345AD002703D9470020AFB1 /* Products */,
-				B8016955C403553EF7E5C086 /* Pods */,
-				C3E2C8C89963A77B9020C2FF /* Frameworks */,
+				FE2ED09728010096006007BA /* Podfile */,
+				FE2ED0512800FE0D006007BA /* Shared */,
+				FE2ED08E28010002006007BA /* iOS */,
+				FE2ED0602800FE0F006007BA /* macOS */,
+				FE2ED05A2800FE0F006007BA /* Products */,
+				606AFE1D7495419E24EF0F63 /* Pods */,
+				2D587383CDC18F70FA2636BF /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
-		7345AD002703D9470020AFB1 /* Products */ = {
+		FE2ED0512800FE0D006007BA /* Shared */ = {
 			isa = PBXGroup;
 			children = (
-				7345ACFF2703D9470020AFB1 /* DaysUntilBirthdayForPod.app */,
+				FE2ED09F28010507006007BA /* Models */,
+				FE2ED0A128010507006007BA /* Services */,
+				FE2ED09C28010507006007BA /* ViewModels */,
+				FE2ED0A528010508006007BA /* Views */,
+				FE2ED0712800FF54006007BA /* Assets.xcassets */,
+				FE2ED0702800FF54006007BA /* DaysUntilBirthday.swift */,
+			);
+			path = Shared;
+			sourceTree = "<group>";
+		};
+		FE2ED05A2800FE0F006007BA /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				FE2ED0592800FE0F006007BA /* DaysUntilBirthdayForPod.app */,
+				FE2ED05F2800FE0F006007BA /* DaysUntilBirthdayForPod.app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
-		7345AD012703D9470020AFB1 /* Sources */ = {
+		FE2ED0602800FE0F006007BA /* macOS */ = {
 			isa = PBXGroup;
 			children = (
-				7345AD022703D9470020AFB1 /* DaysUntilBirthday.swift */,
-				7345AD042703D9470020AFB1 /* ContentView.swift */,
-				7345AD162703D9C30020AFB1 /* AuthenticationViewModel.swift */,
-				736F49B9270E05E200580053 /* BirthdayLoader.swift */,
-				736F49BB270E102C00580053 /* BirthdayViewModel.swift */,
-				739FCC47270E659A00C92042 /* Birthday.swift */,
-				739FCC45270E467600C92042 /* BirthdayView.swift */,
-				7345AD172703D9C30020AFB1 /* GoogleSignInAuthenticator.swift */,
-				7345AD132703D9C30020AFB1 /* GoogleSignInButtonWrapper.swift */,
-				7345AD112703D9C30020AFB1 /* SignInView.swift */,
-				7345AD192703D9C30020AFB1 /* UserProfileImageLoader.swift */,
-				7345AD142703D9C30020AFB1 /* UserProfileImageView.swift */,
-				7345AD1A2703D9C30020AFB1 /* UserProfileView.swift */,
-				7345AD062703D9480020AFB1 /* Assets.xcassets */,
-				7345AD0B2703D9480020AFB1 /* Info.plist */,
-				7345AD082703D9480020AFB1 /* Preview Content */,
-			);
-			path = Sources;
+				FE2ED0862800FF74006007BA /* DaysUntilBirthdayOnMac.entitlements */,
+				FE2ED0852800FF74006007BA /* GoogleSignInButtonWrapper.swift */,
+				FE2ED0832800FF74006007BA /* Info.plist */,
+				FE2ED0822800FF74006007BA /* Preview Content */,
+				FE2ED0842800FF74006007BA /* UserProfileView.swift */,
+			);
+			path = macOS;
 			sourceTree = "<group>";
 		};
-		7345AD082703D9480020AFB1 /* Preview Content */ = {
+		FE2ED08E28010002006007BA /* iOS */ = {
 			isa = PBXGroup;
 			children = (
-				7345AD092703D9480020AFB1 /* Preview Assets.xcassets */,
+				FE2ED09028010015006007BA /* GoogleSignInButtonWrapper.swift */,
+				FE2ED08F28010015006007BA /* Info.plist */,
+				FE2ED09228010015006007BA /* Preview Content */,
+				FE2ED09128010015006007BA /* UserProfileView.swift */,
 			);
-			path = "Preview Content";
+			name = iOS;
 			sourceTree = "<group>";
 		};
-		B8016955C403553EF7E5C086 /* Pods */ = {
+		FE2ED09C28010507006007BA /* ViewModels */ = {
 			isa = PBXGroup;
 			children = (
-				EEB3D2312581D01EAB21E02A /* Pods-DaysUntilBirthdayForPod.debug.xcconfig */,
-				CE1D65F7694C48ED17C48C7A /* Pods-DaysUntilBirthdayForPod.release.xcconfig */,
+				FE2ED09D28010507006007BA /* BirthdayViewModel.swift */,
+				FE2ED09E28010507006007BA /* AuthenticationViewModel.swift */,
 			);
-			name = Pods;
-			path = Pods;
+			path = ViewModels;
 			sourceTree = "<group>";
 		};
-		C3E2C8C89963A77B9020C2FF /* Frameworks */ = {
+		FE2ED09F28010507006007BA /* Models */ = {
 			isa = PBXGroup;
 			children = (
-				9D224E5A98AD4723D3CDC222 /* libPods-DaysUntilBirthdayForPod.a */,
+				FE2ED0A028010507006007BA /* Birthday.swift */,
 			);
-			name = Frameworks;
+			path = Models;
+			sourceTree = "<group>";
+		};
+		FE2ED0A128010507006007BA /* Services */ = {
+			isa = PBXGroup;
+			children = (
+				FE2ED0A228010507006007BA /* UserProfileImageLoader.swift */,
+				FE2ED0A328010507006007BA /* GoogleSignInAuthenticator.swift */,
+				FE2ED0A428010507006007BA /* BirthdayLoader.swift */,
+			);
+			path = Services;
+			sourceTree = "<group>";
+		};
+		FE2ED0A528010508006007BA /* Views */ = {
+			isa = PBXGroup;
+			children = (
+				FE2ED0A628010508006007BA /* BirthdayView.swift */,
+				FE2ED0A728010508006007BA /* SignInView.swift */,
+				FE2ED0A828010508006007BA /* UserProfileImageView.swift */,
+				FE2ED0A928010508006007BA /* ContentView.swift */,
+			);
+			path = Views;
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		7345ACFE2703D9470020AFB1 /* DaysUntilBirthdayForPod */ = {
+		FE2ED0582800FE0F006007BA /* DaysUntilBirthdayForPod (iOS) */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod" */;
+			buildConfigurationList = FE2ED06A2800FE0F006007BA /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod (iOS)" */;
 			buildPhases = (
-				5C126313BC6F0229B5B245F4 /* [CP] Check Pods Manifest.lock */,
-				7345ACFB2703D9470020AFB1 /* Sources */,
-				7345ACFC2703D9470020AFB1 /* Frameworks */,
-				7345ACFD2703D9470020AFB1 /* Resources */,
-				FCBC4E621013094E19A4D011 /* [CP] Copy Pods Resources */,
+				E5281C4FB6891AEEF4D98F34 /* [CP] Check Pods Manifest.lock */,
+				FE2ED0552800FE0F006007BA /* Sources */,
+				FE2ED0562800FE0F006007BA /* Frameworks */,
+				FE2ED0572800FE0F006007BA /* Resources */,
+				3C2AF50DEF579CD4D20D7FB4 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
 			dependencies = (
 			);
-			name = DaysUntilBirthdayForPod;
-			productName = "GSI-Swift-Sample";
-			productReference = 7345ACFF2703D9470020AFB1 /* DaysUntilBirthdayForPod.app */;
+			name = "DaysUntilBirthdayForPod (iOS)";
+			productName = "DaysUntilBirthdayForPod (iOS)";
+			productReference = FE2ED0592800FE0F006007BA /* DaysUntilBirthdayForPod.app */;
+			productType = "com.apple.product-type.application";
+		};
+		FE2ED05E2800FE0F006007BA /* DaysUntilBirthdayForPod (macOS) */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = FE2ED06D2800FE0F006007BA /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod (macOS)" */;
+			buildPhases = (
+				09125C10B78DB2C9C3CDD745 /* [CP] Check Pods Manifest.lock */,
+				FE2ED05B2800FE0F006007BA /* Sources */,
+				FE2ED05C2800FE0F006007BA /* Frameworks */,
+				FE2ED05D2800FE0F006007BA /* Resources */,
+				695CEEB6F1EA80C63A6FBE3D /* [CP] Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "DaysUntilBirthdayForPod (macOS)";
+			productName = "DaysUntilBirthdayForPod (macOS)";
+			productReference = FE2ED05F2800FE0F006007BA /* DaysUntilBirthdayForPod.app */;
 			productType = "com.apple.product-type.application";
 		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
-		7345ACF72703D9470020AFB1 /* Project object */ = {
+		FE2ED04D2800FE0D006007BA /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastSwiftUpdateCheck = 1250;
-				LastUpgradeCheck = 1250;
+				BuildIndependentTargetsInParallel = 1;
+				LastSwiftUpdateCheck = 1310;
+				LastUpgradeCheck = 1310;
 				TargetAttributes = {
-					7345ACFE2703D9470020AFB1 = {
-						CreatedOnToolsVersion = 12.5.1;
+					FE2ED0582800FE0F006007BA = {
+						CreatedOnToolsVersion = 13.1;
+						LastSwiftMigration = 1310;
+					};
+					FE2ED05E2800FE0F006007BA = {
+						CreatedOnToolsVersion = 13.1;
+						LastSwiftMigration = 1310;
 					};
 				};
 			};
-			buildConfigurationList = 7345ACFA2703D9470020AFB1 /* Build configuration list for PBXProject "DaysUntilBirthdayForPod" */;
-			compatibilityVersion = "Xcode 9.3";
+			buildConfigurationList = FE2ED0502800FE0D006007BA /* Build configuration list for PBXProject "DaysUntilBirthdayForPod" */;
+			compatibilityVersion = "Xcode 13.0";
 			developmentRegion = en;
 			hasScannedForEncodings = 0;
 			knownRegions = (
 				en,
 				Base,
 			);
-			mainGroup = 7345ACF62703D9470020AFB1;
-			productRefGroup = 7345AD002703D9470020AFB1 /* Products */;
+			mainGroup = FE2ED04C2800FE0D006007BA;
+			productRefGroup = FE2ED05A2800FE0F006007BA /* Products */;
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				7345ACFE2703D9470020AFB1 /* DaysUntilBirthdayForPod */,
+				FE2ED0582800FE0F006007BA /* DaysUntilBirthdayForPod (iOS) */,
+				FE2ED05E2800FE0F006007BA /* DaysUntilBirthdayForPod (macOS) */,
 			);
 		};
 /* End PBXProject section */
 
 /* Begin PBXResourcesBuildPhase section */
-		7345ACFD2703D9470020AFB1 /* Resources */ = {
+		FE2ED0572800FE0F006007BA /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				FE2ED09828010096006007BA /* Podfile in Resources */,
+				FE2ED09628010015006007BA /* Preview Content in Resources */,
+				FE2ED0782800FF54006007BA /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		FE2ED05D2800FE0F006007BA /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				7345AD0A2703D9480020AFB1 /* Preview Assets.xcassets in Resources */,
-				7345AD072703D9480020AFB1 /* Assets.xcassets in Resources */,
+				FE2ED0872800FF74006007BA /* Preview Content in Resources */,
+				FE2ED09928010096006007BA /* Podfile in Resources */,
+				FE2ED0792800FF54006007BA /* Assets.xcassets in Resources */,
+				FE2ED0882800FF74006007BA /* Info.plist in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		5C126313BC6F0229B5B245F4 /* [CP] Check Pods Manifest.lock */ = {
+		09125C10B78DB2C9C3CDD745 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -209,63 +333,122 @@
 			outputFileListPaths = (
 			);
 			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-DaysUntilBirthdayForPod-checkManifestLockResult.txt",
+				"$(DERIVED_FILE_DIR)/Pods-DaysUntilBirthdayForPod (macOS)-checkManifestLockResult.txt",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		FCBC4E621013094E19A4D011 /* [CP] Copy Pods Resources */ = {
+		3C2AF50DEF579CD4D20D7FB4 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputFileListPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod/Pods-DaysUntilBirthdayForPod-resources-${CONFIGURATION}-input-files.xcfilelist",
+				"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod (iOS)/Pods-DaysUntilBirthdayForPod (iOS)-resources-${CONFIGURATION}-input-files.xcfilelist",
 			);
 			name = "[CP] Copy Pods Resources";
 			outputFileListPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod/Pods-DaysUntilBirthdayForPod-resources-${CONFIGURATION}-output-files.xcfilelist",
+				"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod (iOS)/Pods-DaysUntilBirthdayForPod (iOS)-resources-${CONFIGURATION}-output-files.xcfilelist",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod/Pods-DaysUntilBirthdayForPod-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod (iOS)/Pods-DaysUntilBirthdayForPod (iOS)-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		695CEEB6F1EA80C63A6FBE3D /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod (macOS)/Pods-DaysUntilBirthdayForPod (macOS)-resources-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Copy Pods Resources";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod (macOS)/Pods-DaysUntilBirthdayForPod (macOS)-resources-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod (macOS)/Pods-DaysUntilBirthdayForPod (macOS)-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		E5281C4FB6891AEEF4D98F34 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-DaysUntilBirthdayForPod (iOS)-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
-		7345ACFB2703D9470020AFB1 /* Sources */ = {
+		FE2ED0552800FE0F006007BA /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				739FCC48270E659A00C92042 /* Birthday.swift in Sources */,
-				739FCC46270E467600C92042 /* BirthdayView.swift in Sources */,
-				7345AD1B2703D9C30020AFB1 /* SignInView.swift in Sources */,
-				7345AD212703D9C30020AFB1 /* GoogleSignInAuthenticator.swift in Sources */,
-				7345AD232703D9C30020AFB1 /* UserProfileImageLoader.swift in Sources */,
-				7345AD1E2703D9C30020AFB1 /* UserProfileImageView.swift in Sources */,
-				736F49BC270E102C00580053 /* BirthdayViewModel.swift in Sources */,
-				736F49BA270E05E200580053 /* BirthdayLoader.swift in Sources */,
-				7345AD242703D9C30020AFB1 /* UserProfileView.swift in Sources */,
-				7345AD202703D9C30020AFB1 /* AuthenticationViewModel.swift in Sources */,
-				7345AD052703D9470020AFB1 /* ContentView.swift in Sources */,
-				7345AD1D2703D9C30020AFB1 /* GoogleSignInButtonWrapper.swift in Sources */,
-				7345AD032703D9470020AFB1 /* DaysUntilBirthday.swift in Sources */,
+				FE2ED0B428010508006007BA /* BirthdayLoader.swift in Sources */,
+				FE2ED09428010015006007BA /* GoogleSignInButtonWrapper.swift in Sources */,
+				FE2ED0AC28010508006007BA /* AuthenticationViewModel.swift in Sources */,
+				FE2ED0B228010508006007BA /* GoogleSignInAuthenticator.swift in Sources */,
+				FE2ED0AE28010508006007BA /* Birthday.swift in Sources */,
+				FE2ED0B628010508006007BA /* BirthdayView.swift in Sources */,
+				FE2ED0AA28010508006007BA /* BirthdayViewModel.swift in Sources */,
+				FE2ED0BA28010508006007BA /* UserProfileImageView.swift in Sources */,
+				FE2ED09528010015006007BA /* UserProfileView.swift in Sources */,
+				FE2ED0B028010508006007BA /* UserProfileImageLoader.swift in Sources */,
+				FE2ED0BC28010508006007BA /* ContentView.swift in Sources */,
+				FE2ED0B828010508006007BA /* SignInView.swift in Sources */,
+				FE2ED0762800FF54006007BA /* DaysUntilBirthday.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		FE2ED05B2800FE0F006007BA /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				FE2ED0B528010508006007BA /* BirthdayLoader.swift in Sources */,
+				FE2ED08A2800FF74006007BA /* GoogleSignInButtonWrapper.swift in Sources */,
+				FE2ED0AD28010508006007BA /* AuthenticationViewModel.swift in Sources */,
+				FE2ED0B328010508006007BA /* GoogleSignInAuthenticator.swift in Sources */,
+				FE2ED0AF28010508006007BA /* Birthday.swift in Sources */,
+				FE2ED0B728010508006007BA /* BirthdayView.swift in Sources */,
+				FE2ED0AB28010508006007BA /* BirthdayViewModel.swift in Sources */,
+				FE2ED0BB28010508006007BA /* UserProfileImageView.swift in Sources */,
+				FE2ED0772800FF54006007BA /* DaysUntilBirthday.swift in Sources */,
+				FE2ED0B128010508006007BA /* UserProfileImageLoader.swift in Sources */,
+				FE2ED0BD28010508006007BA /* ContentView.swift in Sources */,
+				FE2ED0B928010508006007BA /* SignInView.swift in Sources */,
+				FE2ED0892800FF74006007BA /* UserProfileView.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
-		7345AD0C2703D9480020AFB1 /* Debug */ = {
+		FE2ED0682800FE0F006007BA /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
@@ -310,23 +493,21 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
-				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 			};
 			name = Debug;
 		};
-		7345AD0D2703D9480020AFB1 /* Release */ = {
+		FE2ED0692800FE0F006007BA /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
@@ -365,82 +546,169 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
-				SDKROOT = iphoneos;
 				SWIFT_COMPILATION_MODE = wholemodule;
 				SWIFT_OPTIMIZATION_LEVEL = "-O";
-				VALIDATE_PRODUCT = YES;
 			};
 			name = Release;
 		};
-		7345AD0F2703D9480020AFB1 /* Debug */ = {
+		FE2ED06B2800FE0F006007BA /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = EEB3D2312581D01EAB21E02A /* Pods-DaysUntilBirthdayForPod.debug.xcconfig */;
+			baseConfigurationReference = 0B08EF288921DB186EA58624 /* Pods-DaysUntilBirthdayForPod (iOS).debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				CURRENT_PROJECT_VERSION = 1;
 				ENABLE_PREVIEWS = YES;
-				INFOPLIST_FILE = Sources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = iOS/Info.plist;
+				INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+				INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthdayForPod;
-				PRODUCT_NAME = "$(TARGET_NAME)";
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
+				PRODUCT_NAME = DaysUntilBirthdayForPod;
+				SDKROOT = iphoneos;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Debug;
 		};
-		7345AD102703D9480020AFB1 /* Release */ = {
+		FE2ED06C2800FE0F006007BA /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = CE1D65F7694C48ED17C48C7A /* Pods-DaysUntilBirthdayForPod.release.xcconfig */;
+			baseConfigurationReference = 11B3DE96F8E9E52403BA1C30 /* Pods-DaysUntilBirthdayForPod (iOS).release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				CURRENT_PROJECT_VERSION = 1;
 				ENABLE_PREVIEWS = YES;
-				INFOPLIST_FILE = Sources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = iOS/Info.plist;
+				INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+				INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthdayForPod;
-				PRODUCT_NAME = "$(TARGET_NAME)";
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
+				PRODUCT_NAME = DaysUntilBirthdayForPod;
+				SDKROOT = iphoneos;
+				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		FE2ED06E2800FE0F006007BA /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 752CE6A223C1E34534585077 /* Pods-DaysUntilBirthdayForPod (macOS).debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				CURRENT_PROJECT_VERSION = 1;
+				ENABLE_PREVIEWS = YES;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = macOS/Info.plist;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 12.0;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
+				PRODUCT_NAME = DaysUntilBirthdayForPod;
+				SDKROOT = macosx;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+			};
+			name = Debug;
+		};
+		FE2ED06F2800FE0F006007BA /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 54C0CE39088F529ED3F7DB46 /* Pods-DaysUntilBirthdayForPod (macOS).release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				CURRENT_PROJECT_VERSION = 1;
+				ENABLE_PREVIEWS = YES;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = macOS/Info.plist;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 12.0;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
+				PRODUCT_NAME = DaysUntilBirthdayForPod;
+				SDKROOT = macosx;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
 			};
 			name = Release;
 		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
-		7345ACFA2703D9470020AFB1 /* Build configuration list for PBXProject "DaysUntilBirthdayForPod" */ = {
+		FE2ED0502800FE0D006007BA /* Build configuration list for PBXProject "DaysUntilBirthdayForPod" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				FE2ED0682800FE0F006007BA /* Debug */,
+				FE2ED0692800FE0F006007BA /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		FE2ED06A2800FE0F006007BA /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod (iOS)" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				7345AD0C2703D9480020AFB1 /* Debug */,
-				7345AD0D2703D9480020AFB1 /* Release */,
+				FE2ED06B2800FE0F006007BA /* Debug */,
+				FE2ED06C2800FE0F006007BA /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod" */ = {
+		FE2ED06D2800FE0F006007BA /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod (macOS)" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				7345AD0F2703D9480020AFB1 /* Debug */,
-				7345AD102703D9480020AFB1 /* Release */,
+				FE2ED06E2800FE0F006007BA /* Debug */,
+				FE2ED06F2800FE0F006007BA /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
 /* End XCConfigurationList section */
 	};
-	rootObject = 7345ACF72703D9470020AFB1 /* Project object */;
+	rootObject = FE2ED04D2800FE0D006007BA /* Project object */;
 }

+ 8 - 4
Samples/Swift/DaysUntilBirthday/Podfile

@@ -1,6 +1,10 @@
-platform :ios, '9.0'
+pod 'GoogleSignIn', :path => '../../../', :testspecs => ['unit']
+project 'DaysUntilBirthdayForPod.xcodeproj'
 
-target 'DaysUntilBirthdayForPod' do
-  pod 'GoogleSignIn', :path => '../../../', :testspecs => ['unit']
-  project 'DaysUntilBirthdayForPod.xcodeproj'
+target 'DaysUntilBirthdayForPod (iOS)' do
+  platform :ios, '9.0'
+end
+
+target 'DaysUntilBirthdayForPod (macOS)' do
+  platform :osx, '10.15'
 end

+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AccentColor.colorset/Contents.json → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AccentColor.colorset/Contents.json


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/Icon.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/Icon.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_76pt.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_76pt.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/Contents.json → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/Contents.json


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/PlaceholderAvatar.imageset/Contents.json → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/PlaceholderAvatar.imageset/Contents.json


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/PlaceholderAvatar.imageset/PlaceholderAvatar.png → Samples/Swift/DaysUntilBirthday/Shared/Assets.xcassets/PlaceholderAvatar.imageset/PlaceholderAvatar.png


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/DaysUntilBirthday.swift → Samples/Swift/DaysUntilBirthday/Shared/DaysUntilBirthday.swift


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Birthday.swift → Samples/Swift/DaysUntilBirthday/Shared/Models/Birthday.swift


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/BirthdayLoader.swift → Samples/Swift/DaysUntilBirthday/Shared/Services/BirthdayLoader.swift


+ 61 - 16
Samples/Swift/DaysUntilBirthday/Sources/GoogleSignInAuthenticator.swift → Samples/Swift/DaysUntilBirthday/Shared/Services/GoogleSignInAuthenticator.swift

@@ -20,7 +20,11 @@ import GoogleSignIn
 /// An observable class for authenticating via Google.
 final class GoogleSignInAuthenticator: ObservableObject {
   // TODO: Replace this with your own ID.
+  #if os(iOS)
   private let clientID = "687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com"
+  #elseif os(macOS)
+  private let clientID = "687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com"
+  #endif
 
   private lazy var configuration: GIDConfiguration = {
     return GIDConfiguration(clientID: clientID)
@@ -37,19 +41,38 @@ final class GoogleSignInAuthenticator: ObservableObject {
   /// Signs in the user based upon the selected account.'
   /// - note: Successful calls to this will set the `authViewModel`'s `state` property.
   func signIn() {
-    guard let rootViewController = UIApplication.shared.windows.first?.rootViewController else {
-      print("There is no root view controller!")
-      return
-    }
-    GIDSignIn.sharedInstance.signIn(with: configuration,
-                                    presenting: rootViewController) { user, error in
-      guard let user = user else {
-        print("Error! \(String(describing: error))")
+      #if os(iOS)
+      guard let rootViewController = UIApplication.shared.windows.first?.rootViewController else {
+        print("There is no root view controller!")
+        return
+      }
+
+      GIDSignIn.sharedInstance.signIn(with: configuration,
+                                      presenting: rootViewController) { user, error in
+        guard let user = user else {
+          print("Error! \(String(describing: error))")
+          return
+        }
+        self.authViewModel.state = .signedIn(user)
+      }
+
+      #elseif os(macOS)
+      guard let presentingWindow = NSApplication.shared.windows.first else {
+        print("There is no presenting window!")
         return
       }
-      self.authViewModel.state = .signedIn(user)
+
+      GIDSignIn.sharedInstance.signIn(with: configuration,
+                                      presenting: presentingWindow) { user, error in
+        guard let user = user else {
+          print("Error! \(String(describing: error))")
+          return
+        }
+        self.authViewModel.state = .signedIn(user)
+      }
+      #endif
+
     }
-  }
 
   /// Signs out the current user.
   func signOut() {
@@ -57,12 +80,24 @@ final class GoogleSignInAuthenticator: ObservableObject {
     authViewModel.state = .signedOut
   }
 
+  /// Disconnects the previously granted scope and signs the user out.
+  func disconnect() {
+    GIDSignIn.sharedInstance.disconnect { error in
+      if let error = error {
+        print("Encountered error disconnecting scope: \(error).")
+      }
+      self.signOut()
+    }
+  }
+
+  // Confines birthday calucation to iOS for now.
   /// Adds the birthday read scope for the current user.
   /// - parameter completion: An escaping closure that is called upon successful completion of the
   /// `addScopes(_:presenting:)` request.
   /// - note: Successful requests will update the `authViewModel.state` with a new current user that
   /// has the granted scope.
   func addBirthdayReadScope(completion: @escaping () -> Void) {
+    #if os(iOS)
     guard let rootViewController = UIApplication.shared.windows.first?.rootViewController else {
       fatalError("No root view controller!")
     }
@@ -78,15 +113,25 @@ final class GoogleSignInAuthenticator: ObservableObject {
       self.authViewModel.state = .signedIn(currentUser)
       completion()
     }
-  }
 
-  /// Disconnects the previously granted scope and signs the user out.
-  func disconnect() {
-    GIDSignIn.sharedInstance.disconnect { error in
+    #elseif os(macOS)
+    guard let presentingWindow = NSApplication.shared.windows.first else {
+      fatalError("No presenting window!")
+    }
+
+    GIDSignIn.sharedInstance.addScopes([BirthdayLoader.birthdayReadScope],
+                                       presenting: presentingWindow) { user, error in
       if let error = error {
-        print("Encountered error disconnecting scope: \(error).")
+        print("Found error while adding birthday read scope: \(error).")
+        return
       }
-      self.signOut()
+
+      guard let currentUser = user else { return }
+      self.authViewModel.state = .signedIn(currentUser)
+      completion()
     }
+
+    #endif
   }
+
 }

+ 12 - 2
Samples/Swift/DaysUntilBirthday/Sources/UserProfileImageLoader.swift → Samples/Swift/DaysUntilBirthday/Shared/Services/UserProfileImageLoader.swift

@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
+#if os(iOS)
+typealias GIDImage = UIImage
+#elseif os(macOS)
+typealias GIDImage = NSImage
+#endif
+
 import Combine
 import SwiftUI
 import GoogleSignIn
@@ -24,7 +30,7 @@ final class UserProfileImageLoader: ObservableObject {
   private let imageLoaderQueue = DispatchQueue(label: "com.google.days-until-birthday")
   /// A `UIImage` property containing the current user's profile image.
   /// - note: This will default to a placeholder, and updates will be published to subscribers.
-  @Published var image = UIImage(named: "PlaceholderAvatar")!
+  @Published var image = GIDImage(named: "PlaceholderAvatar")!
 
   /// Creates an instance of this loader with provided user profile.
   /// - note: The instance will asynchronously fetch the image data upon creation.
@@ -35,10 +41,14 @@ final class UserProfileImageLoader: ObservableObject {
     }
 
     imageLoaderQueue.async {
+      #if os(iOS)
       let dimension = 45 * UIScreen.main.scale
+      #elseif os(macOS)
+      let dimension = 120
+      #endif
       guard let url = userProfile.imageURL(withDimension: UInt(dimension)),
             let data = try? Data(contentsOf: url),
-            let image = UIImage(data: data) else {
+            let image = GIDImage(data: data) else {
         return
       }
       DispatchQueue.main.async {

+ 9 - 8
Samples/Swift/DaysUntilBirthday/Sources/AuthenticationViewModel.swift → Samples/Swift/DaysUntilBirthday/Shared/ViewModels/AuthenticationViewModel.swift

@@ -36,10 +36,6 @@ final class AuthenticationViewModel: ObservableObject {
     }
   }
 
-  var hasBirthdayReadScope: Bool {
-    return authorizedScopes.contains(BirthdayLoader.birthdayReadScope)
-  }
-
   /// Creates an instance of this view model.
   init() {
     if let user = GIDSignIn.sharedInstance.currentUser {
@@ -59,16 +55,21 @@ final class AuthenticationViewModel: ObservableObject {
     authenticator.signOut()
   }
 
+  /// Disconnects the previously granted scope and logs the user out.
+  func disconnect() {
+    authenticator.disconnect()
+  }
+
+  var hasBirthdayReadScope: Bool {
+    return authorizedScopes.contains(BirthdayLoader.birthdayReadScope)
+  }
+
   /// Adds the requested birthday read scope.
   /// - parameter completion: An escaping closure that is called upon successful completion.
   func addBirthdayReadScope(completion: @escaping () -> Void) {
     authenticator.addBirthdayReadScope(completion: completion)
   }
 
-  /// Disconnects the previously granted scope and logs the user out.
-  func disconnect() {
-    authenticator.disconnect()
-  }
 }
 
 extension AuthenticationViewModel {

+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/BirthdayViewModel.swift → Samples/Swift/DaysUntilBirthday/Shared/ViewModels/BirthdayViewModel.swift


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/BirthdayView.swift → Samples/Swift/DaysUntilBirthday/Shared/Views/BirthdayView.swift


+ 2 - 0
Samples/Swift/DaysUntilBirthday/Sources/ContentView.swift → Samples/Swift/DaysUntilBirthday/Shared/Views/ContentView.swift

@@ -40,7 +40,9 @@ struct ContentView: View {
               ))
         }
       }
+      #if os(iOS)
       .navigationViewStyle(StackNavigationViewStyle())
+      #endif
     }
   }
 }

+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/SignInView.swift → Samples/Swift/DaysUntilBirthday/Shared/Views/SignInView.swift


+ 10 - 0
Samples/Swift/DaysUntilBirthday/Sources/UserProfileImageView.swift → Samples/Swift/DaysUntilBirthday/Shared/Views/UserProfileImageView.swift

@@ -25,6 +25,7 @@ struct UserProfileImageView: View {
   }
 
   var body: some View {
+    #if os(iOS)
     Image(uiImage: userProfileImageLoader.image)
       .resizable()
       .aspectRatio(contentMode: .fill)
@@ -32,5 +33,14 @@ struct UserProfileImageView: View {
       .scaledToFit()
       .clipShape(Circle())
       .accessibilityLabel(Text("User profile image."))
+    #elseif os(macOS)
+    Image(nsImage: userProfileImageLoader.image)
+      .resizable()
+      .aspectRatio(contentMode: .fill)
+      .frame(width: 45, height: 45, alignment: .center)
+      .scaledToFit()
+      .clipShape(Circle())
+      .accessibilityLabel(Text("User profile image."))
+    #endif
   }
 }

+ 1 - 2
Samples/Swift/DaysUntilBirthday/Sources/GoogleSignInButtonWrapper.swift → Samples/Swift/DaysUntilBirthday/iOS/GoogleSignInButtonWrapper.swift

@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import SwiftUI
 import GoogleSignIn
 
@@ -38,7 +37,7 @@ struct GoogleSignInButtonWrapper: UIViewRepresentable {
   }
 
   func updateUIView(_ uiView: UIViewType, context: Context) {
-    context.coordinator.handler = handler 
+    context.coordinator.handler = handler
   }
 }
 

+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Info.plist → Samples/Swift/DaysUntilBirthday/iOS/Info.plist


+ 0 - 0
Samples/Swift/DaysUntilBirthday/Sources/Preview Content/Preview Assets.xcassets/Contents.json → Samples/Swift/DaysUntilBirthday/iOS/Preview Content/Preview Assets.xcassets/Contents.json


+ 2 - 4
Samples/Swift/DaysUntilBirthday/Sources/UserProfileView.swift → Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift

@@ -27,18 +27,17 @@ struct UserProfileView: View {
   var body: some View {
     return Group {
       if let userProfile = user?.profile {
-        VStack {
+        VStack(spacing: 10) {
           HStack(alignment: .top) {
             UserProfileImageView(userProfile: userProfile)
               .padding(.leading)
             VStack(alignment: .leading) {
               Text(userProfile.name)
+                .font(.headline)
                 .accessibilityLabel(Text("User name."))
               Text(userProfile.email)
                 .accessibilityLabel(Text("User email."))
-                .foregroundColor(.gray)
             }
-            Spacer()
           }
           NavigationLink(NSLocalizedString("View Days Until Birthday", comment: "View birthday days"),
                          destination: BirthdayView(birthdayViewModel: birthdayViewModel).onAppear {
@@ -53,7 +52,6 @@ struct UserProfileView: View {
               return
             }
           })
-            .accessibilityLabel(Text("View days until birthday."))
           Spacer()
         }
         .toolbar {

+ 12 - 0
Samples/Swift/DaysUntilBirthday/macOS/DaysUntilBirthdayOnMac.entitlements

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
+	<key>com.apple.security.files.user-selected.read-only</key>
+	<true/>
+	<key>com.apple.security.network.client</key>
+	<true/>
+</dict>
+</plist>

+ 17 - 0
Samples/Swift/DaysUntilBirthday/macOS/GoogleSignInButtonWrapper.swift

@@ -0,0 +1,17 @@
+import SwiftUI
+
+struct GoogleSignInButtonWrapper: View {
+  let handler: () -> Void
+
+  var body: some View {
+    Button(action: {
+      handler()
+    }) {
+      Text("SIGN IN")
+          .frame(width: 100 , height: 50, alignment: .center)
+    }
+     .background(Color.blue)
+     .foregroundColor(Color.white)
+     .cornerRadius(5)
+  }
+}

+ 19 - 0
Samples/Swift/DaysUntilBirthday/macOS/Info.plist

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleURLTypes</key>
+	<array>
+		<dict>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+			<key>CFBundleURLName</key>
+			<string>com.googleusercontent.apps.687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>com.googleusercontent.apps.687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv</string>
+			</array>
+		</dict>
+	</array>
+</dict>
+</plist>

+ 6 - 0
Samples/Swift/DaysUntilBirthday/macOS/Preview Content/Preview Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 71 - 0
Samples/Swift/DaysUntilBirthday/macOS/UserProfileView.swift

@@ -0,0 +1,71 @@
+import SwiftUI
+import GoogleSignIn
+
+struct UserProfileView: View {
+  @EnvironmentObject var authViewModel: AuthenticationViewModel
+  @StateObject var birthdayViewModel = BirthdayViewModel()
+  private var user: GIDGoogleUser? {
+    return GIDSignIn.sharedInstance.currentUser
+  }
+
+  var body: some View {
+    return Group {
+      if let userProfile = user?.profile {
+        VStack(spacing: 10) {
+          HStack(alignment: .top) {
+            UserProfileImageView(userProfile: userProfile)
+              .padding(.leading)
+            VStack(alignment: .leading) {
+              Text(userProfile.name)
+                .font(.headline)
+                .accessibilityLabel(Text("User name."))
+              Text(userProfile.email)
+                .accessibilityLabel(Text("User email."))
+            }
+          }
+          Button(NSLocalizedString("Sign Out", comment: "Sign out button"), action: signOut)
+            .background(Color.blue)
+            .foregroundColor(Color.white)
+            .cornerRadius(5)
+            .accessibilityLabel(Text("Sign out button"))
+
+          Button(NSLocalizedString("Disconnect", comment: "Disconnect button"), action: disconnect)
+            .background(Color.blue)
+            .foregroundColor(Color.white)
+            .cornerRadius(5)
+            .accessibilityLabel(Text("Disconnect scope button."))
+          Spacer()
+          NavigationLink(NSLocalizedString("View Days Until Birthday", comment: "View birthday days"),
+                         destination: BirthdayView(birthdayViewModel: birthdayViewModel).onAppear {
+            guard self.birthdayViewModel.birthday != nil else {
+              if !self.authViewModel.hasBirthdayReadScope {
+                self.authViewModel.addBirthdayReadScope {
+                  self.birthdayViewModel.fetchBirthday()
+                }
+              } else {
+                self.birthdayViewModel.fetchBirthday()
+              }
+              return
+            }
+          })
+            .background(Color.blue)
+            .foregroundColor(Color.white)
+            .cornerRadius(5)
+            .accessibilityLabel(Text("View days until birthday."))
+          Spacer()
+        }
+      } else {
+        Text(NSLocalizedString("Failed to get user profile!", comment: "Empty user profile text"))
+          .accessibilityLabel(Text("Failed to get user profile"))
+      }
+    }
+  }
+
+  func disconnect() {
+    authViewModel.disconnect()
+  }
+
+  func signOut() {
+    authViewModel.signOut()
+  }
+}