BirthdayLoader.swift 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import GoogleSignIn
  18. class BirthdayLoader {
  19. /// The scope required to read a user's birthday.
  20. static let birthdayReadScope = "https://www.googleapis.com/auth/user.birthday.read"
  21. private let baseUrlString = "https://people.googleapis.com/v1/people/me"
  22. private let personFieldsQuery = URLQueryItem(name: "personFields", value: "birthdays")
  23. private lazy var components: URLComponents? = {
  24. var comps = URLComponents(string: baseUrlString)
  25. comps?.queryItems = [personFieldsQuery]
  26. return comps
  27. }()
  28. private lazy var request: URLRequest? = {
  29. guard let components = components, let url = components.url else {
  30. return nil
  31. }
  32. return URLRequest(url: url)
  33. }()
  34. private func sessionWithFreshToken(completion: @escaping (Result<URLSession, Error>) -> Void) {
  35. GIDSignIn.sharedInstance.currentUser?.refreshTokensIfNeeded { user, error in
  36. guard let token = user?.accessToken.tokenString else {
  37. completion(.failure(.couldNotRefreshToken))
  38. return
  39. }
  40. let config = URLSessionConfiguration.default
  41. config.httpAdditionalHeaders = [
  42. "Authorization": "Bearer \(token)"
  43. ]
  44. let session = URLSession(configuration: config)
  45. completion(.success(session))
  46. }
  47. }
  48. func requestBirthday(completion: @escaping (Result<Birthday, Error>) -> Void) {
  49. guard let req = request else {
  50. completion(.failure(Error.noRequest))
  51. return
  52. }
  53. sessionWithFreshToken { sessionResult in
  54. switch sessionResult {
  55. case .success(let session):
  56. let task = session.dataTask(with: req) { data, response, error in
  57. guard let data else {
  58. completion(.failure(Error.noData))
  59. return
  60. }
  61. do {
  62. let jsonData = try JSONSerialization.jsonObject(with: data)
  63. guard let json = jsonData as? [String: Any] else {
  64. completion(.failure(Error.jsonDataCannotCastToString))
  65. return
  66. }
  67. guard let birthdays = json["birthdays"] as? [[String: Any]],
  68. let firstBday = birthdays.first?["date"] as? [String: Int],
  69. let day = firstBday["day"],
  70. let month = firstBday["month"] else {
  71. completion(.failure(Error.noBirthday))
  72. return
  73. }
  74. completion(.success(Birthday(day: day, month: month)))
  75. } catch {
  76. completion(.failure(Error.noJSON))
  77. }
  78. }
  79. task.resume()
  80. case .failure(let error):
  81. completion(.failure(error))
  82. }
  83. }
  84. }
  85. }
  86. extension BirthdayLoader {
  87. enum Error: Swift.Error {
  88. case noRequest
  89. case noData
  90. case noJSON
  91. case jsonDataCannotCastToString
  92. case noBirthday
  93. case couldNotRefreshToken
  94. }
  95. }
  96. struct Birthday: CustomStringConvertible {
  97. let day: Int
  98. let month: Int
  99. var description: String {
  100. return "Day: \(day); month: \(month)"
  101. }
  102. }