From 77872ed1eaea60b5a7990962d0f26fe88edc69ae Mon Sep 17 00:00:00 2001 From: Vegard Bieker Matthey Date: Fri, 17 Jul 2026 02:30:56 +0200 Subject: [PATCH] feat: alerts --- ImageViewer/ViewController.swift | 168 ++++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 24 deletions(-) diff --git a/ImageViewer/ViewController.swift b/ImageViewer/ViewController.swift index 468a3c6..06fda8f 100644 --- a/ImageViewer/ViewController.swift +++ b/ImageViewer/ViewController.swift @@ -59,6 +59,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { var scrollPos: CGPoint! var hasSetContentOffset = false + var pendingAlert: UIAlertController? + var imageView = UIImageView() var mode = PageTurnMode.leftToRight var metadataList: [URL: Metadata] = [:] @@ -105,6 +107,13 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { setup() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if let alert = pendingAlert { + present(alert, animated: false, completion: nil) + } + } + func setup() { do { if try fileManager.contentsOfDirectory(atPath: documentsURL.path).isEmpty { @@ -140,8 +149,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { setupHomeView() if let name = globalState.comicName { readComic(name: name) - } else { - print("no comic?") } } @@ -163,14 +170,21 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { } func loadComics() { + let alert = UIAlertController(title: "Failed to load comics.", message: "This is an alert.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) do { var directories: [URL] = [] - let contents = try fileManager.contentsOfDirectory( + guard let contents = try? fileManager.contentsOfDirectory( at: documentsURL, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles] ) + else { + alert.message = String(format: "Failed to read contents of directory: \(documentsURL)") + pendingAlert = alert + return + } directories = contents.filter { url in var isDirectory: ObjCBool = false @@ -181,33 +195,63 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { for dir in directories { if !fileManager.fileExists(atPath: dir.appendingPathComponent(metadataFilename).path) { getMetadataFromFilename(path: dir) + if pendingAlert != nil { + return + } } currentPath = dir - let data = try! Data(contentsOf: dir.appendingPathComponent(metadataFilename)) + guard let data = try? Data(contentsOf: dir.appendingPathComponent(metadataFilename)) + else { + alert.message = String(format: "Failed to read contents of metadata: \(dir.appendingPathComponent(metadataFilename))") + pendingAlert = alert + return + } var byteBuffer = ByteBuffer(data: data) - metadata = try! getCheckedRoot(byteBuffer: &byteBuffer) + metadata = try? getCheckedRoot(byteBuffer: &byteBuffer) + if metadata == nil { + alert.message = String(format: "Failed to read contents of metadata flatbuffer file: \(dir.appendingPathComponent(metadataFilename))") + pendingAlert = alert + return + } metadataList[dir] = metadata loadLocalState() let volCovP = ProgressIndices(v: progress.v, c: 0, i: 0) let archivePath = getArchiveURL(volCovP.v) - let archive = try! Archive(url: archivePath, accessMode: .read) + let archive = try Archive(url: archivePath, accessMode: .read) let filename = metadata.volumes[volCovP.v].chapters[volCovP.c].images[volCovP.i].filename! - let targetEntry = archive[filename]! + guard let targetEntry = archive[filename] else { + alert.message = String(format: "Archive entry does not exist: \(filename)") + pendingAlert = alert + return + } var extractedData = Data() - _ = try archive.extract(targetEntry) { data in - extractedData.append(data) + do { + _ = try archive.extract(targetEntry) { data in + extractedData.append(data) + } + } catch { + alert.message = String(format: "Failed to extract data from entry: \(targetEntry.path)") + pendingAlert = alert + return + } + guard let coverImage = UIImage(data: extractedData) else { + alert.message = String(format: "Cover image is nil: \(filename)") + pendingAlert = alert + return } comics.append( Comic( - cover: UIImage(data: extractedData)!, + cover: coverImage, metadata: metadata, path: dir ) ) } } catch { + pendingAlert = alert + alert.message = String(format: "Generic failure.") print("Failed to read directories") } } @@ -263,9 +307,9 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { func getMetadataFromFilename(path: URL) { // Beautiful, is it not? - let patterns: [(NSRegularExpression, NSRegularExpression, Int, Int, Int)] = [ - (try! NSRegularExpression(pattern: #"c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#), try! NSRegularExpression(pattern: #"^(.*) - c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#), 3, 1, 2), - (try! NSRegularExpression(pattern: "v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"), try! NSRegularExpression(pattern: "^(.*) - v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"), 2, 3, 1), + let patterns: [(NSRegularExpression?, NSRegularExpression?, Int, Int, Int)] = [ + (try? NSRegularExpression(pattern: #"c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#), try? NSRegularExpression(pattern: #"^(.*) - c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#), 3, 1, 2), + (try? NSRegularExpression(pattern: "v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"), try? NSRegularExpression(pattern: "^(.*) - v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"), 2, 3, 1), ] var pattern: NSRegularExpression! = nil var titlePattern: NSRegularExpression! = nil @@ -293,6 +337,9 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { var titleValue: String = "" + let alert = UIAlertController(title: "Failed to get metadata from filename.", message: "This is an alert.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) + let volumeDir = path.appendingPathComponent("volumes") var isDir: ObjCBool = false @@ -300,25 +347,42 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { print("Subdirectory volumes not found in \"\(path)\", returning") return } - let volumeURLs = try! fileManager.contentsOfDirectory( + let volumeURLs = try? fileManager.contentsOfDirectory( at: volumeDir, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles] ).filter { $0.pathExtension == "cbz" } .sorted { $0.lastPathComponent < $1.lastPathComponent } + if volumeURLs == nil { + alert.message = String(format: "failed to read dir: \(volumeDir)") + pendingAlert = alert + return + } - if volumeURLs.isEmpty { + if volumeURLs!.isEmpty { print("found no volumes in volume dir: \(volumeDir)") return } var filenames: [(String, String)] = [] - for volumeURL in volumeURLs { + for volumeURL in volumeURLs! { do { let volumeArchive = try Archive(url: volumeURL, accessMode: .read) - volumeArchive.map { URL(string: $0.path)!.lastPathComponent }.sorted().forEach{ filenames.append((volumeURL.lastPathComponent, $0)) } + var volumeArchiveFiles: [String] = [] + for entry in volumeArchive { + // guard let url = URL(string: entry.path) else { + // alert.message = String(format: "failed to create URL from string: \(entry.path)") + // pendingAlert = alert + // return + // } + volumeArchiveFiles.append(entry.path) + } + for filename in volumeArchiveFiles.sorted() { + filenames.append((volumeURL.lastPathComponent, filename)) + } } catch { - print("Failed to read archive \(volumeURL)") + alert.message = String(format: "failed to read archive: \(volumeURL)") + pendingAlert = alert continue } } @@ -330,6 +394,16 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { do { for (currentPattern, currentTitlePattern, currentPageRegexPosition, currentChapterRegexPosition, currentVolumeRegexPosition) in patterns { + guard let currentPattern = currentPattern else { + alert.message = String(format: "failed to get currentPattern") + pendingAlert = alert + continue + } + guard let currentTitlePattern = currentTitlePattern else { + alert.message = String(format: "failed to get currentTitlePattern") + pendingAlert = alert + continue + } let filename = filenames[0].1 let range = NSRange(filename.startIndex..., in: filename) if let match = currentTitlePattern.firstMatch(in: filename, range: range) { @@ -390,25 +464,51 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { if let match = regex.firstMatch(in: filename, range: range) { let chapterStr = (filename as NSString).substring(with: match.range(at: chapterRegexPosition)) let chapterParts = chapterStr.split(separator: "x", maxSplits: 1) + if chapterParts.count < 1 { + alert.message = String(format: "Did not find chapter number in filename: \(filename)") + pendingAlert = alert + return + } + guard let nChapter0 = UInt32(chapterParts[0]) else { + alert.message = String(format: "Failed to convert chapter number to an int: \(filename)") + pendingAlert = alert + return + } let chapter: (UInt32, UInt32?) = ( - UInt32(chapterParts[0])!, chapterParts.count > 1 ? UInt32(chapterParts[1])! : nil + nChapter0, chapterParts.count > 1 ? UInt32(chapterParts[1]) : nil ) - let volume: UInt32 = UInt32((filename as NSString).substring(with: match.range(at: volumeRegexPosition)))! + + guard let volume: UInt32 = UInt32((filename as NSString).substring(with: match.range(at: volumeRegexPosition))) else { + alert.message = String(format: "failed to get match for volume in filename: \(filename)") + pendingAlert = alert + return + } + let pageStr = (filename as NSString).substring(with: match.range(at: pageRegexPosition)) let pageParts = pageStr.split(separator: "-", maxSplits: 1) + guard let nPage = UInt32(pageParts[0]) else { + alert.message = String(format: "failed to read archive: \(filename)") + pendingAlert = alert + return + } let (page, doublePage) = ( - UInt32(pageParts[0])!, pageParts.count > 1 + nPage, pageParts.count > 1 ) let archive = try Archive(url: volumeDir.appendingPathComponent(volumeURL), accessMode: .read) - let entry = archive[filename]! + guard let entry = archive[filename] else { + alert.message = String(format: "failed to read archive: \(filename)") + pendingAlert = alert + return + } var size = ImageDimensions(width: 0, height: 0) do { size = try imageDimensions(of: entry, in: archive) } catch { - print("Failed to get image dimensions of filename: \(filename)") + alert.message = String(format: "Failed to get image dimensions of filename: \(filename)") + pendingAlert = alert return } let m_filename = builder.create(string: filename) @@ -452,8 +552,23 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { currentChapters = [] } else if chapter != currentChapter { if chapter.0 == currentChapter.0 { + if chapter.1 == nil { + alert.message = String(format: "chapter.1 is nil") + pendingAlert = alert + return + } + if chapter.1! != currentChapter.1 ?? 1 { + alert.message = String(format: "chapter != currentChapter") + pendingAlert = alert + return + } assert(chapter.1! == currentChapter.1 ?? 1) } else { + if chapter.0 != currentChapter.0 + 1 { + alert.message = String(format: "chapter.0 != currentChapter.0 + 1") + pendingAlert = alert + return + } assert(chapter.0 == currentChapter.0 + 1) } @@ -547,7 +662,12 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { ) builder.finish(offset: metadata) - try! builder.data.write(to: path.appendingPathComponent(metadataFilename)) + do { + try builder.data.write(to: path.appendingPathComponent(metadataFilename)) + } catch { + alert.message = String(format: "Failed to write metadata file at: \(metadataFilename)") + pendingAlert = alert + } } func saveLocalState() {