From adbc5ddd8912ad94a2d7559fa5c292ae77be1148 Mon Sep 17 00:00:00 2001 From: rzen Date: Sat, 8 Aug 2026 11:25:53 -0400 Subject: [PATCH] =?UTF-8?q?Backup=20bows=20out=20of=20the=20phone=20?= =?UTF-8?q?=E2=80=94=20the=20zipped-share=20future=20replaces=20it,=20late?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IndieBackup comes out whole: the package, the Backup directory, the Settings section, the .kanbanbackup document type and its onOpenURL restore path. Settings is now the About section alone. The Files-app keys stay — they serve local-board visibility, not backups. Board sharing as a zipped archive is the planned replacement and is deliberately not started here. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY --- KanbanMobile/App/MobileApp.swift | 34 +--------- .../Backup/MobileBackupConfiguration.swift | 68 ------------------- KanbanMobile/CHANGELOG.md | 2 - KanbanMobile/Info.plist | 37 +--------- KanbanMobile/Screens/SettingsTabView.swift | 39 +---------- .../SettingsAboutUITests.swift | 4 +- README.md | 2 +- project.yml | 10 --- 8 files changed, 7 insertions(+), 189 deletions(-) delete mode 100644 KanbanMobile/Backup/MobileBackupConfiguration.swift diff --git a/KanbanMobile/App/MobileApp.swift b/KanbanMobile/App/MobileApp.swift index 90974b8..9c6a6c2 100644 --- a/KanbanMobile/App/MobileApp.swift +++ b/KanbanMobile/App/MobileApp.swift @@ -1,9 +1,7 @@ import SwiftUI -import IndieBackup -import os /// The iPhone app's entry point. Two tabs, per the mobile MVP charter: Boards (the whole -/// board → lane → card navigation stack) and Settings (backup/restore). +/// board → lane → card navigation stack) and Settings. @main struct MobileApp: App { @@ -13,40 +11,10 @@ struct MobileApp: App { /// constructing one. @State private var boardIndex = BoardIndexStore() - /// Backup/restore (indie-backup skill), built once `boardIndex.home` resolves to a real - /// `CloudHome` — see `MobileBackupConfiguration`'s doc comment for why this can't happen at - /// `@State` init time. Stays `nil` for the lifetime of a run with no iCloud account; - /// `SettingsTabView` renders the quiet disabled state for exactly that case. - @State private var backupController: BackupController? - - private static let logger = Logger(subsystem: "dev.rzen.indie.KanbanMobile", category: "backup") - var body: some Scene { WindowGroup { RootTabView() .environment(boardIndex) - .environment(\.backupController, backupController) - .task(id: boardIndex.home) { - guard backupController == nil, let home = boardIndex.home else { return } - backupController = BackupController( - configuration: MobileBackupConfiguration(root: home.documentsURL, index: boardIndex) - ) - } - .onOpenURL { url in - // A backup file opened from Files, AirDrop, or a share sheet. Silently - // ignored before `backupController` exists — there is no resolved cloud home - // to restore into either, at that point. - guard url.pathExtension == MobileBackupConfiguration.fileExtension, - let backupController - else { return } - Task { - do { - try await backupController.restoreBackup(from: url) - } catch { - Self.logger.error("restore from opened file failed: \(error.localizedDescription, privacy: .public)") - } - } - } } } } diff --git a/KanbanMobile/Backup/MobileBackupConfiguration.swift b/KanbanMobile/Backup/MobileBackupConfiguration.swift deleted file mode 100644 index 6241d12..0000000 --- a/KanbanMobile/Backup/MobileBackupConfiguration.swift +++ /dev/null @@ -1,68 +0,0 @@ -import Foundation -import IndieBackup -import SwiftUI - -/// IndieBackup's app-specific wiring (indie-backup skill): what gets backed up, and how the -/// app's in-memory board list is rebuilt after a restore. -/// -/// **Why this type is constructed rather than defaulted.** `BackupConfiguration.backupRoot` is a -/// non-optional `URL`, but the ubiquity container resolves asynchronously and can fail outright -/// (`CloudHomeResolver`, CloudHome.swift) — there is no root to hand IndieBackup until -/// `BoardIndexStore.home` exists. Rather than answer with a placeholder path, this type is only -/// ever constructed once a real `CloudHome` is in hand (`MobileApp`'s `.task(id:)`); before that -/// there is no `BackupController` at all, and `SettingsTabView` renders a quiet disabled -/// explanation instead of a section built on a fabricated root. -final class MobileBackupConfiguration: BackupConfiguration { - /// No dot. Shared by the Info.plist document-type/UTI registration - /// (`dev.rzen.indie.kanban-backup`) and the archive filenames IndieBackup writes. - static let fileExtension = "kanbanbackup" - - private let root: URL - private let index: BoardIndexStore - - /// - Parameters: - /// - root: The resolved `CloudHome.documentsURL` — the same `Documents/` folder every - /// `.kanban` board lives directly inside. IndieBackup walks it file-by-file (it has no - /// notion of "package"), so a board's directory structure round-trips through a backup - /// as an ordinary tree of files — no directory registration or special-casing needed. - /// - index: Rescanned after a restore replaces the tree, in `rebuildCacheAfterRestore`. - init(root: URL, index: BoardIndexStore) { - self.root = root - self.index = index - } - - var backupFileExtension: String { Self.fileExtension } - var backupDisplayName: String { "Lanework Backup" } - var backupRoot: URL { root } - - /// `BoardIndexStore` caches nothing durable — no Core Data, no SwiftData, no file on disk — - /// only an in-memory `[BoardSummary]` scanned straight from the files under `backupRoot`. A - /// restore already replaced that tree by the time this runs, so the only rebuild step is - /// asking the store to rescan it; a `BoardSession` for a board that's open elsewhere reloads - /// its own content from disk independently the next time it's asked (BoardIndexStore's doc - /// comment on the observer flow). - /// - /// `refresh()` only *starts* the rescan (it hands off to a detached `Task` and returns), so - /// this reports done as soon as the request lands, not once the boards tab has repainted — - /// same as any other call to `refresh()` in this app (e.g. pull-to-refresh). - func rebuildCacheAfterRestore(progress: @escaping (Double, String) -> Void) async throws { - progress(0, "Rescanning boards…") - await index.refresh() - progress(1, "Rescanning boards…") - } -} - -/// Threads the lazily-constructed `BackupController` from `MobileApp` down to `SettingsTabView` -/// (the backup section) and back up to `MobileApp`'s own `onOpenURL` handler (imported backup -/// files), without forcing `RootTabView` — or anything else between them — to know backup exists. -/// `nil` until the cloud home resolves; see `MobileBackupConfiguration`. -private struct BackupControllerKey: EnvironmentKey { - static let defaultValue: BackupController? = nil -} - -extension EnvironmentValues { - var backupController: BackupController? { - get { self[BackupControllerKey.self] } - set { self[BackupControllerKey.self] = newValue } - } -} diff --git a/KanbanMobile/CHANGELOG.md b/KanbanMobile/CHANGELOG.md index f4cd0cc..7055ba8 100644 --- a/KanbanMobile/CHANGELOG.md +++ b/KanbanMobile/CHANGELOG.md @@ -3,5 +3,3 @@ Version 1.0: Lanework comes to iPhone — browse your boards from iCloud Drive, move cards between lanes, and edit them on the go. Swipe a board in the list to open its settings and move it between iCloud and this iPhone, which now works without an iCloud account. - -Back up your boards from Settings and restore any backup later, even on a new phone. diff --git a/KanbanMobile/Info.plist b/KanbanMobile/Info.plist index 23774f3..f0d7048 100644 --- a/KanbanMobile/Info.plist +++ b/KanbanMobile/Info.plist @@ -28,9 +28,8 @@ UIInterfaceOrientationPortrait - + LSSupportsOpeningDocumentsInPlace - - CFBundleTypeName - Lanework Backup - CFBundleTypeRole - Editor - LSHandlerRank - Owner - LSItemContentTypes - - dev.rzen.indie.kanban-backup - - UTExportedTypeDeclarations @@ -93,23 +77,6 @@ kanban - - UTTypeConformsTo - - public.data - - UTTypeDescription - Lanework Backup - UTTypeIdentifier - dev.rzen.indie.kanban-backup - UTTypeTagSpecification - - public.filename-extension - - kanbanbackup - - -