// // Upload.swift // CapCollector // // Created by Christoph on 26.04.20. // Copyright © 2020 CH. All rights reserved. // import Foundation import UIKit import SQLite struct Upload { static let offlineKey = "offline" let serverUrl: URL let table = Table("uploads") let rowCapId = Expression("cap") let rowCapVersion = Expression("version") init(server: URL) { self.serverUrl = server } // MARK: Paths var serverImageUrl: URL { serverUrl.appendingPathComponent("images") } private func serverImageUrl(for cap: Int, version: Int = 0) -> URL { serverImageUrl.appendingPathComponent("\(cap)/\(cap)-\(version).jpg") } private func serverImageUploadUrl(for cap: Int) -> URL { serverImageUrl.appendingPathComponent("\(cap)") } private func serverNameUploadUrl(for cap: Int) -> URL { serverUrl.appendingPathComponent("name/\(cap)") } private func serverChangeMainImageUrl(for cap: Int, to newValue: Int) -> URL { serverUrl.appendingPathComponent("switch/\(cap)/\(newValue)") } // MARK: SQLite var createQuery: String { table.create(ifNotExists: true) { t in t.column(rowCapId) t.column(rowCapVersion) } } func existsQuery(for cap: Int, version: Int) -> ScalarQuery { table.filter(rowCapId == cap && rowCapVersion == version).count } func insertQuery(for cap: Int, version: Int) -> Insert { table.insert(rowCapId <- cap, rowCapVersion <- version) } func deleteQuery(for cap: Int, version: Int) -> Delete { table.filter(rowCapId == cap && rowCapVersion == version).delete() } // MARK: Uploading data func upload(name: String, for cap: Int, completion: @escaping (_ success: Bool) -> Void) { var request = URLRequest(url: serverNameUploadUrl(for: cap)) request.httpMethod = "POST" request.httpBody = name.data(using: .utf8) let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { self.log("Failed to upload name of cap \(cap): \(error)") completion(false) return } guard let response = response else { self.log("Failed to upload name of cap \(cap): No response") completion(false) return } guard let urlResponse = response as? HTTPURLResponse else { self.log("Failed to upload name of cap \(cap): \(response)") completion(false) return } guard urlResponse.statusCode == 200 else { self.log("Failed to upload name of cap \(cap): Response \(urlResponse.statusCode)") completion(false) return } completion(true) } task.resume() } func upload(_ cap: Cap, timeout: TimeInterval = 30) -> Bool { upload(name: cap.name, for: cap.id, timeout: timeout) } func upload(name: String, for cap: Int, timeout: TimeInterval = 30) -> Bool { let group = DispatchGroup() group.enter() var result = true upload(name: name, for: cap) { success in if success { self.log("Uploaded cap \(cap)") } else { result = false } group.leave() } guard group.wait(timeout: .now() + timeout) == .success else { log("Timed out uploading cap \(cap)") return false } return result } func upload(imageAt url: URL, for cap: Int, completion: @escaping (_ count: Int?) -> Void) { var request = URLRequest(url: serverImageUploadUrl(for: cap)) request.httpMethod = "POST" let task = URLSession.shared.uploadTask(with: request, fromFile: url) { data, response, error in if let error = error { self.log("Failed to upload image of cap \(cap): \(error)") completion(nil) return } guard let response = response else { self.log("Failed to upload image of cap \(cap): No response") completion(nil) return } guard let urlResponse = response as? HTTPURLResponse else { self.log("Failed to upload image of cap \(cap): \(response)") completion(nil) return } guard urlResponse.statusCode == 200 else { self.log("Failed to upload image of cap \(cap): Response \(urlResponse.statusCode)") completion(nil) return } guard let d = data, let string = String(data: d, encoding: .utf8), let int = Int(string) else { self.log("Failed to upload image of cap \(cap): Invalid response") completion(nil) return } completion(int) } task.resume() } func upload(imageAt url: URL, of cap: Int, timeout: TimeInterval = 30) -> Int? { let group = DispatchGroup() group.enter() var result: Int? = nil upload(imageAt: url, for: cap) { count in result = count group.leave() } guard group.wait(timeout: .now() + timeout) == .success else { log("Timed out uploading image of \(cap)") return nil } return result } /** Sets the main image for a cap to a different version. - Parameter cap: The id of the cap - Parameter version: The version to set as the main version. - Parameter completion: A callback with the result on completion. */ func setMainImage(for cap: Int, to version: Int, completion: @escaping (_ success: Bool) -> Void) { let url = serverChangeMainImageUrl(for: cap, to: version) let task = URLSession.shared.dataTask(with: url) { data, response, error in if let error = error { self.log("Failed to set main image of cap \(cap) to \(version): \(error)") completion(false) return } guard let response = response else { self.log("Failed to set main image of cap \(cap) to \(version): No response") completion(false) return } guard let urlResponse = response as? HTTPURLResponse else { self.log("Failed to set main image of cap \(cap) to \(version): \(response)") completion(false) return } guard urlResponse.statusCode == 200 else { self.log("Failed to set main image of cap \(cap) to \(version): Response \(urlResponse.statusCode)") completion(false) return } completion(true) } task.resume() } } extension Upload: Logger { }