This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| swift [2023/10/22 17:48] admin | swift [2023/11/19 22:16] (current) admin | ||
|---|---|---|---|
| Line 6: | Line 6: | ||
| `struct ScrumsView_Previews: PreviewProvider` is just for showing an mock up preview when you coding. It is not a part of the real app. | `struct ScrumsView_Previews: PreviewProvider` is just for showing an mock up preview when you coding. It is not a part of the real app. | ||
| - | |||
| - | {{:swiftui_binding_state.png?nolink&400|}} | ||
| Add `@State` at the beginning to declear a single source of truth at the very most parent view. Use `$[variable name]` to pass it to child view. In child view initialize with `@Binding var [variable name]: [variable type]` to use that data. | Add `@State` at the beginning to declear a single source of truth at the very most parent view. Use `$[variable name]` to pass it to child view. In child view initialize with `@Binding var [variable name]: [variable type]` to use that data. | ||
| Line 73: | Line 71: | ||
| ### iCloud Key-Value store (NSUbiquitousKeyValueStore) | ### iCloud Key-Value store (NSUbiquitousKeyValueStore) | ||
| You can think of it like UserDefaults, but the value is shared across different devices for the same app, that ties to an Apple ID account. | You can think of it like UserDefaults, but the value is shared across different devices for the same app, that ties to an Apple ID account. | ||
| + | |||
| + | |||
| + | ref: | ||
| + | [[https://developer.apple.com/library/archive/documentation/General/Conceptual/iCloudDesignGuide/Chapters/Introduction.html#//apple_ref/doc/uid/TP40012094|Apple: iCloud Design Guide]] | ||
| + | [[https://developer.apple.com/library/archive/documentation/General/Conceptual/iCloudDesignGuide/Chapters/DesigningForKey-ValueDataIniCloud.html#//apple_ref/doc/uid/TP40012094-CH7|Apple: Designing for Key-Value Data in iCloud]] | ||
| + | |||
| + | ### Relationship: Connect entities in CoreData | ||
| + | ref: https://www.hackingwithswift.com/books/ios-swiftui/one-to-many-relationships-with-core-data-swiftui-and-fetchrequest | ||
| + | |||
| + | 1. Create Entities in CoreData | ||
| + | 2. Select the relationship created, in the right tool panel, change `Type` to `To Many` | ||
| + | 3. Select the entity, in the right tool panel, change `Codegen` to `Manual/None` | ||
| + | 4. Use `Editor>Create NSManagedObject SubClass...` menu to create `...CoreDataClass` file and `...CoreDataProperties` file for each entity. | ||
| + | 5. In `...CoreDataProperties` file, you should be able to see code like below for adding transactions to this certain piggy object. | ||
| + | |||
| + | ``` | ||
| + | extension Piggy { | ||
| + | |||
| + | @nonobjc public class func fetchRequest() -> NSFetchRequest<Piggy> { | ||
| + | return NSFetchRequest<Piggy>(entityName: "Piggy") | ||
| + | } | ||
| + | |||
| + | @NSManaged public var capacity: NSDecimalNumber? | ||
| + | @NSManaged public var currentMoney: NSDecimalNumber? | ||
| + | @NSManaged public var dailyAllowance: NSDecimalNumber? | ||
| + | @NSManaged public var id: UUID? | ||
| + | @NSManaged public var lastDepositDay: Date? | ||
| + | @NSManaged public var name: String? | ||
| + | @NSManaged public var totalSaved: NSDecimalNumber? | ||
| + | @NSManaged public var transactions: NSSet? | ||
| + | } | ||
| + | extension Piggy { | ||
| + | @objc(addTransactionsObject:) | ||
| + | @NSManaged public func addToTransactions(_ value: PurchasedItem) | ||
| + | |||
| + | @objc(removeTransactionsObject:) | ||
| + | @NSManaged public func removeFromTransactions(_ value: PurchasedItem) | ||
| + | |||
| + | @objc(addTransactions:) | ||
| + | @NSManaged public func addToTransactions(_ values: NSSet) | ||
| + | |||
| + | @objc(removeTransactions:) | ||
| + | @NSManaged public func removeFromTransactions(_ values: NSSet) | ||
| + | } | ||
| + | ``` | ||
| + | |||
| + | 1. Edit ` ...CoreDataProperties` file, add code like `public var wrappedCapacity: NSDecimalNumber { capacity ?? 0.0 }` | ||
| + | 2. In ` ...CoreDataProperties` file, `transactions` is an `NSSet`, perform the following steps to convert it from an `NSSet` to a `Set<PurchasedItem>` – a Swift-native type where the types of its contents is specificy to `PurchasedItem` type. Convert that `Set<PurchasedItem>` into an array, so that `ForEach` can read individual values from there. Sort that array, so the transactions come in a sensible order. | ||
| + | |||
| + | ``` | ||
| + | public var transactionArray: [PurchasedItem] { | ||
| + | let set = transactions as? Set<PurchasedItem> ?? [] | ||
| + | |||
| + | return set.sorted { | ||
| + | $0.wrappedTimestamp < $1.wrappedTimestamp | ||
| + | } | ||
| + | } | ||
| + | ``` | ||
| + | |||
| + | 1. Use them in your View: To read purchased items connected to a certain piggy in a View. Use the following code: | ||
| + | |||
| + | ``` | ||
| + | struct HistoryView: View { | ||
| + | @Environment(\.managedObjectContext) private var viewContext | ||
| + | @EnvironmentObject private var persistenceController: PersistenceController | ||
| + | let piggy: Piggy | ||
| + | |||
| + | @FetchRequest private var items: FetchedResults<PurchasedItem> | ||
| + | |||
| + | init(piggy: Piggy) { | ||
| + | _items = FetchRequest<PurchasedItem>( | ||
| + | sortDescriptors: [NSSortDescriptor(keyPath: \PurchasedItem.timestamp, ascending: false)], | ||
| + | predicate: NSPredicate(format: "piggyStored == %@", piggy) | ||
| + | ) | ||
| + | self.piggy = piggy | ||
| + | } | ||
| + | |||
| + | var body: some View { | ||
| + | NavigationView { | ||
| + | if (items.count != 0) { | ||
| + | List { | ||
| + | ForEach(items) { item in | ||
| + | // view related code | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | ``` | ||
| + | |||
| + | Or use the following code. However with this method the HistoryView won't update if you add new purchasedItem (transaction) because it is only monitoring the object `piggy`. To trigger view update when purchasedItem is added, you need to use the method above to fetch `PurchasedItem` directly. | ||
| + | |||
| + | ``` | ||
| + | struct HistoryView: View { | ||
| + | @Environment(\.managedObjectContext) private var viewContext | ||
| + | @EnvironmentObject private var persistenceController: PersistenceController | ||
| + | let piggy: Piggy | ||
| + | |||
| + | var body: some View { | ||
| + | NavigationView { | ||
| + | if (piggy.transactionArrary.count != 0) { | ||
| + | List { | ||
| + | ForEach(piggy.transactionArray) { item in | ||
| + | // view related code | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | ``` | ||
| + | |||
| + | 1. To write new purchased item and connect it to piggy. | ||
| + | |||
| + | ```swiftUI | ||
| + | let newPurchase = PurchasedItem(context: viewContext) | ||
| + | newPurchase.id = UUID() | ||
| + | newPurchase.type = 0 // 0: purchase | ||
| + | newPurchase.name = name | ||
| + | newPurchase.price = NSDecimalNumber(decimal: Decimal(price)) | ||
| + | newPurchase.timestamp = Date() | ||
| + | newPurchase.piggyStored = piggy | ||
| + | try? viewContext.save() | ||
| + | ``` | ||
| + | |||