diff --git a/AppShared/ContentView.swift b/AppShared/ContentView.swift index 82ad708..3ec7613 100644 --- a/AppShared/ContentView.swift +++ b/AppShared/ContentView.swift @@ -9,22 +9,11 @@ import SwiftUI struct ContentView: View { var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundColor(.accentColor) - - #if os(iOS) - Text("Hello \(UIDevice.isIPad ? "iPadOS" : "iOS")") - #elseif os(macOS) - Text("Hello macOS") - #elseif os(tvOS) - Text("Hello tvOS") - #elseif os(watchOS) - Text("Hello watchOS") - #endif - } - .padding() + #if os(iOS) // iOS can add and remove + ListOfThingsView() + #elseif os(tvOS) || os(watchOS) || os(macOS) // macOS and tvOS and watchOS just show a readonly list + SimpleListOfThingsView() + #endif } } diff --git a/AppShared/JustOneThingApp.swift b/AppShared/JustOneThingApp.swift index e8d3160..9de021d 100644 --- a/AppShared/JustOneThingApp.swift +++ b/AppShared/JustOneThingApp.swift @@ -9,10 +9,11 @@ import SwiftUI @main struct JustOneThingApp: App { - let persistenceController = PersistenceController.shared + let persistenceController = PersistenceController.preview // or PersistenceController.shared var body: some Scene { WindowGroup { ContentView() + .environment(\.managedObjectContext, persistenceController.container.viewContext) } } } diff --git a/AppShared/SimpleListOfThingsView.swift b/AppShared/SimpleListOfThingsView.swift new file mode 100644 index 0000000..e143d24 --- /dev/null +++ b/AppShared/SimpleListOfThingsView.swift @@ -0,0 +1,25 @@ +// +// SimpleListOfThingsView.swift +// JustOneThingWatch +// +// Created by Alan Francis on 10/07/2023. +// + +import SwiftUI +import CoreData + +struct SimpleListOfThingsView: View { + @Environment(\.managedObjectContext) private var viewContext + @FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \PersistentThing.createdAt, ascending: true)], + animation: .default) + private var items: FetchedResults + + var body: some View { + List { + ForEach(items) { item in + Text(item.text ?? "no text") + } + } + } +} diff --git a/JustOneThing.xcodeproj/project.pbxproj b/JustOneThing.xcodeproj/project.pbxproj index 9e110bc..c1ff1b5 100644 --- a/JustOneThing.xcodeproj/project.pbxproj +++ b/JustOneThing.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 5405B2992A5C939C00B36DB6 /* ListOfThingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5405B2982A5C939C00B36DB6 /* ListOfThingsView.swift */; }; + 5405B29B2A5C94BA00B36DB6 /* SimpleListOfThingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5405B29A2A5C94BA00B36DB6 /* SimpleListOfThingsView.swift */; }; + 5405B2BC2A5CA1A700B36DB6 /* SimpleListOfThingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5405B29A2A5C94BA00B36DB6 /* SimpleListOfThingsView.swift */; }; 5407E5022A2A8AE200EF00B8 /* JustOneThingApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5483903A2A2A773900BF5624 /* JustOneThingApp.swift */; }; 5407E5032A2A8AE700EF00B8 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5483903C2A2A773900BF5624 /* ContentView.swift */; }; 5407E5042A2A8AEA00EF00B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5483903E2A2A773A00BF5624 /* Assets.xcassets */; }; @@ -131,6 +134,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 5405B2982A5C939C00B36DB6 /* ListOfThingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOfThingsView.swift; sourceTree = ""; }; + 5405B29A2A5C94BA00B36DB6 /* SimpleListOfThingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleListOfThingsView.swift; sourceTree = ""; }; 5407E5062A35B96E00EF00B8 /* JustOneThingWatch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = JustOneThingWatch.entitlements; sourceTree = ""; }; 5416C7372A56A867002196EE /* InterfaceExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceExtensions.swift; sourceTree = ""; }; 5431ED6D2A2A77C900515680 /* JustOneThingWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JustOneThingWatch.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -212,6 +217,7 @@ children = ( 5483903A2A2A773900BF5624 /* JustOneThingApp.swift */, 5483903C2A2A773900BF5624 /* ContentView.swift */, + 5405B29A2A5C94BA00B36DB6 /* SimpleListOfThingsView.swift */, 5483903E2A2A773A00BF5624 /* Assets.xcassets */, ); path = AppShared; @@ -271,6 +277,7 @@ children = ( 5432A6032A582D5D00FE2351 /* Info.plist */, 548390402A2A773A00BF5624 /* JustOneThing.entitlements */, + 5405B2982A5C939C00B36DB6 /* ListOfThingsView.swift */, ); path = JustOneThing; sourceTree = ""; @@ -532,6 +539,7 @@ files = ( 5407E5032A2A8AE700EF00B8 /* ContentView.swift in Sources */, 54AE28422A5823D900719A56 /* JustOneThingCloudModel.xcdatamodeld in Sources */, + 5405B2BC2A5CA1A700B36DB6 /* SimpleListOfThingsView.swift in Sources */, 5407E5022A2A8AE200EF00B8 /* JustOneThingApp.swift in Sources */, 547201932A365868005B2FCC /* Widget.intentdefinition in Sources */, 54AE28482A58245700719A56 /* PersistenceController.swift in Sources */, @@ -544,7 +552,9 @@ files = ( 5483903D2A2A773900BF5624 /* ContentView.swift in Sources */, 54AE28412A5823D900719A56 /* JustOneThingCloudModel.xcdatamodeld in Sources */, + 5405B2992A5C939C00B36DB6 /* ListOfThingsView.swift in Sources */, 5483903B2A2A773900BF5624 /* JustOneThingApp.swift in Sources */, + 5405B29B2A5C94BA00B36DB6 /* SimpleListOfThingsView.swift in Sources */, 54928C222A35F89A00095445 /* Widget.intentdefinition in Sources */, 54AE28472A58245700719A56 /* PersistenceController.swift in Sources */, ); diff --git a/JustOneThing/ListOfThingsView.swift b/JustOneThing/ListOfThingsView.swift new file mode 100644 index 0000000..313e3cb --- /dev/null +++ b/JustOneThing/ListOfThingsView.swift @@ -0,0 +1,85 @@ +// +// ListOfThingsView.swift +// JustOneThing +// +// Created by Alan Francis on 10/07/2023. +// + +import SwiftUI +import CoreData + +struct ListOfThingsView: View { + @Environment(\.managedObjectContext) private var viewContext + + @FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \PersistentThing.createdAt, ascending: true)], + animation: .default) + private var things: FetchedResults + private let timeFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .none + formatter.timeStyle = .medium + return formatter + }() + + var body: some View { + NavigationView { + List { + ForEach(things) { thing in + NavigationLink { + Text(thing.text ?? "no text") + } label: { + Text(thing.text ?? "no text") + } + } + .onDelete(perform: deleteItems) + } + #if os(iOS) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + EditButton() + } + ToolbarItem { + Button(action: addItem) { + Label("Add Item", systemImage: "plus") + } + } + } + #endif + Text("Select an item") + } + } + + private func addItem() { + withAnimation { + let newThing = PersistentThing(context: viewContext) + newThing.createdAt = Date() + newThing.uuid = UUID() + newThing.text = "thing created \(timeFormatter.string(from: Date()))" + + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } + + private func deleteItems(offsets: IndexSet) { + withAnimation { + offsets.map { things[$0] }.forEach(viewContext.delete) + + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } +} diff --git a/Model/PersistenceController.swift b/Model/PersistenceController.swift index 625e179..fabfb43 100644 --- a/Model/PersistenceController.swift +++ b/Model/PersistenceController.swift @@ -84,4 +84,26 @@ extension PersistenceController { } - +extension PersistenceController { + static var preview: PersistenceController = { + let result = PersistenceController(inMemory: true) + let viewContext = result.container.viewContext + var i = 1 + for _ in 0..<10 { + let newItem = PersistentThing(context: viewContext) + newItem.text = "action \(i)" + newItem.createdAt = Date() + newItem.uuid = UUID() + i = i+1 + } + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + return result + }() +}