Skip to content

Commit c3813c3

Browse files
authored
Merge pull request #366 from Esri/Caleb/Update-DownloadPortalItemScriptAsync
[Update] Remove `DowloadPortalItemData` script GCD
2 parents 77ed1a1 + 94898f1 commit c3813c3

File tree

1 file changed

+81
-86
lines changed

1 file changed

+81
-86
lines changed

Scripts/DowloadPortalItemData.swift

Lines changed: 81 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -126,61 +126,47 @@ func uncompressArchive(at sourceURL: URL, to destinationURL: URL) throws {
126126
process.waitUntilExit()
127127
}
128128

129-
/// Downloads file from portal and write the file(s) to appropriate path(s).
129+
/// Downloads a file from a URL to a given location.
130130
/// - Parameters:
131131
/// - sourceURL: The portal URL to the resource.
132-
/// - downloadDirectory: The directory that stores downloaded data.
133-
/// - completion: A closure to handle the results.
134-
func downloadFile(at sourceURL: URL, to downloadDirectory: URL, completion: @escaping (Result<URL, Error>) -> Void) {
135-
let downloadTaskCompleted = { (temporaryURL: URL?, response: URLResponse?, error: Error?) in
136-
if let temporaryURL = temporaryURL,
137-
let response = response,
138-
let suggestedFilename = response.suggestedFilename {
139-
do {
140-
let downloadName: String
141-
let isArchive = (suggestedFilename as NSString).pathExtension == "zip"
142-
// If the downloaded file is an archive and contains
143-
// - 1 file, use the name of that file.
144-
// - multiple files, use the suggested filename (*.zip).
145-
// If it is not an archive, use the server suggested filename.
146-
if isArchive {
147-
let count = try count(ofFilesInArchiveAt: temporaryURL)
148-
if count == 1 {
149-
downloadName = try name(ofFileInArchiveAt: temporaryURL)
150-
} else {
151-
downloadName = suggestedFilename
152-
}
153-
} else {
154-
downloadName = suggestedFilename
155-
}
156-
157-
let downloadURL = downloadDirectory.appendingPathComponent(downloadName, isDirectory: false)
158-
159-
if FileManager.default.fileExists(atPath: downloadURL.path) {
160-
try FileManager.default.removeItem(at: downloadURL)
161-
}
162-
163-
if isArchive {
164-
let extractURL = downloadURL.pathExtension == "zip"
165-
// Uncompresses to directory named after archive.
166-
? downloadURL.deletingPathExtension()
167-
// Uncompresses to appropriate subdirectory.
168-
: downloadURL.deletingLastPathComponent()
169-
try uncompressArchive(at: temporaryURL, to: extractURL)
170-
} else {
171-
try FileManager.default.moveItem(at: temporaryURL, to: downloadURL)
172-
}
173-
174-
completion(.success(downloadURL))
175-
} catch {
176-
completion(.failure(error))
177-
}
178-
} else if let error = error {
179-
completion(.failure(error))
132+
/// - downloadDirectory: The directory to store the downloaded data in.
133+
/// - Throws: Exceptions when downloading, naming, uncompressing, and moving the file.
134+
/// - Returns: The name of the downloaded file.
135+
func downloadFile(from sourceURL: URL, to downloadDirectory: URL) async throws -> String? {
136+
let (temporaryURL, response) = try await URLSession.shared.download(from: sourceURL)
137+
138+
guard let suggestedFilename = response.suggestedFilename else { return nil }
139+
let isArchive = NSString(string: suggestedFilename).pathExtension == "zip"
140+
141+
let downloadName: String = try {
142+
// If the downloaded file is an archive and contains
143+
// - 1 file, use the name of that file.
144+
// - multiple files, use the server suggested filename (*.zip).
145+
// If it is not an archive, use the server suggested filename.
146+
if isArchive,
147+
try count(ofFilesInArchiveAt: temporaryURL) == 1 {
148+
return try name(ofFileInArchiveAt: temporaryURL)
149+
} else {
150+
return suggestedFilename
180151
}
152+
}()
153+
let downloadURL = downloadDirectory.appendingPathComponent(downloadName, isDirectory: false)
154+
155+
try? FileManager.default.removeItem(at: downloadURL)
156+
157+
if isArchive {
158+
let extractURL = downloadURL.pathExtension == "zip"
159+
// Uncompresses to directory named after archive.
160+
? downloadURL.deletingPathExtension()
161+
// Uncompresses to appropriate subdirectory.
162+
: downloadURL.deletingLastPathComponent()
163+
164+
try uncompressArchive(at: temporaryURL, to: extractURL)
165+
} else {
166+
try FileManager.default.moveItem(at: temporaryURL, to: downloadURL)
181167
}
182-
let downloadTask = URLSession.shared.downloadTask(with: sourceURL, completionHandler: downloadTaskCompleted)
183-
downloadTask.resume()
168+
169+
return downloadName
184170
}
185171

186172
// MARK: Script Entry
@@ -242,45 +228,54 @@ let previousDownloadedItems: DownloadedItems = {
242228
}()
243229
var downloadedItems = previousDownloadedItems
244230

245-
// Asynchronously downloads portal items.
246-
let dispatchGroup = DispatchGroup()
247-
248-
for portalItem in portalItems {
249-
// Checks to see if an item is already downloaded.
250-
guard downloadedItems[portalItem.identifier] == nil else {
251-
print("note: Item already downloaded: \(portalItem.identifier)")
252-
continue
253-
}
254-
255-
let destinationURL = downloadDirectoryURL.appendingPathComponent(portalItem.identifier, isDirectory: true)
256-
257-
// Deletes the directory when the item is not in the plist.
258-
try? FileManager.default.removeItem(at: destinationURL)
259-
260-
do {
261-
// Creates an enclosing directory with portal item ID as its name.
262-
try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: false)
263-
} catch {
264-
print("error: Error creating download directory: \(error.localizedDescription)")
265-
exit(1)
266-
}
267-
268-
print("note: Downloading item \(portalItem.identifier)")
269-
fflush(stdout)
270-
dispatchGroup.enter()
271-
downloadFile(at: portalItem.dataURL, to: destinationURL) { result in
272-
switch result {
273-
case .success(let url):
274-
downloadedItems[portalItem.identifier] = url.lastPathComponent
275-
dispatchGroup.leave()
276-
case .failure(let error):
277-
print("error: Error downloading item \(portalItem.identifier): \(error.localizedDescription)")
278-
URLSession.shared.invalidateAndCancel()
231+
await withTaskGroup(of: Void.self) { group in
232+
for portalItem in portalItems {
233+
let destinationURL = downloadDirectoryURL.appendingPathComponent(
234+
portalItem.identifier,
235+
isDirectory: true
236+
)
237+
238+
// Checks to see if an item needs downloading.
239+
guard downloadedItems[portalItem.identifier] == nil ||
240+
!FileManager.default.fileExists(atPath: destinationURL.path) else {
241+
print("note: Item already downloaded: \(portalItem.identifier)")
242+
continue
243+
}
244+
245+
// Deletes the directory when the item is not in the plist.
246+
try? FileManager.default.removeItem(at: destinationURL)
247+
248+
do {
249+
// Creates an enclosing directory with the portal item ID as its name.
250+
try FileManager.default.createDirectory(
251+
at: destinationURL,
252+
withIntermediateDirectories: false
253+
)
254+
} catch {
255+
print("error: Error creating download directory: \(error.localizedDescription)")
279256
exit(1)
280257
}
258+
259+
group.addTask {
260+
do {
261+
guard let downloadName = try await downloadFile(
262+
from: portalItem.dataURL,
263+
to: destinationURL
264+
) else { return }
265+
print("note: Downloaded item: \(portalItem.identifier)")
266+
fflush(stdout)
267+
268+
_ = await MainActor.run {
269+
downloadedItems.updateValue(downloadName, forKey: portalItem.identifier)
270+
}
271+
} catch {
272+
print("error: Error downloading item \(portalItem.identifier): \(error.localizedDescription)")
273+
URLSession.shared.invalidateAndCancel()
274+
exit(1)
275+
}
276+
}
281277
}
282278
}
283-
dispatchGroup.wait()
284279

285280
// Updates the downloaded items property list record if needed.
286281
if downloadedItems != previousDownloadedItems {

0 commit comments

Comments
 (0)