소스 검색

Add SwiftUI sample app (#63)

* Add SwiftUI sample app

* Update file headers with license

* Add birthday models and loader

* Do not double fetch birthday

* Improve parsing and add do with fresh tokens

* Add doc comments and fix do with fresh tokens crash

* Improves user profile image loading

* Improve layout to remove broken nav bar constraint warnings

* Reuse birthday if fetched

* Update directory structure and Swift sample app name

* Update READMEs and adds a Podfile for Swift sample

* Update Podfile path

* Update Podfile paths

* Update .gitignore

* Add pod project

* Update path for ObjC sample Podfile

* Add Podfile.lock for ObjC sample

* Update READMEs and remove Podfile.lock

* Revert to StackNavigationViewStyle() to avoid compatibility issues

* Update README instructions and demo project path

* Remove space from 'Cocoa Pods'

* Add app icon assets

* Update app client id
mdmathias 4 년 전
부모
커밋
c88adbd4df
100개의 변경된 파일1563개의 추가작업 그리고 22개의 파일을 삭제
  1. 1 1
      .cocoapods.yml
  2. 12 1
      .gitignore
  3. 36 15
      README.md
  4. 1 1
      Samples/ObjC/SignInSample/Podfile
  5. 5 4
      Samples/ObjC/SignInSample/README.md
  6. 0 0
      Samples/ObjC/SignInSample/Resources/DataPickerDictionary.plist
  7. 0 0
      Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-1024.png
  8. 0 0
      Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-60-2x.png
  9. 0 0
      Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-60-3x.png
  10. 0 0
      Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-76-2x.png
  11. 0 0
      Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-76.png
  12. 0 0
      Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-83-2x.png
  13. 0 0
      Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/Contents.json
  14. 0 0
      Samples/ObjC/SignInSample/Resources/PlaceholderAvatar.png
  15. 0 0
      Samples/ObjC/SignInSample/Resources/ar.lproj/Dummy.strings
  16. 0 0
      Samples/ObjC/SignInSample/Resources/ca.lproj/Dummy.strings
  17. 0 0
      Samples/ObjC/SignInSample/Resources/cs.lproj/Dummy.strings
  18. 0 0
      Samples/ObjC/SignInSample/Resources/da.lproj/Dummy.strings
  19. 0 0
      Samples/ObjC/SignInSample/Resources/de.lproj/Dummy.strings
  20. 0 0
      Samples/ObjC/SignInSample/Resources/el.lproj/Dummy.strings
  21. 0 0
      Samples/ObjC/SignInSample/Resources/en.lproj/Dummy.strings
  22. 0 0
      Samples/ObjC/SignInSample/Resources/en_GB.lproj/Dummy.strings
  23. 0 0
      Samples/ObjC/SignInSample/Resources/es.lproj/Dummy.strings
  24. 0 0
      Samples/ObjC/SignInSample/Resources/es_MX.lproj/Dummy.strings
  25. 0 0
      Samples/ObjC/SignInSample/Resources/fi.lproj/Dummy.strings
  26. 0 0
      Samples/ObjC/SignInSample/Resources/fr.lproj/Dummy.strings
  27. 0 0
      Samples/ObjC/SignInSample/Resources/he.lproj/Dummy.strings
  28. 0 0
      Samples/ObjC/SignInSample/Resources/hr.lproj/Dummy.strings
  29. 0 0
      Samples/ObjC/SignInSample/Resources/hu.lproj/Dummy.strings
  30. 0 0
      Samples/ObjC/SignInSample/Resources/id.lproj/Dummy.strings
  31. 0 0
      Samples/ObjC/SignInSample/Resources/it.lproj/Dummy.strings
  32. 0 0
      Samples/ObjC/SignInSample/Resources/ja.lproj/Dummy.strings
  33. 0 0
      Samples/ObjC/SignInSample/Resources/ko.lproj/Dummy.strings
  34. 0 0
      Samples/ObjC/SignInSample/Resources/ms.lproj/Dummy.strings
  35. 0 0
      Samples/ObjC/SignInSample/Resources/nb.lproj/Dummy.strings
  36. 0 0
      Samples/ObjC/SignInSample/Resources/nl.lproj/Dummy.strings
  37. 0 0
      Samples/ObjC/SignInSample/Resources/pl.lproj/Dummy.strings
  38. 0 0
      Samples/ObjC/SignInSample/Resources/pt.lproj/Dummy.strings
  39. 0 0
      Samples/ObjC/SignInSample/Resources/pt_BR.lproj/Dummy.strings
  40. 0 0
      Samples/ObjC/SignInSample/Resources/pt_PT.lproj/Dummy.strings
  41. 0 0
      Samples/ObjC/SignInSample/Resources/refresh.png
  42. 0 0
      Samples/ObjC/SignInSample/Resources/ro.lproj/Dummy.strings
  43. 0 0
      Samples/ObjC/SignInSample/Resources/ru.lproj/Dummy.strings
  44. 0 0
      Samples/ObjC/SignInSample/Resources/sk.lproj/Dummy.strings
  45. 0 0
      Samples/ObjC/SignInSample/Resources/sv.lproj/Dummy.strings
  46. 0 0
      Samples/ObjC/SignInSample/Resources/th.lproj/Dummy.strings
  47. 0 0
      Samples/ObjC/SignInSample/Resources/tr.lproj/Dummy.strings
  48. 0 0
      Samples/ObjC/SignInSample/Resources/uk.lproj/Dummy.strings
  49. 0 0
      Samples/ObjC/SignInSample/Resources/vi.lproj/Dummy.strings
  50. 0 0
      Samples/ObjC/SignInSample/Resources/zh_CN.lproj/Dummy.strings
  51. 0 0
      Samples/ObjC/SignInSample/Resources/zh_TW.lproj/Dummy.strings
  52. 0 0
      Samples/ObjC/SignInSample/SignInSample-Info.plist
  53. 0 0
      Samples/ObjC/SignInSample/SignInSample.xcodeproj/project.pbxproj
  54. 0 0
      Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj
  55. 0 0
      Samples/ObjC/SignInSample/Source/AppDelegate.h
  56. 0 0
      Samples/ObjC/SignInSample/Source/AppDelegate.m
  57. 0 0
      Samples/ObjC/SignInSample/Source/AuthInspectorViewController.h
  58. 0 0
      Samples/ObjC/SignInSample/Source/AuthInspectorViewController.m
  59. 0 0
      Samples/ObjC/SignInSample/Source/DataPickerState.h
  60. 0 0
      Samples/ObjC/SignInSample/Source/DataPickerState.m
  61. 0 0
      Samples/ObjC/SignInSample/Source/DataPickerViewController.h
  62. 0 0
      Samples/ObjC/SignInSample/Source/DataPickerViewController.m
  63. 0 0
      Samples/ObjC/SignInSample/Source/LaunchScreen.xib
  64. 0 0
      Samples/ObjC/SignInSample/Source/SignInViewController.h
  65. 0 0
      Samples/ObjC/SignInSample/Source/SignInViewController.m
  66. 0 0
      Samples/ObjC/SignInSample/Source/SignInViewController.xib
  67. 0 0
      Samples/ObjC/SignInSample/Source/main.m
  68. 406 0
      Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/project.pbxproj
  69. 446 0
      Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayForPod.xcodeproj/project.pbxproj
  70. 6 0
      Samples/Swift/DaysUntilBirthday/Podfile
  71. 28 0
      Samples/Swift/DaysUntilBirthday/README.md
  72. 11 0
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AccentColor.colorset/Contents.json
  73. 116 0
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json
  74. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/Icon.png
  75. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt.png
  76. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png
  77. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png
  78. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png
  79. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt.png
  80. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png
  81. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png
  82. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png
  83. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt.png
  84. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png
  85. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png
  86. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png
  87. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png
  88. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png
  89. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_76pt.png
  90. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png
  91. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png
  92. 6 0
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/Contents.json
  93. 21 0
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/PlaceholderAvatar.imageset/Contents.json
  94. BIN
      Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/PlaceholderAvatar.imageset/PlaceholderAvatar.png
  95. 82 0
      Samples/Swift/DaysUntilBirthday/Sources/AuthenticationViewModel.swift
  96. 137 0
      Samples/Swift/DaysUntilBirthday/Sources/Birthday.swift
  97. 112 0
      Samples/Swift/DaysUntilBirthday/Sources/BirthdayLoader.swift
  98. 39 0
      Samples/Swift/DaysUntilBirthday/Sources/BirthdayView.swift
  99. 52 0
      Samples/Swift/DaysUntilBirthday/Sources/BirthdayViewModel.swift
  100. 46 0
      Samples/Swift/DaysUntilBirthday/Sources/ContentView.swift

+ 1 - 1
.cocoapods.yml

@@ -1,2 +1,2 @@
 try:
-  project: 'Sample/SignInSampleForPod.xcodeproj'
+  project: 'Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj'

+ 12 - 1
.gitignore

@@ -1,6 +1,17 @@
 # MacOS
 .DS_Store
 
+# Xcode
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+*.xcuserstate
+*.xcworkspace/
+project.xcworkspace/
+xcuserdata/
+
 # CocoaPods
 Pods/
-gen/
+gen/
+Podfile.lock

+ 36 - 15
README.md

@@ -5,7 +5,8 @@
 
 # Google Sign-In for iOS
 
-Get users into your apps quickly and securely, using a registration system they already use and trust—their Google account.
+Get users into your apps quickly and securely, using a registration system they
+already use and trust—their Google account.
 
 Visit [our developer site](https://developers.google.com/identity/sign-in/ios/)
 for integration instructions, documentation, support information, and terms of
@@ -13,17 +14,37 @@ service.
 
 ## Getting Started
 
-* Try the [sample app](Sample).
-  * Using [CocoaPods](https://cocoapods.org):
-    ```
-    pod try GoogleSignIn
-    ```
-  * Using [Swift Package Manager](https://swift.org/package-manager/):
-    ```
-    git clone https://github.com/google/GoogleSignIn-iOS
-    ```
-    ```
-    open GoogleSignIn-iOS/Sample/SignInSample.xcodeproj
-    ```
-* Add Google Sign-In to your own app by following our [getting started guides](https://developers.google.com/identity/sign-in/ios/start-integrating).
-* Take a look at the [API reference](https://developers.google.com/identity/sign-in/ios/api/).
+Try either the [Objective-C](Samples/ObjC) or [Swift](Samples/Swift) sample app.
+For example, to demo the Objective-C sample project, you have three options:
+
+1. Using [CocoaPods](https://cocoapods.org/)'s `try` method:
+
+```
+pod try GoogleSignIn
+```
+
+Note, this will default to providing you with the Objective-C sample app.
+
+2. Using CocoaPod's `install` method:
+
+```
+git clone https://github.com/google/GoogleSignIn-iOS
+cd GoogleSignIn-iOS/Samples/ObjC/SignInSample/
+pod install
+open SignInSampleForPod.xcworkspace
+```
+
+3. Using [Swift Package Manager](https://swift.org/package-manager/):
+
+```
+git clone https://github.com/google/GoogleSignIn-iOS
+open GoogleSignIn-iOS/Samples/ObjC/SignInSample/SignInSample.xcodeproj
+```
+
+If you would like to see a Swift example, take a look at 
+[Samples/Swift/DaysUntilBirthday](Samples/Swift/DaysUntilBirthday).
+
+* Add Google Sign-In to your own app by following our
+[getting started guides](https://developers.google.com/identity/sign-in/ios/start-integrating).
+* Take a look at the
+[API reference](https://developers.google.com/identity/sign-in/ios/api/).

+ 1 - 1
Sample/Podfile → Samples/ObjC/SignInSample/Podfile

@@ -1,6 +1,6 @@
 platform :ios, '9.0'
 
 target 'SampleForPod' do
-  pod 'GoogleSignIn', :path => '../', :testspecs => ['unit']
+  pod 'GoogleSignIn', :path => '../../../', :testspecs => ['unit']
   project 'SignInSampleForPod.xcodeproj'
 end

+ 5 - 4
Sample/README.md → Samples/ObjC/SignInSample/README.md

@@ -2,8 +2,9 @@
 
 ## CocoaPods
 
-1. In the Sample folder, run the following [CocoaPods](https://cocoapods.org) command
-   to install the required dependencies:
+1. In the `../Samples/ObjC/SignInSample/` folder, run the following 
+[CocoaPods](https://cocoapods.org) command to install the require
+dependencies:
 
 ```
 pod install
@@ -19,8 +20,8 @@ open SignInSampleForPod.xcworkspace
 
 ## Swift Pacakage Manager
 
-1. In the Sample folder, open the
-   [Swift Package Manager](https://swift.org/package-manager/) project:
+1. In the `../Sample/ObjC/SignInSample/` folder, open the
+[Swift Package Manager](https://swift.org/package-manager/) project:
 
 ```
 open SignInSample.xcodeproj

+ 0 - 0
Sample/Resources/DataPickerDictionary.plist → Samples/ObjC/SignInSample/Resources/DataPickerDictionary.plist


+ 0 - 0
Sample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-1024.png → Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-1024.png


+ 0 - 0
Sample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-60-2x.png → Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-60-2x.png


+ 0 - 0
Sample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-60-3x.png → Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-60-3x.png


+ 0 - 0
Sample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-76-2x.png → Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-76-2x.png


+ 0 - 0
Sample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-76.png → Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-76.png


+ 0 - 0
Sample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-83-2x.png → Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/AppIcon-83-2x.png


+ 0 - 0
Sample/Resources/Images.xcassets/AppIcon.appiconset/Contents.json → Samples/ObjC/SignInSample/Resources/Images.xcassets/AppIcon.appiconset/Contents.json


+ 0 - 0
Sample/Resources/PlaceholderAvatar.png → Samples/ObjC/SignInSample/Resources/PlaceholderAvatar.png


+ 0 - 0
Sample/Resources/ar.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/ar.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/ca.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/ca.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/cs.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/cs.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/da.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/da.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/de.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/de.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/el.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/el.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/en.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/en.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/en_GB.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/en_GB.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/es.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/es.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/es_MX.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/es_MX.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/fi.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/fi.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/fr.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/fr.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/he.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/he.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/hr.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/hr.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/hu.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/hu.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/id.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/id.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/it.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/it.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/ja.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/ja.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/ko.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/ko.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/ms.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/ms.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/nb.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/nb.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/nl.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/nl.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/pl.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/pl.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/pt.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/pt.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/pt_BR.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/pt_BR.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/pt_PT.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/pt_PT.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/refresh.png → Samples/ObjC/SignInSample/Resources/refresh.png


+ 0 - 0
Sample/Resources/ro.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/ro.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/ru.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/ru.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/sk.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/sk.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/sv.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/sv.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/th.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/th.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/tr.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/tr.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/uk.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/uk.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/vi.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/vi.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/zh_CN.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/zh_CN.lproj/Dummy.strings


+ 0 - 0
Sample/Resources/zh_TW.lproj/Dummy.strings → Samples/ObjC/SignInSample/Resources/zh_TW.lproj/Dummy.strings


+ 0 - 0
Sample/SignInSample-Info.plist → Samples/ObjC/SignInSample/SignInSample-Info.plist


+ 0 - 0
Sample/SignInSample.xcodeproj/project.pbxproj → Samples/ObjC/SignInSample/SignInSample.xcodeproj/project.pbxproj


+ 0 - 0
Sample/SignInSampleForPod.xcodeproj/project.pbxproj → Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj


+ 0 - 0
Sample/Source/AppDelegate.h → Samples/ObjC/SignInSample/Source/AppDelegate.h


+ 0 - 0
Sample/Source/AppDelegate.m → Samples/ObjC/SignInSample/Source/AppDelegate.m


+ 0 - 0
Sample/Source/AuthInspectorViewController.h → Samples/ObjC/SignInSample/Source/AuthInspectorViewController.h


+ 0 - 0
Sample/Source/AuthInspectorViewController.m → Samples/ObjC/SignInSample/Source/AuthInspectorViewController.m


+ 0 - 0
Sample/Source/DataPickerState.h → Samples/ObjC/SignInSample/Source/DataPickerState.h


+ 0 - 0
Sample/Source/DataPickerState.m → Samples/ObjC/SignInSample/Source/DataPickerState.m


+ 0 - 0
Sample/Source/DataPickerViewController.h → Samples/ObjC/SignInSample/Source/DataPickerViewController.h


+ 0 - 0
Sample/Source/DataPickerViewController.m → Samples/ObjC/SignInSample/Source/DataPickerViewController.m


+ 0 - 0
Sample/Source/LaunchScreen.xib → Samples/ObjC/SignInSample/Source/LaunchScreen.xib


+ 0 - 0
Sample/Source/SignInViewController.h → Samples/ObjC/SignInSample/Source/SignInViewController.h


+ 0 - 0
Sample/Source/SignInViewController.m → Samples/ObjC/SignInSample/Source/SignInViewController.m


+ 0 - 0
Sample/Source/SignInViewController.xib → Samples/ObjC/SignInSample/Source/SignInViewController.xib


+ 0 - 0
Sample/Source/main.m → Samples/ObjC/SignInSample/Source/main.m


+ 406 - 0
Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/project.pbxproj

@@ -0,0 +1,406 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 52;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		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 */; };
+		7345AD272703DA3E0020AFB1 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 7345AD262703DA3E0020AFB1 /* GoogleSignIn */; };
+		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 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		7345ACFF2703D9470020AFB1 /* DaysUntilBirthday.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DaysUntilBirthday.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>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		7345ACFC2703D9470020AFB1 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				7345AD272703DA3E0020AFB1 /* GoogleSignIn in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		7345ACF62703D9470020AFB1 = {
+			isa = PBXGroup;
+			children = (
+				7345AD012703D9470020AFB1 /* Sources */,
+				7345AD002703D9470020AFB1 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		7345AD002703D9470020AFB1 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				7345ACFF2703D9470020AFB1 /* DaysUntilBirthday.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		7345AD012703D9470020AFB1 /* Sources */ = {
+			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;
+			sourceTree = "<group>";
+		};
+		7345AD082703D9480020AFB1 /* Preview Content */ = {
+			isa = PBXGroup;
+			children = (
+				7345AD092703D9480020AFB1 /* Preview Assets.xcassets */,
+			);
+			path = "Preview Content";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		7345ACFE2703D9470020AFB1 /* DaysUntilBirthday */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday" */;
+			buildPhases = (
+				7345ACFB2703D9470020AFB1 /* Sources */,
+				7345ACFC2703D9470020AFB1 /* Frameworks */,
+				7345ACFD2703D9470020AFB1 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = DaysUntilBirthday;
+			packageProductDependencies = (
+				7345AD262703DA3E0020AFB1 /* GoogleSignIn */,
+			);
+			productName = "GSI-Swift-Sample";
+			productReference = 7345ACFF2703D9470020AFB1 /* DaysUntilBirthday.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		7345ACF72703D9470020AFB1 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 1250;
+				LastUpgradeCheck = 1250;
+				TargetAttributes = {
+					7345ACFE2703D9470020AFB1 = {
+						CreatedOnToolsVersion = 12.5.1;
+					};
+				};
+			};
+			buildConfigurationList = 7345ACFA2703D9470020AFB1 /* Build configuration list for PBXProject "DaysUntilBirthday" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 7345ACF62703D9470020AFB1;
+			packageReferences = (
+				7345AD252703DA3E0020AFB1 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */,
+			);
+			productRefGroup = 7345AD002703D9470020AFB1 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				7345ACFE2703D9470020AFB1 /* DaysUntilBirthday */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		7345ACFD2703D9470020AFB1 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				7345AD0A2703D9480020AFB1 /* Preview Assets.xcassets in Resources */,
+				7345AD072703D9480020AFB1 /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		7345ACFB2703D9470020AFB1 /* 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 */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		7345AD0C2703D9480020AFB1 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_APPICON_NAME = "";
+				ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				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 */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_APPICON_NAME = "";
+				ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				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 */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				ENABLE_PREVIEWS = YES;
+				INFOPLIST_FILE = Sources/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		7345AD102703D9480020AFB1 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				ENABLE_PREVIEWS = YES;
+				INFOPLIST_FILE = Sources/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		7345ACFA2703D9470020AFB1 /* Build configuration list for PBXProject "DaysUntilBirthday" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				7345AD0C2703D9480020AFB1 /* Debug */,
+				7345AD0D2703D9480020AFB1 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthday" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				7345AD0F2703D9480020AFB1 /* Debug */,
+				7345AD102703D9480020AFB1 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+		7345AD252703DA3E0020AFB1 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/google/GoogleSignIn-iOS.git";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 6.0.2;
+			};
+		};
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+		7345AD262703DA3E0020AFB1 /* GoogleSignIn */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = 7345AD252703DA3E0020AFB1 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */;
+			productName = GoogleSignIn;
+		};
+/* End XCSwiftPackageProductDependency section */
+	};
+	rootObject = 7345ACF72703D9470020AFB1 /* Project object */;
+}

+ 446 - 0
Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayForPod.xcodeproj/project.pbxproj

@@ -0,0 +1,446 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 50;
+	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 */; };
+/* 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>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		7345ACFC2703D9470020AFB1 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				6D820AE53F1E83F8D828D207 /* libPods-DaysUntilBirthdayForPod.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		7345ACF62703D9470020AFB1 = {
+			isa = PBXGroup;
+			children = (
+				7345AD012703D9470020AFB1 /* Sources */,
+				7345AD002703D9470020AFB1 /* Products */,
+				B8016955C403553EF7E5C086 /* Pods */,
+				C3E2C8C89963A77B9020C2FF /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		7345AD002703D9470020AFB1 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				7345ACFF2703D9470020AFB1 /* DaysUntilBirthdayForPod.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		7345AD012703D9470020AFB1 /* Sources */ = {
+			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;
+			sourceTree = "<group>";
+		};
+		7345AD082703D9480020AFB1 /* Preview Content */ = {
+			isa = PBXGroup;
+			children = (
+				7345AD092703D9480020AFB1 /* Preview Assets.xcassets */,
+			);
+			path = "Preview Content";
+			sourceTree = "<group>";
+		};
+		B8016955C403553EF7E5C086 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				EEB3D2312581D01EAB21E02A /* Pods-DaysUntilBirthdayForPod.debug.xcconfig */,
+				CE1D65F7694C48ED17C48C7A /* Pods-DaysUntilBirthdayForPod.release.xcconfig */,
+			);
+			name = Pods;
+			path = Pods;
+			sourceTree = "<group>";
+		};
+		C3E2C8C89963A77B9020C2FF /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				9D224E5A98AD4723D3CDC222 /* libPods-DaysUntilBirthdayForPod.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		7345ACFE2703D9470020AFB1 /* DaysUntilBirthdayForPod */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod" */;
+			buildPhases = (
+				5C126313BC6F0229B5B245F4 /* [CP] Check Pods Manifest.lock */,
+				7345ACFB2703D9470020AFB1 /* Sources */,
+				7345ACFC2703D9470020AFB1 /* Frameworks */,
+				7345ACFD2703D9470020AFB1 /* Resources */,
+				FCBC4E621013094E19A4D011 /* [CP] Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = DaysUntilBirthdayForPod;
+			productName = "GSI-Swift-Sample";
+			productReference = 7345ACFF2703D9470020AFB1 /* DaysUntilBirthdayForPod.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		7345ACF72703D9470020AFB1 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 1250;
+				LastUpgradeCheck = 1250;
+				TargetAttributes = {
+					7345ACFE2703D9470020AFB1 = {
+						CreatedOnToolsVersion = 12.5.1;
+					};
+				};
+			};
+			buildConfigurationList = 7345ACFA2703D9470020AFB1 /* Build configuration list for PBXProject "DaysUntilBirthdayForPod" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 7345ACF62703D9470020AFB1;
+			productRefGroup = 7345AD002703D9470020AFB1 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				7345ACFE2703D9470020AFB1 /* DaysUntilBirthdayForPod */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		7345ACFD2703D9470020AFB1 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				7345AD0A2703D9480020AFB1 /* Preview Assets.xcassets in Resources */,
+				7345AD072703D9480020AFB1 /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		5C126313BC6F0229B5B245F4 /* [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-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 */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod/Pods-DaysUntilBirthdayForPod-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",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DaysUntilBirthdayForPod/Pods-DaysUntilBirthdayForPod-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		7345ACFB2703D9470020AFB1 /* 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 */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		7345AD0C2703D9480020AFB1 /* 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_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				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 */ = {
+			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_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				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 */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = EEB3D2312581D01EAB21E02A /* Pods-DaysUntilBirthdayForPod.debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				ENABLE_PREVIEWS = YES;
+				INFOPLIST_FILE = Sources/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthdayForPod;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		7345AD102703D9480020AFB1 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = CE1D65F7694C48ED17C48C7A /* Pods-DaysUntilBirthdayForPod.release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
+				ENABLE_PREVIEWS = YES;
+				INFOPLIST_FILE = Sources/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthdayForPod;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		7345ACFA2703D9470020AFB1 /* Build configuration list for PBXProject "DaysUntilBirthdayForPod" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				7345AD0C2703D9480020AFB1 /* Debug */,
+				7345AD0D2703D9480020AFB1 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		7345AD0E2703D9480020AFB1 /* Build configuration list for PBXNativeTarget "DaysUntilBirthdayForPod" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				7345AD0F2703D9480020AFB1 /* Debug */,
+				7345AD102703D9480020AFB1 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 7345ACF72703D9470020AFB1 /* Project object */;
+}

+ 6 - 0
Samples/Swift/DaysUntilBirthday/Podfile

@@ -0,0 +1,6 @@
+platform :ios, '9.0'
+
+target 'DaysUntilBirthdayForPod' do
+  pod 'GoogleSignIn', :path => '../../../', :testspecs => ['unit']
+  project 'DaysUntilBirthdayForPod.xcodeproj'
+end

+ 28 - 0
Samples/Swift/DaysUntilBirthday/README.md

@@ -0,0 +1,28 @@
+# Google Sign-In Swift Sample App
+
+## CocoaPods
+
+1. In the `../Samples/Swift/DaysUntilBirthday/` folder, run the following 
+[CocoaPods](https://cocoapods.org) command.
+
+```
+pod install
+```
+
+2. Open the generated workspace:
+
+```
+open DaysUntilBirthdayForPod.xcworkspace
+```
+
+3. Run the `DaysUntilBirthdayForPod` target.
+
+## Swift Package Manager
+
+1. In the `../Samples/Swift/DaysUntilBirthday/` folder, open the project:
+
+```
+open DaysUntilBirthday.xcodeproj
+```
+
+2. Run the `DaysUntilBirthday` target.

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

@@ -0,0 +1,11 @@
+{
+  "colors" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

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

@@ -0,0 +1,116 @@
+{
+  "images" : [
+    {
+      "filename" : "icon_20pt@2x-1.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "icon_20pt@3x.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "icon_29pt@2x-1.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "icon_29pt@3x.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "icon_40pt@2x-1.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "icon_40pt@3x.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "icon_60pt@2x.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "60x60"
+    },
+    {
+      "filename" : "icon_60pt@3x.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "60x60"
+    },
+    {
+      "filename" : "icon_20pt.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "icon_20pt@2x.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "icon_29pt.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "icon_29pt@2x.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "icon_40pt.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "icon_40pt@2x.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "icon_76pt.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "76x76"
+    },
+    {
+      "filename" : "icon_76pt@2x.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "76x76"
+    },
+    {
+      "filename" : "icon_83.5@2x.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "83.5x83.5"
+    },
+    {
+      "filename" : "Icon.png",
+      "idiom" : "ios-marketing",
+      "scale" : "1x",
+      "size" : "1024x1024"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/Icon.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x-1.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@2x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_20pt@3x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x-1.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@2x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_29pt@3x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x-1.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@2x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_40pt@3x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_76pt.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png


BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png


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

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

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

@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "filename" : "PlaceholderAvatar.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
Samples/Swift/DaysUntilBirthday/Sources/Assets.xcassets/PlaceholderAvatar.imageset/PlaceholderAvatar.png


+ 82 - 0
Samples/Swift/DaysUntilBirthday/Sources/AuthenticationViewModel.swift

@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import SwiftUI
+import GoogleSignIn
+
+/// A class conforming to `ObservableObject` used to represent a user's authentication status.
+final class AuthenticationViewModel: ObservableObject {
+  /// The user's log in status.
+  /// - note: This will publish updates when its value changes.
+  @Published var state: State
+  private var authenticator: GoogleSignInAuthenticator {
+    return GoogleSignInAuthenticator(authViewModel: self)
+  }
+  /// The user-authorized scopes.
+  /// - note: If the user is logged out, then this will default to empty.
+  var authorizedScopes: [String] {
+    switch state {
+    case .signedIn(let user):
+      return user.grantedScopes ?? []
+    case .signedOut:
+      return []
+    }
+  }
+
+  var hasBirthdayReadScope: Bool {
+    return authorizedScopes.contains(BirthdayLoader.birthdayReadScope)
+  }
+
+  /// Creates an instance of this view model.
+  init() {
+    if let user = GIDSignIn.sharedInstance.currentUser {
+      self.state = .signedIn(user)
+    } else {
+      self.state = .signedOut
+    }
+  }
+
+  /// Signs the user in.
+  func signIn() {
+    authenticator.signIn()
+  }
+
+  /// Signs the user out.
+  func signOut() {
+    authenticator.signOut()
+  }
+
+  /// 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 {
+  /// An enumeration representing logged in status.
+  enum State {
+    /// The user is logged in and is the associated value of this case.
+    case signedIn(GIDGoogleUser)
+    /// The user is logged out.
+    case signedOut
+  }
+}

+ 137 - 0
Samples/Swift/DaysUntilBirthday/Sources/Birthday.swift

@@ -0,0 +1,137 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Foundation
+
+/// A model type representing the current user's birthday.
+struct Birthday: Decodable {
+  let integerDate: Birthday.IntegerDate
+
+  /// The birthday as a `Date`.
+  var date: Date? {
+    let now = Date()
+    let currentCalendar = Calendar.autoupdatingCurrent
+    let currentYear = currentCalendar.dateComponents([.year], from: now)
+    let comps = DateComponents(calendar: Calendar.autoupdatingCurrent,
+                               timeZone: TimeZone.autoupdatingCurrent,
+                               year: currentYear.year,
+                               month: integerDate.month,
+                               day: integerDate.day)
+    guard let d = comps.date else { return nil }
+    if d < now {
+      var nextYearComponent = DateComponents()
+      nextYearComponent.year = 1
+      let bdayNextYear = currentCalendar.date(byAdding: .year, value: 1, to: d)
+      return bdayNextYear
+    } else {
+      return d
+    }
+}
+
+  init(from decoder: Decoder) throws {
+    let container = try decoder.container(keyedBy: CodingKeys.self)
+    self.integerDate = try container.decode(IntegerDate.self, forKey: .integerDate)
+  }
+}
+
+extension Birthday {
+  /// A nested type representing the month and day values of a birthday as integers.
+  struct IntegerDate: Decodable {
+    let month: Int
+    let day: Int
+  }
+}
+
+extension Birthday {
+  enum CodingKeys: String, CodingKey {
+    case integerDate = "date"
+  }
+}
+
+extension Birthday: CustomStringConvertible {
+  /// Converts the instances `date` to a `String`.
+  var description: String {
+    return date?.description ?? "NA"
+  }
+}
+
+/// A model type representing the response from the request for the current user's birthday.
+struct BirthdayResponse: Decodable {
+  /// The requested user's birthdays.
+  let birthdays: [Birthday]
+  /// The first birthday in the returned results.
+  /// - note: We only care about the birthday's month and day, and so we just use the first
+  /// birthday in the results.
+  let firstBirthday: Birthday
+
+  init(from decoder: Decoder) throws {
+    let container = try decoder.container(keyedBy: CodingKeys.self)
+    self.birthdays = try container.decode([Birthday].self, forKey: .birthdays)
+    guard let first = birthdays.first else {
+      throw Error.noBirthdayInResult
+    }
+    self.firstBirthday = first
+  }
+}
+
+extension BirthdayResponse {
+  enum CodingKeys: String, CodingKey {
+    case birthdays
+  }
+}
+
+extension BirthdayResponse {
+  /// An error representing what may go wrong in processing the birthday request.
+  enum Error: Swift.Error {
+    /// There was no birthday in the returned results.
+    case noBirthdayInResult
+  }
+}
+
+/*
+ {
+   "resourceName": "people/111941908710159755740",
+   "etag": "%EgQBBy43GgQBAgUHIgxvOUdlOWN5d3lmZz0=",
+   "birthdays": [
+     {
+       "metadata": {
+         "primary": true,
+         "source": {
+           "type": "PROFILE",
+           "id": "111941908710159755740"
+         }
+       },
+       "date": {
+         "month": 5,
+         "day": 31
+       }
+     },
+     {
+       "metadata": {
+         "source": {
+           "type": "ACCOUNT",
+           "id": "111941908710159755740"
+         }
+       },
+       "date": {
+         "year": 1982,
+         "month": 5,
+         "day": 31
+       }
+     }
+   ]
+ }
+ */

+ 112 - 0
Samples/Swift/DaysUntilBirthday/Sources/BirthdayLoader.swift

@@ -0,0 +1,112 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Combine
+import GoogleSignIn
+
+/// An observable class to load the current user's birthday.
+final class BirthdayLoader: ObservableObject {
+  /// The scope required to read a user's birthday.
+  static let birthdayReadScope = "https://www.googleapis.com/auth/user.birthday.read"
+  private let baseUrlString = "https://people.googleapis.com/v1/people/me"
+  private let personFieldsQuery = URLQueryItem(name: "personFields", value: "birthdays")
+  private let birthdaySubject = PassthroughSubject<Birthday, Error>()
+
+  private lazy var components: URLComponents? = {
+    var comps = URLComponents(string: baseUrlString)
+    comps?.queryItems = [personFieldsQuery]
+    return comps
+  }()
+
+  private lazy var request: URLRequest? = {
+    guard let components = components, let url = components.url else {
+      return nil
+    }
+    return URLRequest(url: url)
+  }()
+
+  private lazy var session: URLSession? = {
+    guard let accessToken = GIDSignIn
+            .sharedInstance
+            .currentUser?
+            .authentication
+            .accessToken else { return nil }
+    let configuration = URLSessionConfiguration.default
+    configuration.httpAdditionalHeaders = [
+      "Authorization": "Bearer \(accessToken)"
+    ]
+    return URLSession(configuration: configuration)
+  }()
+
+  private func sessionWithFreshToken(completion: @escaping (Result<URLSession, Error>) -> Void) {
+    let authentication = GIDSignIn.sharedInstance.currentUser?.authentication
+    authentication?.do { auth, error in
+      guard let token = auth?.accessToken else {
+        completion(.failure(.couldNotCreateURLSession(error)))
+        return
+      }
+      let configuration = URLSessionConfiguration.default
+      configuration.httpAdditionalHeaders = [
+        "Authorization": "Bearer \(token)"
+      ]
+      let session = URLSession(configuration: configuration)
+      completion(.success(session))
+    }
+  }
+
+  /// Creates a `Publisher` to fetch a user's `Birthday`.
+  /// - parameter completion: A closure passing back the `AnyPublisher<Birthday, Error>`
+  /// upon success.
+  /// - note: The `AnyPublisher` passed back through the `completion` closure is created with a
+  /// fresh token. See `sessionWithFreshToken(completion:)` for more details.
+  func birthdayPublisher(completion: @escaping (AnyPublisher<Birthday, Error>) -> Void) {
+    sessionWithFreshToken { [weak self] result in
+      switch result {
+      case .success(let authSession):
+        guard let request = self?.request else {
+          return completion(Fail(error: .couldNotCreateURLRequest).eraseToAnyPublisher())
+        }
+        let bdayPublisher = authSession.dataTaskPublisher(for: request)
+          .tryMap { data, error -> Birthday in
+            let decoder = JSONDecoder()
+            let birthdayResponse = try decoder.decode(BirthdayResponse.self, from: data)
+            return birthdayResponse.firstBirthday
+          }
+          .mapError { error -> Error in
+            guard let loaderError = error as? Error else {
+              return Error.couldNotFetchBirthday(underlying: error)
+            }
+            return loaderError
+          }
+          .receive(on: DispatchQueue.main)
+          .eraseToAnyPublisher()
+        completion(bdayPublisher)
+      case .failure(let error):
+        completion(Fail(error: error).eraseToAnyPublisher())
+      }
+    }
+  }
+}
+
+extension BirthdayLoader {
+  /// An error representing what went wrong in fetching a user's number of day until their birthday.
+  enum Error: Swift.Error {
+    case couldNotCreateURLSession(Swift.Error?)
+    case couldNotCreateURLRequest
+    case userHasNoBirthday
+    case couldNotFetchBirthday(underlying: Swift.Error)
+  }
+}

+ 39 - 0
Samples/Swift/DaysUntilBirthday/Sources/BirthdayView.swift

@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import SwiftUI
+import GoogleSignIn
+
+struct BirthdayView: View {
+  @ObservedObject var birthdayViewModel: BirthdayViewModel
+
+  var body: some View {
+    if let _ = birthdayViewModel.birthday {
+      VStack {
+        Text(birthdayViewModel.daysUntilBirthday)
+          .font(.system(size: 80))
+          .accessibilityLabel("Number of days until your birthday")
+        Spacer()
+      }
+      .navigationTitle(NSLocalizedString("Days Until Birthday",
+                                         comment: "Days until birthday label"))
+    } else {
+      ProgressView()
+        .navigationTitle(NSLocalizedString("Days Until Birthday",
+                                           comment: "Days until birthday label"))
+    }
+  }
+}

+ 52 - 0
Samples/Swift/DaysUntilBirthday/Sources/BirthdayViewModel.swift

@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Combine
+import Foundation
+
+/// An observable class representing the current user's `Birthday` and the number of days until that date.
+final class BirthdayViewModel: ObservableObject {
+  /// The `Birthday` of the current user.
+  /// - note: Changes to this property will be published to observers.
+  @Published private(set) var birthday: Birthday?
+  /// Computed property calculating the number of days until the current user's birthday.
+  var daysUntilBirthday: String {
+    guard let bday = birthday?.date else { return "NA" }
+    let now = Date()
+    let calendar = Calendar.autoupdatingCurrent
+    let dayComps = calendar.dateComponents([.day], from: now, to: bday)
+    guard let days = dayComps.day else { return "NA" }
+    return String(days)
+  }
+  private var cancellable: AnyCancellable?
+  private let birthdayLoader = BirthdayLoader()
+
+  /// Fetches the birthday of the current user.
+  func fetchBirthday() {
+    birthdayLoader.birthdayPublisher { publisher in
+      self.cancellable = publisher.sink { completion in
+        switch completion {
+        case .finished:
+          break
+        case .failure(let error):
+          print("Error retrieving birthday: \(error)")
+        }
+      } receiveValue: { birthday in
+        self.birthday = birthday
+      }
+    }
+  }
+}

+ 46 - 0
Samples/Swift/DaysUntilBirthday/Sources/ContentView.swift

@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import SwiftUI
+import GoogleSignIn
+
+struct ContentView: View {
+  @EnvironmentObject var authViewModel: AuthenticationViewModel
+
+  var body: some View {
+    return Group {
+      NavigationView {
+        switch authViewModel.state {
+        case .signedIn:
+          UserProfileView()
+            .navigationTitle(
+              NSLocalizedString(
+                "User Profile",
+                comment: "User profile navigation title"
+              ))
+        case .signedOut:
+          SignInView()
+            .navigationTitle(
+              NSLocalizedString(
+                "Sign-in with Google",
+                comment: "Sign-in navigation title"
+              ))
+        }
+      }
+      .navigationViewStyle(StackNavigationViewStyle())
+    }
+  }
+}

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.