Caps-iOS/Caps/Data/Upload.swift
2022-04-28 15:54:13 +02:00

213 lines
7.1 KiB
Swift

//
// 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<Int>("cap")
let rowCapVersion = Expression<Int>("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<Int> {
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 { }