StorageFetcherService.swift 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // Copyright 2024 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import Foundation
  15. #if COCOAPODS
  16. import GTMSessionFetcher
  17. #else
  18. import GTMSessionFetcherCore
  19. #endif
  20. /// Manage Storage's fetcherService
  21. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  22. actor StorageFetcherService {
  23. static let shared = StorageFetcherService()
  24. private var _fetcherService: GTMSessionFetcherService?
  25. func service(_ storage: Storage) async -> GTMSessionFetcherService {
  26. if let _fetcherService {
  27. return _fetcherService
  28. }
  29. let app = storage.app
  30. if let fetcherService = getFromMap(appName: app.name, bucket: storage.storageBucket) {
  31. return fetcherService
  32. } else {
  33. let fetcherService = GTMSessionFetcherService()
  34. fetcherService.isRetryEnabled = true
  35. fetcherService.retryBlock = retryWhenOffline
  36. fetcherService.allowLocalhostRequest = true
  37. fetcherService.maxRetryInterval = storage.maxOperationRetryInterval
  38. fetcherService.testBlock = testBlock
  39. let authorizer = StorageTokenAuthorizer(
  40. googleAppID: app.options.googleAppID,
  41. callbackQueue: storage.callbackQueue,
  42. authProvider: storage.auth,
  43. appCheck: storage.appCheck
  44. )
  45. fetcherService.authorizer = authorizer
  46. if storage.usesEmulator {
  47. fetcherService.allowLocalhostRequest = true
  48. fetcherService.allowedInsecureSchemes = ["http"]
  49. }
  50. setMap(appName: app.name, bucket: storage.storageBucket, fetcher: fetcherService)
  51. return fetcherService
  52. }
  53. }
  54. /// Update the testBlock for unit testing. Save it as a property since this may be called before
  55. /// fetcherService is initialized.
  56. func updateTestBlock(_ block: GTMSessionFetcherTestBlock?) {
  57. testBlock = block
  58. if let _fetcherService {
  59. _fetcherService.testBlock = testBlock
  60. }
  61. }
  62. private var testBlock: GTMSessionFetcherTestBlock?
  63. private var retryWhenOffline: GTMSessionFetcherRetryBlock = {
  64. (suggestedWillRetry: Bool,
  65. error: Error?,
  66. response: @escaping GTMSessionFetcherRetryResponse) in
  67. var shouldRetry = suggestedWillRetry
  68. // GTMSessionFetcher does not consider being offline a retryable error, but we do, so we
  69. // special-case it here.
  70. if !shouldRetry, error != nil {
  71. shouldRetry = (error as? NSError)?.code == URLError.notConnectedToInternet.rawValue
  72. }
  73. response(shouldRetry)
  74. }
  75. /// Map of apps to a dictionary of buckets to GTMSessionFetcherService.
  76. private var fetcherServiceMap: [String: [String: GTMSessionFetcherService]] = [:]
  77. private func getFromMap(appName: String, bucket: String) -> GTMSessionFetcherService? {
  78. return fetcherServiceMap[appName]?[bucket]
  79. }
  80. private func setMap(appName: String, bucket: String, fetcher: GTMSessionFetcherService) {
  81. fetcherServiceMap[appName, default: [:]][bucket] = fetcher
  82. }
  83. }