SettingsDownloadClient.swift 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. //
  2. // Copyright 2022 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. import Foundation
  16. @_implementationOnly import GoogleUtilities
  17. protocol SettingsDownloadClient {
  18. func fetch(completion: @escaping (Result<[String: Any], Error>) -> Void)
  19. }
  20. class SettingsDownloader: SettingsDownloadClient {
  21. private let appInfo: ApplicationInfoProtocol
  22. private let identifiers: IdentifierProvider
  23. private let installations: InstallationsProtocol
  24. init(appInfo: ApplicationInfoProtocol, identifiers: IdentifierProvider,
  25. installations: InstallationsProtocol) {
  26. self.appInfo = appInfo
  27. self.identifiers = identifiers
  28. self.installations = installations
  29. }
  30. func fetch(completion: @escaping (Result<[String: Any], Error>) -> Void) {
  31. guard let validURL = url else {
  32. completion(.failure(FirebaseSessionsError.SettingsError("Invalid URL")))
  33. return
  34. }
  35. installations.installationID { result in
  36. switch result {
  37. case let .success(fiid):
  38. let request = self.buildRequest(url: validURL, fiid: fiid)
  39. let task = URLSession.shared.dataTask(with: request) { data, response, error in
  40. if let data = data {
  41. if let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
  42. completion(.success(dict))
  43. } else {
  44. completion(.failure(FirebaseSessionsError
  45. .SettingsError("Failed to parse JSON to dictionary")))
  46. }
  47. } else if let error = error {
  48. completion(.failure(FirebaseSessionsError.SettingsError(error.localizedDescription)))
  49. }
  50. }
  51. // Start the task that sends the network request
  52. task.resume()
  53. case let .failure(error):
  54. completion(.failure(FirebaseSessionsError.SettingsError(error.localizedDescription)))
  55. }
  56. }
  57. }
  58. private var url: URL? {
  59. var components = URLComponents()
  60. components.scheme = "https"
  61. components.host = "firebase-settings.crashlytics.com"
  62. components.path = "/spi/v2/platforms/\(appInfo.osName)/gmp/\(appInfo.appID)/settings"
  63. components.queryItems = [
  64. URLQueryItem(name: "build_version", value: appInfo.appBuildVersion),
  65. URLQueryItem(name: "display_version", value: appInfo.appDisplayVersion),
  66. ]
  67. return components.url
  68. }
  69. private func buildRequest(url: URL, fiid: String) -> URLRequest {
  70. var request = URLRequest(url: url)
  71. request.setValue("application/json", forHTTPHeaderField: "Accept")
  72. request.setValue(fiid, forHTTPHeaderField: "X-Crashlytics-Installation-ID")
  73. request.setValue(appInfo.deviceModel, forHTTPHeaderField: "X-Crashlytics-Device-Model")
  74. request.setValue(
  75. appInfo.osBuildVersion,
  76. forHTTPHeaderField: "X-Crashlytics-OS-Build-Version"
  77. )
  78. request.setValue(
  79. appInfo.osDisplayVersion,
  80. forHTTPHeaderField: "X-Crashlytics-OS-Display-Version"
  81. )
  82. request.setValue(appInfo.sdkVersion, forHTTPHeaderField: "X-Crashlytics-API-Client-Version")
  83. return request
  84. }
  85. }