Initial scaffold — NASBackup iOS app

Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Robin Kutesa
2026-05-15 17:05:04 +03:00
commit b96711c535
44 changed files with 3412 additions and 0 deletions

34
App/AppDelegate.swift Normal file
View File

@@ -0,0 +1,34 @@
import UIKit
import BackgroundTasks
import UserNotifications
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
BackgroundTaskManager.registerTasks()
requestNotificationPermission()
setupLANMonitor()
return true
}
private func requestNotificationPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { _, _ in }
}
private func setupLANMonitor() {
let monitor = LANMonitor.shared
monitor.onTrustedNetworkJoined = { ssid in
let store = ConnectionStore.shared
guard store.lanTriggerEnabled, !store.isInQuietHours else { return }
let delay = TimeInterval(store.startDelaySeconds)
BackgroundTaskManager.scheduleBackup(delay: delay)
}
monitor.start()
}
func applicationDidEnterBackground(_ application: UIApplication) {
BackgroundTaskManager.scheduleAppRefresh()
}
}

72
App/Info.plist Normal file
View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.albert.nasbackup.backup</string>
<string>com.albert.nasbackup.refresh</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>NASBackup</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSFaceIDUsageDescription</key>
<string>Sign in quickly with Face ID.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to detect your home Wi-Fi network name for automatic backup.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>NASBackup needs access to your photos to back them up to your NAS.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
<key>UILaunchStoryboardName</key>
<string></string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

31
App/NASBackupApp.swift Normal file
View File

@@ -0,0 +1,31 @@
import SwiftUI
@main
struct NASBackupApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject private var store = ConnectionStore.shared
@StateObject private var engine = BackupEngine.shared
@StateObject private var lanMonitor = LANMonitor.shared
var body: some Scene {
WindowGroup {
RootView()
.environmentObject(store)
.environmentObject(engine)
.environmentObject(lanMonitor)
}
}
}
struct RootView: View {
@EnvironmentObject var store: ConnectionStore
var body: some View {
if store.savedConnection != nil {
LoginView()
} else {
ConnectView()
}
}
}

14
App/SceneDelegate.swift Normal file
View File

@@ -0,0 +1,14 @@
import UIKit
import SwiftUI
final class SceneDelegate: NSObject, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
// SwiftUI lifecycle handles window setup via NASBackupApp.swift
}
}