Skip to content

Commit 00fb453

Browse files
committed
Convert Storage Task implementation to Swift (#9880)
1 parent d01b2b1 commit 00fb453

File tree

87 files changed

+2988
-4560
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+2988
-4560
lines changed

FirebaseStorage.podspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Firebase Storage provides robust, secure file uploads and downloads from Firebas
3333
s.prefix_header_file = false
3434

3535
s.source_files = [
36-
'FirebaseStorage/Sources/*.swift',
36+
'FirebaseStorage/Sources/**/*.swift',
3737
'FirebaseStorage/Typedefs/*.h',
3838
]
3939

@@ -42,6 +42,7 @@ Firebase Storage provides robust, secure file uploads and downloads from Firebas
4242
s.dependency 'FirebaseAuthInterop', '~> 9.0'
4343
s.dependency 'FirebaseCore', '~> 9.0'
4444
s.dependency 'FirebaseCoreExtension', '~> 9.0'
45+
s.dependency 'GTMSessionFetcher/Core', '>= 1.7', '< 3.0'
4546

4647
s.test_spec 'ObjCIntegration' do |objc_tests|
4748
objc_tests.scheme = { :code_coverage => true }
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2022 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+
15+
import Foundation
16+
17+
import FirebaseStorageInternal
18+
#if COCOAPODS
19+
import GTMSessionFetcher
20+
#else
21+
import GTMSessionFetcherCore
22+
#endif
23+
24+
/**
25+
* Task which provides the ability to delete an object in Firebase Storage.
26+
*/
27+
internal class StorageDeleteTask: StorageTask, StorageTaskManagement {
28+
private var fetcher: GTMSessionFetcher?
29+
private var fetcherCompletion: ((Data?, NSError?) -> Void)?
30+
private var taskCompletion: ((_ error: Error?) -> Void)?
31+
32+
internal init(reference: FIRIMPLStorageReference,
33+
fetcherService: GTMSessionFetcherService,
34+
queue: DispatchQueue,
35+
completion: ((_: Error?) -> Void)?) {
36+
super.init(reference: reference, service: fetcherService, queue: queue)
37+
taskCompletion = completion
38+
}
39+
40+
deinit {
41+
self.fetcher?.stopFetching()
42+
}
43+
44+
/**
45+
* Prepares a task and begins execution.
46+
*/
47+
internal func enqueue() {
48+
weak var weakSelf = self
49+
DispatchQueue.global(qos: .background).async {
50+
guard let strongSelf = weakSelf else { return }
51+
strongSelf.state = .queueing
52+
var request = strongSelf.baseRequest
53+
request.httpMethod = "DELETE"
54+
request.timeoutInterval = strongSelf.reference.storage.maxOperationRetryTime
55+
56+
let callback = strongSelf.taskCompletion
57+
strongSelf.taskCompletion = nil
58+
59+
let fetcher = strongSelf.fetcherService.fetcher(with: request)
60+
fetcher.comment = "DeleteTask"
61+
strongSelf.fetcher = fetcher
62+
63+
strongSelf.fetcherCompletion = { (data: Data?, error: NSError?) in
64+
if let error = error, self.error == nil {
65+
self.error = StorageErrorCode.error(withServerError: error, ref: strongSelf.reference)
66+
}
67+
if let callback = callback {
68+
callback(self.error)
69+
}
70+
self.fetcherCompletion = nil
71+
}
72+
73+
strongSelf.fetcher?.beginFetch { data, error in
74+
let strongSelf = weakSelf
75+
if let fetcherCompletion = strongSelf?.fetcherCompletion {
76+
fetcherCompletion(data, error as? NSError)
77+
}
78+
}
79+
}
80+
}
81+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright 2022 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+
15+
import Foundation
16+
17+
import FirebaseStorageInternal
18+
#if COCOAPODS
19+
import GTMSessionFetcher
20+
#else
21+
import GTMSessionFetcherCore
22+
#endif
23+
24+
/**
25+
* Task which provides the ability to get a download URL for an object in Firebase Storage.
26+
*/
27+
internal class StorageGetDownloadURLTask: StorageTask, StorageTaskManagement {
28+
private var fetcher: GTMSessionFetcher?
29+
private var fetcherCompletion: ((Data?, NSError?) -> Void)?
30+
private var taskCompletion: ((_ downloadURL: URL?, _: Error?) -> Void)?
31+
32+
internal init(reference: FIRIMPLStorageReference,
33+
fetcherService: GTMSessionFetcherService,
34+
queue: DispatchQueue,
35+
completion: ((_: URL?, _: Error?) -> Void)?) {
36+
super.init(reference: reference, service: fetcherService, queue: queue)
37+
taskCompletion = completion
38+
}
39+
40+
deinit {
41+
self.fetcher?.stopFetching()
42+
}
43+
44+
/**
45+
* Prepares a task and begins execution.
46+
*/
47+
internal func enqueue() {
48+
weak var weakSelf = self
49+
DispatchQueue.global(qos: .background).async {
50+
guard let strongSelf = weakSelf else { return }
51+
var request = strongSelf.baseRequest
52+
request.httpMethod = "GET"
53+
request.timeoutInterval = strongSelf.reference.storage.maxOperationRetryTime
54+
55+
let callback = strongSelf.taskCompletion
56+
strongSelf.taskCompletion = nil
57+
58+
let fetcher = strongSelf.fetcherService.fetcher(with: request)
59+
fetcher.comment = "GetDownloadURLTask"
60+
strongSelf.fetcher = fetcher
61+
62+
strongSelf.fetcherCompletion = { (data: Data?, error: NSError?) in
63+
var downloadURL: URL?
64+
if let error = error {
65+
if self.error == nil {
66+
self.error = StorageErrorCode.error(withServerError: error, ref: self.reference)
67+
}
68+
} else {
69+
if let data = data,
70+
let responseDictionary = try? JSONSerialization
71+
.jsonObject(with: data) as? [String: String] {
72+
downloadURL = strongSelf.downloadURLFromMetadataDictionary(responseDictionary)
73+
if downloadURL == nil {
74+
self.error = NSError(domain: StorageErrorDomain,
75+
code: StorageErrorCode.unknown.rawValue,
76+
userInfo: [NSLocalizedDescriptionKey:
77+
"Failed to retrieve a download URL."])
78+
}
79+
} else {
80+
self.error = StorageErrorCode.error(withInvalidRequest: data)
81+
}
82+
}
83+
if let callback = callback {
84+
callback(downloadURL, self.error)
85+
}
86+
self.fetcherCompletion = nil
87+
}
88+
89+
strongSelf.fetcher?.beginFetch { data, error in
90+
let strongSelf = weakSelf
91+
if let fetcherCompletion = strongSelf?.fetcherCompletion {
92+
fetcherCompletion(data, error as? NSError)
93+
}
94+
}
95+
}
96+
}
97+
98+
internal func downloadURLFromMetadataDictionary(_ dictionary: [String: String]) -> URL? {
99+
let downloadTokens = dictionary["downloadTokens"]
100+
guard let downloadTokens = downloadTokens,
101+
downloadTokens.count > 0 else {
102+
return nil
103+
}
104+
let downloadTokenArray = downloadTokens.components(separatedBy: ",")
105+
let bucket = dictionary["bucket"] ?? "<error: missing bucket>"
106+
let path = dictionary["name"] ?? "<error: missing path name>"
107+
let fullPath = "/v0/b/\(bucket)/o/\(StorageUtils.GCSEscapedString(path))"
108+
var components = URLComponents()
109+
components.scheme = reference.storage.scheme
110+
components.host = reference.storage.host
111+
components.port = reference.storage.port
112+
components.percentEncodedPath = fullPath
113+
114+
// The backend can return an arbitrary number of download tokens, but we only expose the first
115+
// token via the download URL.
116+
let altItem = URLQueryItem(name: "alt", value: "media")
117+
let tokenItem = URLQueryItem(name: "token", value: downloadTokenArray[0])
118+
components.queryItems = [altItem, tokenItem]
119+
return components.url
120+
}
121+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2022 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+
15+
import Foundation
16+
17+
import FirebaseStorageInternal
18+
#if COCOAPODS
19+
import GTMSessionFetcher
20+
#else
21+
import GTMSessionFetcherCore
22+
#endif
23+
24+
/**
25+
* Task which provides the ability to delete an object in Firebase Storage.
26+
*/
27+
internal class StorageGetMetadataTask: StorageTask, StorageTaskManagement {
28+
private var fetcher: GTMSessionFetcher?
29+
private var fetcherCompletion: ((Data?, NSError?) -> Void)?
30+
private var taskCompletion: ((_ metadata: StorageMetadata?, _: Error?) -> Void)?
31+
32+
internal init(reference: FIRIMPLStorageReference,
33+
fetcherService: GTMSessionFetcherService,
34+
queue: DispatchQueue,
35+
completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
36+
super.init(reference: reference, service: fetcherService, queue: queue)
37+
taskCompletion = completion
38+
}
39+
40+
deinit {
41+
self.fetcher?.stopFetching()
42+
}
43+
44+
/**
45+
* Prepares a task and begins execution.
46+
*/
47+
internal func enqueue() {
48+
weak var weakSelf = self
49+
DispatchQueue.global(qos: .background).async {
50+
guard let strongSelf = weakSelf else { return }
51+
strongSelf.state = .queueing
52+
var request = strongSelf.baseRequest
53+
request.httpMethod = "GET"
54+
request.timeoutInterval = strongSelf.reference.storage.maxOperationRetryTime
55+
56+
let callback = strongSelf.taskCompletion
57+
strongSelf.taskCompletion = nil
58+
59+
let fetcher = strongSelf.fetcherService.fetcher(with: request)
60+
fetcher.comment = "GetMetadataTask"
61+
strongSelf.fetcher = fetcher
62+
63+
strongSelf.fetcherCompletion = { (data: Data?, error: NSError?) in
64+
var metadata: StorageMetadata?
65+
if let error = error {
66+
if self.error == nil {
67+
self.error = StorageErrorCode.error(withServerError: error, ref: self.reference)
68+
}
69+
} else {
70+
if let data = data,
71+
let responseDictionary = try? JSONSerialization
72+
.jsonObject(with: data) as? [String: Any] {
73+
metadata = StorageMetadata(dictionary: responseDictionary)
74+
metadata?.impl.type = .file
75+
} else {
76+
self.error = StorageErrorCode.error(withInvalidRequest: data)
77+
}
78+
}
79+
if let callback = callback {
80+
callback(metadata, self.error)
81+
}
82+
self.fetcherCompletion = nil
83+
}
84+
85+
strongSelf.fetcher?.beginFetch { data, error in
86+
let strongSelf = weakSelf
87+
if let fetcherCompletion = strongSelf?.fetcherCompletion {
88+
fetcherCompletion(data, error as? NSError)
89+
}
90+
}
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)