Skip to content

Instantly share code, notes, and snippets.

@strzempa
Last active October 24, 2019 14:20
Show Gist options
  • Save strzempa/4cc3181ff86f5527ab4e451697477c2f to your computer and use it in GitHub Desktop.
Save strzempa/4cc3181ff86f5527ab4e451697477c2f to your computer and use it in GitHub Desktop.
Purpose of this service is synchronizing an urlSession delegate to have result accessible in single method where we pass a remote url and retreive a local url in closure.
import Foundation
protocol FileService {
var observation: Any? { get set }
var session: URLSession { get set }
var fileManager: FileManager { get set }
func fetchAndSaveLocally(url: URL, _ completion: @escaping (_ result: Result<URL, ServiceError>) -> Void)
}
final class DefaultFileService: NSObject, FileService {
var observation: Any?
lazy var session: URLSession = URLSession(configuration: .default, delegate: self, delegateQueue: operationQueue)
lazy var fileManager: FileManager = FileManager.default
@objc private dynamic var pdfURL: URL?
private var error: Error?
private lazy var operationQueue: OperationQueue = OperationQueue()
func fetchAndSaveLocally(url: URL, _ completion: @escaping (_ result: Result<URL, ServiceError>) -> Void) {
let observationHandlerTask = observationHandler()
let fetchHandlerTask = fetchHandler(session: session)
observation = observe(\.pdfURL, options: [.new], changeHandler: { fileService, _ in
observationHandlerTask(fileService, completion)
})
fetchHandlerTask(url)
}
}
private extension DefaultFileService {
func fetchHandler(session: URLSession) -> (_ url: URL) -> Void {
return { url in
session.downloadTask(with: url).resume()
}
}
typealias ObservationHandlerBlock = (_ fileService: DefaultFileService, _ completion: @escaping (_ result: Result<URL, ServiceError>) -> Void) -> Void
func observationHandler() -> ObservationHandlerBlock {
return { fileService, completion in
guard let pdfURL = fileService.pdfURL else {
completion(.failure(ServiceError.fetchingFailure("no pdfURL saved, possible error \(fileService.error?.localizedDescription ?? "")")))
return
}
completion(.success(pdfURL))
}
}
}
extension DefaultFileService: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
/// create destination URL with the original file name
guard let url = downloadTask.originalRequest?.url else {
self.pdfURL = nil
self.error = ServiceError.noExpectedData("downloadTask.originalRequest.url not retreived")
return
}
let documentsPath = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationURL = documentsPath.appendingPathComponent(url.lastPathComponent)
/// delete original copy
try? FileManager.default.removeItem(at: destinationURL)
/// copy from temp to Document
do {
try FileManager.default.copyItem(at: location, to: destinationURL)
self.pdfURL = destinationURL
} catch let error {
self.pdfURL = nil
self.error = error
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment