From 746b69defc1191131046fdb356732ba76502bc9d Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sun, 10 Jan 2021 16:11:31 +0100 Subject: [PATCH] Fix port, begin upload UI --- .../UserInterfaceState.xcuserstate | Bin 43128 -> 40241 bytes CapCollector/AppDelegate.swift | 2 +- CapCollector/Base.lproj/Main.storyboard | 5 +- CapCollector/Data/Database.swift | 120 +++++++++++++----- CapCollector/Data/Download.swift | 56 +++----- CapCollector/Data/Storage.swift | 6 +- CapCollector/Info.plist | 11 -- .../Presentation/GridViewController.swift | 4 +- CapCollector/Presentation/ImageSelector.swift | 43 +++---- CapCollector/TableView.swift | 13 +- CapCollector/View Components/CropView.swift | 3 +- 11 files changed, 142 insertions(+), 121 deletions(-) diff --git a/CapCollector.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate b/CapCollector.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate index b15d0c88e93fe52d20d54f504e14ada5f0760924..0577e13cd7f7befdc935adab929d237592256af7 100644 GIT binary patch delta 21035 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 rHIbLmYfB8mk_ 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 diff --git a/CapCollector/AppDelegate.swift b/CapCollector/AppDelegate.swift index 3750df9..64f38f4 100644 --- a/CapCollector/AppDelegate.swift +++ b/CapCollector/AppDelegate.swift @@ -60,7 +60,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } - let serverUrl = URL(string: "https://christophhagen.de")! + let serverUrl = URL(string: "https://christophhagen.de:6000")! func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { app = self diff --git a/CapCollector/Base.lproj/Main.storyboard b/CapCollector/Base.lproj/Main.storyboard index 5f90789..ba932a3 100644 --- a/CapCollector/Base.lproj/Main.storyboard +++ b/CapCollector/Base.lproj/Main.storyboard @@ -1,9 +1,8 @@ - + - - + diff --git a/CapCollector/Data/Database.swift b/CapCollector/Data/Database.swift index 6c6c7ab..a49b4c9 100644 --- a/CapCollector/Data/Database.swift +++ b/CapCollector/Data/Database.swift @@ -128,13 +128,23 @@ final class Database { var pendingCapUploads: [Cap] { do { - return try db.prepare(Cap.table.filter(Cap.columnUploaded == false)).map(Cap.init) + return try db.prepare(Cap.table.filter(Cap.columnUploaded == false).order(Cap.columnId.asc)).map(Cap.init) } catch { log("Failed to get pending cap uploads") return [] } } + var hasPendingCapUploads: Bool { + do { + let query = Cap.table.filter(Cap.columnUploaded == false).count + return try db.scalar(query) > 0 + } catch { + log("Failed to get pending cap upload count") + return false + } + } + var classifierVersion: Int { set { UserDefaults.standard.set(newValue, forKey: Classifier.userDefaultsKey) @@ -409,24 +419,17 @@ final class Database { // MARK: Downloads @discardableResult - func downloadMainImage(for cap: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool { - return download.mainImage(for: cap) { success in - guard success else { - completion(false) - return + func downloadImage(for cap: Int, version: Int = 0, completion: @escaping (_ image: UIImage?) -> Void) -> Bool { + return download.image(for: cap, version: version) { image in + if version == 0 && image != nil { + DispatchQueue.main.async { + self.delegate?.database(didLoadImageForCap: cap) + } } - DispatchQueue.main.async { - self.delegate?.database(didLoadImageForCap: cap) - } - completion(true) + completion(image) } } - @discardableResult - func downloadImage(for cap: Int, version: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool { - return download.image(for: cap, version: version, completion: completion) - } - func downloadCapNames(completion: @escaping (_ success: Bool) -> Void) { log("Downloading cap names") download.names { names in @@ -496,7 +499,7 @@ final class Database { DispatchQueue.global(qos: .userInitiated).async { for part in caps.split(intoPartsOf: split) { for id in part { - let downloading = self.downloadMainImage(for: id) { _ in + let downloading = self.downloadImage(for: id) { _ in group.leave() } if downloading { @@ -658,6 +661,24 @@ final class Database { self.update(count: count, for: cap) } } + + private func uploadNextItem() { + let capUploads = self.pendingCapUploads + if let id = capUploads.first { + + return + } + let imageUploads = pendingImageUploads + guard imageUploads.count > 0 else { + log("No pending image uploads") + return + } + uploadRemainingImages() + } + + private func upload(cap: Int) { + + } func uploadRemainingData() { guard !isInOfflineMode else { @@ -673,23 +694,33 @@ final class Database { log("\(uploads.count) cap uploads pending") var remaining = uploads.count - for cap in uploads { - upload.upload(name: cap.name, for: cap.id) { success in - if success { - self.log("Uploaded cap \(cap.id)") - self.update(uploaded: true, for: cap.id) - } else { - self.log("Failed to upload cap \(cap.id)") - } - - remaining -= 1 - if remaining == 0 { - DispatchQueue.main.async { - self.uploadRemainingImages() + DispatchQueue.global(qos: .background).async { + let group = DispatchGroup() + for cap in uploads { + group.enter() + self.upload.upload(name: cap.name, for: cap.id) { success in + group.leave() + if success { + self.log("Uploaded cap \(cap.id)") + self.update(uploaded: true, for: cap.id) + } else { + self.log("Failed to upload cap \(cap.id)") + return } + + remaining -= 1 + + } + guard group.wait(timeout: .now() + .seconds(60)) == .success else { + self.log("Timed out uploading cap \(cap.id)") + return } } + DispatchQueue.main.async { + self.uploadRemainingImages() + } } + } private func uploadRemainingImages() { @@ -700,16 +731,35 @@ final class Database { } log("\(uploads.count) image uploads pending") - for (cap, version) in uploads { - upload.uploadImage(for: cap, version: version) { count in - guard let _ = count else { - self.log("Failed to upload version \(version) of cap \(cap)") + DispatchQueue.global(qos: .background).async { + let group = DispatchGroup() + for (id, version) in uploads { + guard let cap = self.cap(for: id) else { + self.log("No cap \(id) to upload image \(version)") + self.removePendingUpload(of: id, version: version) + continue + } + guard cap.uploaded else { + self.log("Cap \(id) not uploaded, skipping image upload") + continue + } + group.enter() + self.upload.uploadImage(for: id, version: version) { count in + group.leave() + guard let _ = count else { + self.log("Failed to upload version \(version) of cap \(id)") + return + } + self.log("Uploaded version \(version) of cap \(id)") + self.removePendingUpload(of: id, version: version) + } + guard group.wait(timeout: .now() + .seconds(60)) == .success else { + self.log("Timed out uploading version \(version) of cap \(id)") return } - self.log("Uploaded version \(version) of cap \(cap)") - self.removePendingUpload(of: cap, version: version) } } + } @discardableResult diff --git a/CapCollector/Data/Download.swift b/CapCollector/Data/Download.swift index b5f74d0..7cd10d5 100644 --- a/CapCollector/Data/Download.swift +++ b/CapCollector/Data/Download.swift @@ -116,59 +116,35 @@ final class Download { - Parameter cap: The id of the cap. - Parameter version: The image version to download. - Parameter completion: A closure with the resulting image - - Note: The closure will be called from the main queue. - Returns: `true`, of the file download was started, `false`, if the image is already downloading. */ @discardableResult - func mainImage(for cap: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool { - let url = serverImageUrl(for: cap) - let query = "Main image of cap \(cap)" - guard !downloadingMainImages.contains(cap) else { - return false + func image(for cap: Int, version: Int = 0, completion: @escaping (_ image: UIImage?) -> Void) -> Bool { + // Check if main image, and already being downloaded + if version == 0 { + guard !downloadingMainImages.contains(cap) else { + return false + } + downloadingMainImages.insert(cap) } - downloadingMainImages.insert(cap) - - let task = session.downloadTask(with: url) { fileUrl, response, error in - DispatchQueue.main.async { - self.downloadingMainImages.remove(cap) - } - guard let fileUrl = self.convertResponse(to: query, fileUrl, response, error) else { - completion(false) - return - } - guard app.storage.saveImage(at: fileUrl, for: cap) else { - self.log("Request '\(query)' could not move downloaded file") - completion(false) - return - } - completion(true) - } - task.resume() - return true - } - - /** - Download an image for a cap. - - Parameter cap: The id of the cap. - - Parameter version: The image version to download. - - Parameter completion: A closure with the resulting image - - Returns: `true`, of the file download was started, `false`, if the image is already downloading. - */ - @discardableResult - func image(for cap: Int, version: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool { let url = serverImageUrl(for: cap, version: version) let query = "Image of cap \(cap) version \(version)" let task = session.downloadTask(with: url) { fileUrl, response, error in + if version == 0 { + DispatchQueue.main.async { + self.downloadingMainImages.remove(cap) + } + } guard let fileUrl = self.convertResponse(to: query, fileUrl, response, error) else { - completion(false) + completion(nil) return } - guard app.storage.saveImage(at: fileUrl, for: cap, version: version) else { + guard let image = app.storage.saveImage(at: fileUrl, for: cap, version: version) else { self.log("Request '\(query)' could not move downloaded file") - completion(false) + completion(nil) return } - completion(true) + completion(image) } task.resume() return true diff --git a/CapCollector/Data/Storage.swift b/CapCollector/Data/Storage.swift index 2d7feb6..a98ed6f 100644 --- a/CapCollector/Data/Storage.swift +++ b/CapCollector/Data/Storage.swift @@ -56,17 +56,17 @@ final class Storage { - parameter version: The version of the image to get - returns: True, if the image was saved */ - func saveImage(at url: URL, for cap: Int, version: Int = 0) -> Bool { + func saveImage(at url: URL, for cap: Int, version: Int = 0) -> UIImage? { let targetUrl = localImageUrl(for: cap, version: version) do { if fm.fileExists(atPath: targetUrl.path) { try fm.removeItem(at: targetUrl) } try fm.moveItem(at: url, to: targetUrl) - return true + return UIImage(contentsOfFile: targetUrl.path) } catch { log("Failed to delete or move image \(version) for cap \(cap)") - return false + return nil } } diff --git a/CapCollector/Info.plist b/CapCollector/Info.plist index 36be64f..a6c6308 100644 --- a/CapCollector/Info.plist +++ b/CapCollector/Info.plist @@ -18,17 +18,6 @@ APPL CFBundleShortVersionString $(MARKETING_VERSION) - CFBundleURLTypes - - - CFBundleURLName - - CFBundleURLSchemes - - db-n81tx2g638wuffl - - - CFBundleVersion 1 LSApplicationQueriesSchemes diff --git a/CapCollector/Presentation/GridViewController.swift b/CapCollector/Presentation/GridViewController.swift index fe5247c..973f876 100644 --- a/CapCollector/Presentation/GridViewController.swift +++ b/CapCollector/Presentation/GridViewController.swift @@ -230,8 +230,8 @@ class GridViewController: UIViewController { } private func downloadImage(cap id: Int, tile: Int) { - app.database.downloadMainImage(for: id) { success in - guard success else { + app.database.downloadImage(for: id) { img in + guard img != nil else { return } guard let view = self.installedTiles[tile] else { diff --git a/CapCollector/Presentation/ImageSelector.swift b/CapCollector/Presentation/ImageSelector.swift index 43d5ba9..560c060 100644 --- a/CapCollector/Presentation/ImageSelector.swift +++ b/CapCollector/Presentation/ImageSelector.swift @@ -96,33 +96,28 @@ class ImageSelector: UIViewController { private func downloadImages() { images = [UIImage?](repeating: nil, count: cap.count) log("\(cap.count) images for cap \(cap.id)") - if let image = app.storage.image(for: cap.id) { - self.images[0] = image - self.collection.reloadItems(at: [IndexPath(row: 0, section: 0)]) - } else { - app.database.downloadMainImage(for: cap.id) { success in - guard success, let image = app.storage.image(for: self.cap.id) else { - return - } - self.images[0] = image - DispatchQueue.main.async { - self.collection.reloadItems(at: [IndexPath(row: 0, section: 0)]) + for version in 0.. 0 else { - return - } - for number in 1..