Repository: firebase/firebase-ios-sdk (language: cpp)

Issue:
Latest GTMSessionFetcher's new deprecation breaks Storage CI
See https://github.com/firebase/firebase-ios-sdk/pull/8292/checks?check_run_id=2898960029 and 

![Screen Shot 2021-06-23 at 1 47 16 PM](https://user-images.githubusercontent.com/73870/123165802-87154d80-d429-11eb-903a-f3fc93f3d28b.png)

From GTMsessionFetcherService.h:
// DEPRECATED: Callers should use XCTestExpectation instead.
//
// Spin the run loop and discard events (or, if not on the main thread, just sleep the thread)
// until all running and delayed fetchers have completed.
//
// This is only for use in testing or in tools without a user interface.
//
// Synchronous fetches should never be done by shipping apps; they are
// sufficient reason for rejection from the app store.

Given that comment, it seems we might need a more significant refactor than most warnings require.

cc: @thomasvl 


Discussion / hints:
@morganchen12 - Can you take care of this?
@mwyman fyi also

I believe the api docs have always said it should only be used in tests (or tools without UI).


Fix patch:
diff --git a/FirebaseStorage/Sources/FIRStorage.m b/FirebaseStorage/Sources/FIRStorage.m
index a78b13598d3..cfea0aeb1da 100644
--- a/FirebaseStorage/Sources/FIRStorage.m
+++ b/FirebaseStorage/Sources/FIRStorage.m
@@ -166,6 +166,7 @@ - (instancetype)initWithApp:(FIRApp *)app
     _scheme = kFIRStorageScheme;
     _port = @(kFIRStoragePort);
     _fetcherServiceForApp = nil;  // Configured in `ensureConfigured()`
+    // Must be a serial queue.
     _dispatchQueue = dispatch_queue_create("com.google.firebase.storage", DISPATCH_QUEUE_SERIAL);
     _maxDownloadRetryTime = 600.0;
     _maxDownloadRetryInterval =
diff --git a/FirebaseStorage/Sources/FIRStorageDownloadTask.m b/FirebaseStorage/Sources/FIRStorageDownloadTask.m
index 7d53ea604e0..96714953719 100644
--- a/FirebaseStorage/Sources/FIRStorageDownloadTask.m
+++ b/FirebaseStorage/Sources/FIRStorageDownloadTask.m
@@ -165,13 +165,23 @@ - (void)cancelWithError:(NSError *)error {
 - (void)pause {
   __weak FIRStorageDownloadTask *weakSelf = self;
   [self dispatchAsync:^() {
-    weakSelf.state = FIRStorageTaskStatePausing;
-    [weakSelf.fetcher stopFetching];
-    // Give the resume callback a chance to run (if scheduled)
-    [weakSelf.fetcher waitForCompletionWithTimeout:0.001];
-    weakSelf.state = FIRStorageTaskStatePaused;
-    FIRStorageTaskSnapshot *snapshot = weakSelf.snapshot;
-    [weakSelf fireHandlersForStatus:FIRStorageTaskStatusPause snapshot:snapshot];
+    __strong FIRStorageDownloadTask *strongSelf = weakSelf;
+    if (!strongSelf || strongSelf.state == FIRStorageTaskStatePaused ||
+        strongSelf.state == FIRStorageTaskStatePausing) {
+      return;
+    }
+    strongSelf.state = FIRStorageTaskStatePausing;
+    // Use the resume callback to confirm pause status since it always runs after the last
+    // NSURLSession update.
+    [strongSelf.fetcher setResumeDataBlock:^(NSData *data) {
+      // Silence compiler warning about retain cycles
+      __strong __typeof(self) strong = weakSelf;
+      strong->_downloadData = data;
+      strong.state = FIRStorageTaskStatePaused;
+      FIRStorageTaskSnapshot *snapshot = strong.snapshot;
+      [strong fireHandlersForStatus:FIRStorageTaskStatusPause snapshot:snapshot];
+    }];
+    [strongSelf.fetcher stopFetching];
   }];
 }
 


Test patch: