Javier Cicchelli 9bcdaa697b [Setup] Basic project structure (#1)
This PR contains all the work related to setting up this project as required to implement the [Assignment](https://repo.rock-n-code.com/rock-n-code/deep-linking-assignment/wiki/Assignment) on top, as intended.

To summarise this work:
- [x] created a new **Xcode** project;
- [x] cloned the `Wikipedia` app and inserted it into the **Xcode** project;
- [x] created the `Locations` app and also, its `Libraries` package;
- [x] created the `Shared` package to share dependencies between the apps;
- [x] added a `Makefile` file and implemented some **environment** and **help** commands.

Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Reviewed-on: rock-n-code/deep-linking-assignment#1
2023-04-08 18:37:13 +00:00

146 lines
6.6 KiB
Swift

import Foundation
public struct CacheFetchingResult {
let data: Data
let response: URLResponse
}
enum CacheFetchingError: Error {
case missingDataAndURLResponse
case missingURLResponse
case unableToDetermineURLRequest
}
public protocol CacheFetching {
typealias TemporaryFileURL = URL
typealias MIMEType = String
typealias DownloadCompletion = (Error?, URLRequest?, URLResponse?, TemporaryFileURL?, MIMEType?) -> Void
typealias DataCompletion = (Result<CacheFetchingResult, Error>) -> Void
// internally populates urlRequest with cache header fields
func dataForURL(_ url: URL, persistType: Header.PersistItemType, headers: [String: String], completion: @escaping DataCompletion) -> URLSessionTask?
// assumes urlRequest is already populated with cache header fields
func dataForURLRequest(_ urlRequest: URLRequest, completion: @escaping DataCompletion) -> URLSessionTask?
// Session Passthroughs
func cachedResponseForURL(_ url: URL, type: Header.PersistItemType) -> CachedURLResponse?
func cachedResponseForURLRequest(_ urlRequest: URLRequest) -> CachedURLResponse? // assumes urlRequest is already populated with the proper cache headers
func uniqueKeyForURL(_ url: URL, type: Header.PersistItemType) -> String?
func cacheResponse(httpUrlResponse: HTTPURLResponse, content: CacheResponseContentType, urlRequest: URLRequest, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
func uniqueFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String?
func uniqueHeaderFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String?
func uniqueFileNameForURLRequest(_ urlRequest: URLRequest) -> String?
func itemKeyForURLRequest(_ urlRequest: URLRequest) -> String?
func variantForURLRequest(_ urlRequest: URLRequest) -> String?
// Bundled migration only - copies files into cache
func writeBundledFiles(mimeType: String, bundledFileURL: URL, urlRequest: URLRequest, completion: @escaping (Result<Void, Error>) -> Void)
}
extension CacheFetching where Self:Fetcher {
@discardableResult public func dataForURLRequest(_ urlRequest: URLRequest, completion: @escaping DataCompletion) -> URLSessionTask? {
let task = session.dataTask(with: urlRequest) { (data, urlResponse, error) in
if let error = error {
completion(.failure(error))
return
}
guard let unwrappedResponse = urlResponse else {
completion(.failure(CacheFetchingError.missingURLResponse))
return
}
if let httpResponse = unwrappedResponse as? HTTPURLResponse, httpResponse.statusCode != 200 {
completion(.failure(RequestError.unexpectedResponse))
return
}
if let data = data,
let urlResponse = urlResponse {
let result = CacheFetchingResult(data: data, response: urlResponse)
completion(.success(result))
} else {
completion(.failure(CacheFetchingError.missingDataAndURLResponse))
}
}
task?.resume()
return task
}
@discardableResult public func dataForURL(_ url: URL, persistType: Header.PersistItemType, headers: [String: String] = [:], completion: @escaping DataCompletion) -> URLSessionTask? {
guard let urlRequest = session.urlRequestFromPersistence(with: url, persistType: persistType, headers: headers) else {
completion(.failure(CacheFetchingError.unableToDetermineURLRequest))
return nil
}
return dataForURLRequest(urlRequest, completion: completion)
}
}
// MARK: Session Passthroughs
extension CacheFetching where Self:Fetcher {
public func cachedResponseForURL(_ url: URL, type: Header.PersistItemType) -> CachedURLResponse? {
return session.cachedResponseForURL(url, type: type)
}
public func cachedResponseForURLRequest(_ urlRequest: URLRequest) -> CachedURLResponse? {
return session.cachedResponseForURLRequest(urlRequest)
}
public func uniqueFileNameForURLRequest(_ urlRequest: URLRequest) -> String? {
return session.uniqueFileNameForURLRequest(urlRequest)
}
public func uniqueKeyForURL(_ url: URL, type: Header.PersistItemType) -> String? {
return session.uniqueKeyForURL(url, type: type)
}
public func cacheResponse(httpUrlResponse: HTTPURLResponse, content: CacheResponseContentType, urlRequest: URLRequest, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
session.cacheResponse(httpUrlResponse: httpUrlResponse, content: content, urlRequest: urlRequest, success: success, failure: failure)
}
public func writeBundledFiles(mimeType: String, bundledFileURL: URL, urlRequest: URLRequest, completion: @escaping (Result<Void, Error>) -> Void) {
session.writeBundledFiles(mimeType: mimeType, bundledFileURL: bundledFileURL, urlRequest: urlRequest, completion: completion)
}
public func uniqueFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String? {
return session.uniqueFileNameForItemKey(itemKey, variant: variant)
}
public func itemKeyForURLRequest(_ urlRequest: URLRequest) -> String? {
return session.itemKeyForURLRequest(urlRequest)
}
public func variantForURLRequest(_ urlRequest: URLRequest) -> String? {
return session.variantForURLRequest(urlRequest)
}
public func itemKeyForURL(_ url: URL, type: Header.PersistItemType) -> String? {
return session.itemKeyForURL(url, type: type)
}
public func variantForURL(_ url: URL, type: Header.PersistItemType) -> String? {
return session.variantForURL(url, type: type)
}
public func urlRequestFromPersistence(with url: URL, persistType: Header.PersistItemType, cachePolicy: WMFCachePolicy? = nil, headers: [String: String] = [:]) -> URLRequest? {
return session.urlRequestFromPersistence(with: url, persistType: persistType, cachePolicy: cachePolicy, headers: headers)
}
public func uniqueHeaderFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String? {
return session.uniqueHeaderFileNameForItemKey(itemKey, variant: variant)
}
public func isCachedWithURLRequest(_ request: URLRequest, completion: @escaping (Bool) -> Void) {
return session.isCachedWithURLRequest(request, completion: completion)
}
}