Post

Replies

Boosts

Views

Activity

Reply to help me! (data sync between watch and phone)
I’ll answer your questions in 3 sections: 1. DataModels and SwiftUI, 2. Watch Connectivity and 3. Saving and synchronising data. DataModels and SwiftUI A key concept of SwiftUI is that the user interface (SwiftUI Views) only interacts with the user, displaying text etc and collecting input (e.g. button presses).  All other app processing (e.g. data manipulation, data storage and retrieval) is done elsewhere in an app-specific class (typically called “DataModel”) created by the developer.  Your app essentially has two parts: the watch app and the phone app.  Each needs a DataModel - “WatchDataModel” and “PhoneDataModel” - being names that I decided.  The tasks of the WatchDataModel are to 1. Collect and aggregate (?) titles and counts, 2. Transmit those to the phone 3. Maintain a local copy of the collected data 4. Prepare data for displaying to the user.  The PhoneDataModel has to 1. Receive aggregated data from the watch 2. Store those data locally, synchronising as required with existing data (?) 3. Prepare data for displaying to the user, i.e. creating a property that a SwiftUI View can refer to for displaying on the screen. In my sample code, each data model is created as a singleton - meaning that there is only ever one copy created, no matter how many times it gets referred to elsewhere within your app. class WatchDataModel : NSObject, WCSessionDelegate, ObservableObject {     static let shared = WatchDataModel() // when referring to WatchDataModel.shared only one version is ever created     let session = WCSession.default // this essentially creates a shortcut to WCSession.default Watch Connectivity Within each data model, the connectivity to/from the watch is provided by the WatchConnectivity framework and the DataModel’s class has to adopt the WCSessionDelegate protocol for this to work.  import WatchConnectivity // the Apple code to control the watch-phone connection class WatchDataModel : NSObject, WCSessionDelegate, ObservableObject { // WCSessionDelegate allows this class to be "delegated" certain tasks from WCSession When your app starts up (Watch or Phone), it needs to initialise its data model, which then needs to tell the connectivity controller (WCSession.default) to delegate certain tasks (e.g. in PhoneDataModel, deal with incoming data) to the data model.  You can refer to this controller directly as WCSession.default, but typically one creates a static property let session = WCSession.default and then refer to that property.  Before sending / receiving, you have to activate a session (in fact, you should really check that a session is active before attempting to send anything). if WCSession.isSupported() { // checks if this device is capable of watch-phone communication (e.g. an iPad is not)             session.delegate = self // allows this DataModel (class) to process tasks delegated (assigned) to it by WCSession             session.activate() // activates a session so that communication can occur         } else {             print("ERROR: Watch session not supported")         } As I’ve said previously, there’s a number of ways of sending / receiving data:  the choice depends on the volume and type of data, and the urgency of the communication.  The one I usually use is transferUserInfo, because of its reliability under various situations. transferUserInfo sends a dictionary of property list values e.g. “Title”:”Hello World” or “Count” : 21 - the first part is the key and the second is the actual value.  But there are various ways of constructing a Dictionary, especially with multiple values.  Ref https://developer.apple.com/documentation/swift/dictionary. I don’t know enough about the purpose of your app and the volume / complexity of the data for me to make any suggestion, apart from my previous idea of a struct:   struct CountData {     var title : String = ""     var tCounts = [Int]()  // an array of counts     var creationDate = Date() } data.session.transferUserInfo(["CountData":data.countData]) // in the watch's ContentView we send a CountData struct (in data.countData) with a key of "CountData" via transferUserInfo to the phone Importantly, when your phone app receives the dictionary, you must be able to access the dictionary values and do whatever you need to do. func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { // WCSession has delegated to this PhoneDataModel the task of receiving and processing user info         guard let countData = userInfo["CountData"] as? CountData else { // check that what has been received is the info that was sent. In this example a struct we defined, CountData containing title, counts and date, was sent with a key "CountData"             print("ERROR: unknown data received from Watch")             return         }         // now do something with the received CountData     } Saving and Synchronising Data Having received the watch data on the phone via didReceiveUserInfo you now have to do something with it. But what? I can think of at least 6 different ways of saving and synchronising your data (i.e. making sure the watch and phone have identical sets of data). But, again, I don’t know enough about the purpose of your app and the volume / complexity of the data for me to make any recommendation. Your idea of UserDefaults could be problematic because of your data characteristics and the limits of UserDefaults. Ref https://developer.apple.com/documentation/foundation/userdefaults I normally use SQLIte instead of CoreData, but that's only because I'd been using SQL databases for decades before I came to Apple development and I find SQL's queries, views, joins and indexes far more flexible - and within my knowledge base. I'll have to finish here: I'm tired (: Regards, Michaela
Topic: App & System Services SubTopic: General Tags:
Sep ’21
Reply to help me! (data sync between watch and phone)
OK, now that I understand better what you are doing, this is what I would recommend (assuming that your data volumes are not several megabytes in total): Use CoreData on both the Watch and the Phone, with a CoreData entity model that is exactly the same on both. When the watch app starts, create a new Goal (using the entity class created by Xcode in XCDataModel) and set the item name, then start counting. When the "Send to Phone" button is pressed, save (write) the new Goal to Coredata on the Watch and also send it to the phone using transferUserInfo(["AddGoal":newGoal]). This sends a CoreData object to the phone, for saving in the phone's CoreData storage. In the phone app, with the same CoreData entity model, get the newGoal from didReceiveUserInfo userInfo: func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) {         guard let newGoal = userInfo["AddGoal"] as? Goal else { // "Goal" is the class generated by Xcode from your CoreData entity model, so newGoal is a CoreData object.             print("ERROR: unknown data received from Watch")             return         }         // now write newGoal to CoreData in the same way as you did on the watch     } I rarely use CoreData, so I'm not confident in writing sample code for you - but I know that this solution should work. You can then also use CoreData and SwiftUI to display a table of data on the watch (if you want). Time for me to put my feet up and relax :). Cheers, Michaela
Topic: App & System Services SubTopic: General Tags:
Sep ’21
Reply to Date in Swift printed is wrong
This is because the date is stored in Swift as UTC time, i.e. as at longitude 0.0 - which used to be called Greenwich Mean Time. It doesn't take into account your timezone, nor any Summertime adjustment. When you display the time to the user in your code, via UIKit or SwiftUI, you use a DateFormatter (in UIKit) or Text(date, style: .date) in SwiftUI and the system then recognises and applies your device's timezone (including summertime status) and language. SwiftUI has only limited date formatting capabilities, so I normally use my own formatting function e.g.in SwiftUI Text(myAppAPI.dateToString(date)) where myApAPI is a collection of useful functions I have in a class (myAppAPI) and dateToString is a function that applies the DateFormatter() function, with my applied options, of Swift. When you use print(date) no formatting is applied, therefore you get the UTC time.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Sep ’21
Reply to Date in Swift printed is wrong
Mm, comments don't format very well. Here it is again.... let currentHour = Calendar.current.component(.hour, from: Date()) if currentHour < 12 {print("now is morning")} This gets the hour, as an Integer, from the current local time, i.e. the Calendar function converts the UTC time to the user's current timezone and summertime (or not) settings.  I suggest that you experiment with Date formatting and with Calendar in a Playground.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Sep ’21
Reply to Date in Swift printed is wrong
let date = Calendar.current.dateComponents(in: .current, from: Date()).date! print(date) The .date! at the end of the first line converts the date back to UTC. To format the date in local time do this: let df = DateFormatter() df.dateStyle = .full df.timeStyle = .full print(df.string(from: Date())) You have options of full, medium, long, short and none for both time and date. If this doesn't suit, you can set your own formatting df.dateFormat = "yyyy-MM-dd HH:mm:ss.SS" which right now would give 2021-09-11 10:14:23.33 in Sydney time. You adjust the formatting tokens to suit your own needs, e.g. MM-dd-yy HH:mm would give 09-11-21 10:14 Regards, Michaela
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Sep ’21
Reply to Import SQLite legacy data into CoreData
The test import worked as planned, except (as mentioned by others elsewhere) for having to convert the "details" NSOrderedSet of "main" to a Swift Array for use in SwiftUI's ForEach. The import only took a second or two on an oldish iPad Pro in debug mode with Xcode 13 on a MacBook Pro (early 2015) - so that's OK. It looks like Fetch performance is good: List displays the 1000 or so "main" records, with their details, within a second - and that's on app launch. The next step is to implement the CloudKit sync, and see what happens with syncing to other devices: i.e. syncing of the largish volumes of data. Any thoughts/suggestions?
Topic: Programming Languages SubTopic: Swift Tags:
Sep ’21
Reply to DateComponentsFormatter UnitsStyle .abbreviated in MacOS
Thanks Quinn. I'm using Xcode 13.0 Beta 5, MacOS 12.0 Beta 2, with language set to English (Aus) and region set to Aus. I've a vague recollection of the problem being around for a while, but only now needed to investigate. On my system, a new Xcode created Mac project sees the same problem, but a new iOS project (also with the import Foundation - not UIKit) does not. As I said earlier, a Playground for iOS gives the "correct" formatting, but a Playground for the Mac platform (Playground Settings) gives the "mixed" formatting. Interestingly, in an Xcode generated multi-platform project (MacOS and iOS share the same code) the iOS app also shows the incorrect formatting, i.e. mixed. I'll lodge a bug report in the morning. Regards, Michaela
Topic: App & System Services SubTopic: General Tags:
Sep ’21
Reply to Stuck downloading Big Sur
I had a similar problem a few weeks ago. It was because TimeMachine was set for automatic backups: when I set to Manual ( and disconnected the backup drive) the Big Sur download succeeded. Maybe this is your cause (and maybe not ☹️) Regards, Michaela
Topic: App & System Services SubTopic: Core OS Tags:
Sep ’21
Reply to Multi-platform CoreData synchronisation: deleting data from all devices except one?
This seems to be the way https://developer.apple.com/documentation/coredata/linking_data_between_two_core_data_stores, at least in the first part of the problem ( separate stores). I haven’t done the watch part yet because I’ve been importing historical records into the Mac’s raw data container from an old iOS app to make sure the historical concepts are feasible. I’m thinking of keeping raw data on the watch for a couple of weeks before deletion, but will send each session (as a batch) to CloudKit, then the Mac, after session completion. Cheers, Michaela
Sep ’21
Reply to Use CoreDate with swift
PART 2 of answer (had to split because of character limit) EXAMPLE of Coredata Class Properties generated by Xcode and added to by developer (me) import Foundation import CoreData extension BucketItem {     @nonobjc public class func fetchRequest() -> NSFetchRequest<BucketItem> {         return NSFetchRequest<BucketItem>(entityName: "BucketItem")     }     @NSManaged public var cost: Double     @NSManaged public var date: Date?     @NSManaged public var location: String?     @NSManaged public var name: String?     @NSManaged public var priority: Int16     @NSManaged public var costItems: NSSet? } // MARK: Generated accessors for costItems extension BucketItem {     @objc(addCostItemsObject:)     @NSManaged public func addToCostItems(_ value: CostItem)     @objc(removeCostItemsObject:)     @NSManaged public func removeFromCostItems(_ value: CostItem)     @objc(addCostItems:)     @NSManaged public func addToCostItems(_ values: NSSet)     @objc(removeCostItems:)     @NSManaged public func removeFromCostItems(_ values: NSSet)     // MARK: Developer Generated properties for costItems MUST be replaced if Xcode regenerates other code     public var costArray : [CostItem] {         if costItems == nil { return [CostItem]() }         return costItems?.sorted(by: {($0 as! CostItem).costAmount < ($1 as! CostItem).costAmount}) as! [CostItem]     }     public var totalCost : Double {         if costItems  == nil { return 0.0 }         return costItems?.reduce(0.0, {$0 + ($1 as! CostItem).costAmount}) ?? 0.0     } } extension BucketItem : Identifiable { } There are 3 ways of accessing the DataModel (and CoreData stack) within SwiftUI Views: With a @StateObject (e.g. in the main App view) definition and subsequent environment method @StateObject var persistenceController = DataModel.shared ..... ContentView()                 .environment(\.managedObjectContext, persistenceController.persistentContainer.viewContext) Reference to the data model as an ObservedObject. @ObservedObject var dataModel = DataModel.shared ... List(dataModel.getBucketList(), id:\.name) { item in Direct reference to DataModel properties and methods  DataModel.shared.addCostItem(bucket: "RTW Trip", name: "Lots", cost: 40000.0) Which to use depends on the need and the volatility/mutability of the data. I hope this helps.  Regards, Michaela
Sep ’21
Reply to Use CoreDate with swift
This solution is for a single platform (iOS) with no CloudKit synchronisation:  if you need the latter, then I can provide a sample but it’s a bit more complex.  I’m not sure how far you’ve got with using Coredata, so I’ve included all that’s necessary to get a solution working. If you’re creating a new project, in Xcode’s project options select SwiftUI interface, Swift language, but not Use Core Data: if you do, Xcode creates template code that differs from this solution and needs to be gotten rid of. Create your Xcode data model:  in this example, the model name is “BucketListModel” with 2 entities “BucketItem” and “CostItem”.  Create a one-to-many relationship between BucketItem and CostItem, with a one-to-one inverse of CostItem to BucketItem. NOTE: These one-to-many and inverse relationships are important in understanding some of the quirks of using CoreData with SwiftUI/Swift. While still in Xcode’s data model editor, select both entities (BucketItem and CostItem) then in the Inspector panel (right-hand side of screen) change the Codegen option from Class Definition to Manual/None. Then, still in Xcode’s data model editor, use Editor > Create NSManagedObject Subclass then follow the prompts to select both entities and generate code that goes into your project folder.  You should end up with 4 additional Swift files:  “CostItem+CoreDataClass”, “CostItem+CoreDataProperties”, “BucketItem+CoreDataClass”, “BucketItem+CoreDataProperties”. In Xcode, select the BucketItem+CoreDataProperties file and notice that there’s a property called “costItems” which is of type NSSet.  SwiftUI cannot use this in a List, so it must be converted into a Swift array.  Also, an NSSet is unordered, so it’s best to apply some default sort-order - doing so also creates a Swift array.  I created a computed property called costArray, which performs the sort and set conversion.  Note the other computed property, totalCost, which performs a reduce operation on the costItems.  This is an example of how you add functionality by extending the code in each entitiy’s class. Create a Swift file (e.g. called “DataModel”) with your (not Xcode's) DataModel class as per the sample below.  Key features of this class are:  ObservableObject, a singleton (only one instance in the app), loads and manages the Coredata stack, and publishes the bucketList. import Foundation import CoreData class DataModel : ObservableObject {     static let shared = DataModel()     let persistentContainer : NSPersistentContainer     @Published var bucketList = [BucketItem]() private init() {         self.persistentContainer = {             let container = NSPersistentContainer(name: "BucketListModel")             container.loadPersistentStores(completionHandler: { description, error in                 if let error = error {                     print("ERROR loading persistent store \(error)")                 }             })             return container         }()     }     func saveContext() {         let context = persistentContainer.viewContext         if context.hasChanges {             do {                 try context.save()             } catch {                 print("ERROR: Unable to save context")             }         }     }     func addCostItem(bucket: String,name: String,cost: Double) {         let costItem = CostItem(context: persistentContainer.viewContext)         costItem.costName = name         costItem.costAmount = cost         guard let thisBucketItem = getBucketItemFor(bucket) else { return }         thisBucketItem.addToCostItems(costItem)         saveContext()         bucketList = getBucketList()     }     func addBucketItem(name: String,priority: Int, cost: Double, location: String) {         let bucketItem = BucketItem(context: persistentContainer.viewContext)         bucketItem.name = name         bucketItem.priority = Int16(priority)         bucketItem.cost = cost         bucketItem.location = location         bucketItem.date = Date()         saveContext()         bucketList = getBucketList()     }          func getBucketItemFor(_ name: String) -> BucketItem? {         let context = persistentContainer.viewContext         var request = NSFetchRequest<BucketItem>()         request = BucketItem.fetchRequest()         request.fetchLimit = 1         request.entity = NSEntityDescription.entity(forEntityName: "BucketItem", in: context)         request.predicate = NSPredicate(format: "name == %@", name)         do {             let bucketItems = try context.fetch(request)             if bucketItems.count != 1 { return nil}             return bucketItems.first         } catch {             print("ERROR: course fetch failed \(error)")             return nil         }     }     func getBucketList() -> [BucketItem] {         var tempList = [BucketItem]()         let context = persistentContainer.viewContext         let request = NSFetchRequest<BucketItem>()         request.sortDescriptors = [NSSortDescriptor(keyPath: \BucketItem.priority, ascending: true)]         request.entity = NSEntityDescription.entity(forEntityName: "BucketItem", in: context)         do {             if !bucketList.isEmpty { bucketList.removeAll()}             tempList = try context.fetch(request)         } catch {             print("ERROR: fetch failed \(error)")         }         return tempList          }     func getCostTotal() -> Double {         var total = 0.0         let tempList = getBucketList()         total = tempList.reduce(0.0, {$0 + $1.cost})         return total     } }``
Sep ’21
Reply to help me! (data sync between watch and phone)
I’ll answer your questions in 3 sections: 1. DataModels and SwiftUI, 2. Watch Connectivity and 3. Saving and synchronising data. DataModels and SwiftUI A key concept of SwiftUI is that the user interface (SwiftUI Views) only interacts with the user, displaying text etc and collecting input (e.g. button presses).  All other app processing (e.g. data manipulation, data storage and retrieval) is done elsewhere in an app-specific class (typically called “DataModel”) created by the developer.  Your app essentially has two parts: the watch app and the phone app.  Each needs a DataModel - “WatchDataModel” and “PhoneDataModel” - being names that I decided.  The tasks of the WatchDataModel are to 1. Collect and aggregate (?) titles and counts, 2. Transmit those to the phone 3. Maintain a local copy of the collected data 4. Prepare data for displaying to the user.  The PhoneDataModel has to 1. Receive aggregated data from the watch 2. Store those data locally, synchronising as required with existing data (?) 3. Prepare data for displaying to the user, i.e. creating a property that a SwiftUI View can refer to for displaying on the screen. In my sample code, each data model is created as a singleton - meaning that there is only ever one copy created, no matter how many times it gets referred to elsewhere within your app. class WatchDataModel : NSObject, WCSessionDelegate, ObservableObject {     static let shared = WatchDataModel() // when referring to WatchDataModel.shared only one version is ever created     let session = WCSession.default // this essentially creates a shortcut to WCSession.default Watch Connectivity Within each data model, the connectivity to/from the watch is provided by the WatchConnectivity framework and the DataModel’s class has to adopt the WCSessionDelegate protocol for this to work.  import WatchConnectivity // the Apple code to control the watch-phone connection class WatchDataModel : NSObject, WCSessionDelegate, ObservableObject { // WCSessionDelegate allows this class to be "delegated" certain tasks from WCSession When your app starts up (Watch or Phone), it needs to initialise its data model, which then needs to tell the connectivity controller (WCSession.default) to delegate certain tasks (e.g. in PhoneDataModel, deal with incoming data) to the data model.  You can refer to this controller directly as WCSession.default, but typically one creates a static property let session = WCSession.default and then refer to that property.  Before sending / receiving, you have to activate a session (in fact, you should really check that a session is active before attempting to send anything). if WCSession.isSupported() { // checks if this device is capable of watch-phone communication (e.g. an iPad is not)             session.delegate = self // allows this DataModel (class) to process tasks delegated (assigned) to it by WCSession             session.activate() // activates a session so that communication can occur         } else {             print("ERROR: Watch session not supported")         } As I’ve said previously, there’s a number of ways of sending / receiving data:  the choice depends on the volume and type of data, and the urgency of the communication.  The one I usually use is transferUserInfo, because of its reliability under various situations. transferUserInfo sends a dictionary of property list values e.g. “Title”:”Hello World” or “Count” : 21 - the first part is the key and the second is the actual value.  But there are various ways of constructing a Dictionary, especially with multiple values.  Ref https://developer.apple.com/documentation/swift/dictionary. I don’t know enough about the purpose of your app and the volume / complexity of the data for me to make any suggestion, apart from my previous idea of a struct:   struct CountData {     var title : String = ""     var tCounts = [Int]()  // an array of counts     var creationDate = Date() } data.session.transferUserInfo(["CountData":data.countData]) // in the watch's ContentView we send a CountData struct (in data.countData) with a key of "CountData" via transferUserInfo to the phone Importantly, when your phone app receives the dictionary, you must be able to access the dictionary values and do whatever you need to do. func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { // WCSession has delegated to this PhoneDataModel the task of receiving and processing user info         guard let countData = userInfo["CountData"] as? CountData else { // check that what has been received is the info that was sent. In this example a struct we defined, CountData containing title, counts and date, was sent with a key "CountData"             print("ERROR: unknown data received from Watch")             return         }         // now do something with the received CountData     } Saving and Synchronising Data Having received the watch data on the phone via didReceiveUserInfo you now have to do something with it. But what? I can think of at least 6 different ways of saving and synchronising your data (i.e. making sure the watch and phone have identical sets of data). But, again, I don’t know enough about the purpose of your app and the volume / complexity of the data for me to make any recommendation. Your idea of UserDefaults could be problematic because of your data characteristics and the limits of UserDefaults. Ref https://developer.apple.com/documentation/foundation/userdefaults I normally use SQLIte instead of CoreData, but that's only because I'd been using SQL databases for decades before I came to Apple development and I find SQL's queries, views, joins and indexes far more flexible - and within my knowledge base. I'll have to finish here: I'm tired (: Regards, Michaela
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to help me! (data sync between watch and phone)
OK, now that I understand better what you are doing, this is what I would recommend (assuming that your data volumes are not several megabytes in total): Use CoreData on both the Watch and the Phone, with a CoreData entity model that is exactly the same on both. When the watch app starts, create a new Goal (using the entity class created by Xcode in XCDataModel) and set the item name, then start counting. When the "Send to Phone" button is pressed, save (write) the new Goal to Coredata on the Watch and also send it to the phone using transferUserInfo(["AddGoal":newGoal]). This sends a CoreData object to the phone, for saving in the phone's CoreData storage. In the phone app, with the same CoreData entity model, get the newGoal from didReceiveUserInfo userInfo: func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) {         guard let newGoal = userInfo["AddGoal"] as? Goal else { // "Goal" is the class generated by Xcode from your CoreData entity model, so newGoal is a CoreData object.             print("ERROR: unknown data received from Watch")             return         }         // now write newGoal to CoreData in the same way as you did on the watch     } I rarely use CoreData, so I'm not confident in writing sample code for you - but I know that this solution should work. You can then also use CoreData and SwiftUI to display a table of data on the watch (if you want). Time for me to put my feet up and relax :). Cheers, Michaela
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Date in Swift printed is wrong
This is because the date is stored in Swift as UTC time, i.e. as at longitude 0.0 - which used to be called Greenwich Mean Time. It doesn't take into account your timezone, nor any Summertime adjustment. When you display the time to the user in your code, via UIKit or SwiftUI, you use a DateFormatter (in UIKit) or Text(date, style: .date) in SwiftUI and the system then recognises and applies your device's timezone (including summertime status) and language. SwiftUI has only limited date formatting capabilities, so I normally use my own formatting function e.g.in SwiftUI Text(myAppAPI.dateToString(date)) where myApAPI is a collection of useful functions I have in a class (myAppAPI) and dateToString is a function that applies the DateFormatter() function, with my applied options, of Swift. When you use print(date) no formatting is applied, therefore you get the UTC time.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Date in Swift printed is wrong
Mm, comments don't format very well. Here it is again.... let currentHour = Calendar.current.component(.hour, from: Date()) if currentHour < 12 {print("now is morning")} This gets the hour, as an Integer, from the current local time, i.e. the Calendar function converts the UTC time to the user's current timezone and summertime (or not) settings.  I suggest that you experiment with Date formatting and with Calendar in a Playground.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Date in Swift printed is wrong
??? Other comments and my examples that we exchanged disappeared from the thread. Have you got things working now the way you want? I'm getting an error when trying to add a new comment to the thread below.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Date in Swift printed is wrong
let date = Calendar.current.dateComponents(in: .current, from: Date()).date! print(date) The .date! at the end of the first line converts the date back to UTC. To format the date in local time do this: let df = DateFormatter() df.dateStyle = .full df.timeStyle = .full print(df.string(from: Date())) You have options of full, medium, long, short and none for both time and date. If this doesn't suit, you can set your own formatting df.dateFormat = "yyyy-MM-dd HH:mm:ss.SS" which right now would give 2021-09-11 10:14:23.33 in Sydney time. You adjust the formatting tokens to suit your own needs, e.g. MM-dd-yy HH:mm would give 09-11-21 10:14 Regards, Michaela
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Import SQLite legacy data into CoreData
The test import worked as planned, except (as mentioned by others elsewhere) for having to convert the "details" NSOrderedSet of "main" to a Swift Array for use in SwiftUI's ForEach. The import only took a second or two on an oldish iPad Pro in debug mode with Xcode 13 on a MacBook Pro (early 2015) - so that's OK. It looks like Fetch performance is good: List displays the 1000 or so "main" records, with their details, within a second - and that's on app launch. The next step is to implement the CloudKit sync, and see what happens with syncing to other devices: i.e. syncing of the largish volumes of data. Any thoughts/suggestions?
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to DateComponentsFormatter UnitsStyle .abbreviated in MacOS
Thanks Quinn. I'm using Xcode 13.0 Beta 5, MacOS 12.0 Beta 2, with language set to English (Aus) and region set to Aus. I've a vague recollection of the problem being around for a while, but only now needed to investigate. On my system, a new Xcode created Mac project sees the same problem, but a new iOS project (also with the import Foundation - not UIKit) does not. As I said earlier, a Playground for iOS gives the "correct" formatting, but a Playground for the Mac platform (Playground Settings) gives the "mixed" formatting. Interestingly, in an Xcode generated multi-platform project (MacOS and iOS share the same code) the iOS app also shows the incorrect formatting, i.e. mixed. I'll lodge a bug report in the morning. Regards, Michaela
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to DateComponentsFormatter UnitsStyle .abbreviated in MacOS
Bug report is FB9643489 Regards, Michaela
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Stuck downloading Big Sur
I had a similar problem a few weeks ago. It was because TimeMachine was set for automatic backups: when I set to Manual ( and disconnected the backup drive) the Big Sur download succeeded. Maybe this is your cause (and maybe not ☹️) Regards, Michaela
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Multi-platform CoreData synchronisation: deleting data from all devices except one?
This seems to be the way https://developer.apple.com/documentation/coredata/linking_data_between_two_core_data_stores, at least in the first part of the problem ( separate stores). I haven’t done the watch part yet because I’ve been importing historical records into the Mac’s raw data container from an old iOS app to make sure the historical concepts are feasible. I’m thinking of keeping raw data on the watch for a couple of weeks before deletion, but will send each session (as a batch) to CloudKit, then the Mac, after session completion. Cheers, Michaela
Replies
Boosts
Views
Activity
Sep ’21
Reply to DateComponentsFormatter UnitsStyle .abbreviated in MacOS
Thanks Quinn, I've added formatter.calendar!.locale = Locale(identifier: "en_UK") to my function (we sort of speak the same language here, in Oz, as in the UK). All works correctly with that change applied. Regards, Michaela
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Sep ’21
Reply to Use CoreDate with swift
Did you solve this? If not, I have sample code from a recent project, which also includes CloudKit synchronisation and multi-platform. Regarda, Michaela
Replies
Boosts
Views
Activity
Sep ’21
Reply to Use CoreDate with swift
PART 2 of answer (had to split because of character limit) EXAMPLE of Coredata Class Properties generated by Xcode and added to by developer (me) import Foundation import CoreData extension BucketItem {     @nonobjc public class func fetchRequest() -> NSFetchRequest<BucketItem> {         return NSFetchRequest<BucketItem>(entityName: "BucketItem")     }     @NSManaged public var cost: Double     @NSManaged public var date: Date?     @NSManaged public var location: String?     @NSManaged public var name: String?     @NSManaged public var priority: Int16     @NSManaged public var costItems: NSSet? } // MARK: Generated accessors for costItems extension BucketItem {     @objc(addCostItemsObject:)     @NSManaged public func addToCostItems(_ value: CostItem)     @objc(removeCostItemsObject:)     @NSManaged public func removeFromCostItems(_ value: CostItem)     @objc(addCostItems:)     @NSManaged public func addToCostItems(_ values: NSSet)     @objc(removeCostItems:)     @NSManaged public func removeFromCostItems(_ values: NSSet)     // MARK: Developer Generated properties for costItems MUST be replaced if Xcode regenerates other code     public var costArray : [CostItem] {         if costItems == nil { return [CostItem]() }         return costItems?.sorted(by: {($0 as! CostItem).costAmount < ($1 as! CostItem).costAmount}) as! [CostItem]     }     public var totalCost : Double {         if costItems  == nil { return 0.0 }         return costItems?.reduce(0.0, {$0 + ($1 as! CostItem).costAmount}) ?? 0.0     } } extension BucketItem : Identifiable { } There are 3 ways of accessing the DataModel (and CoreData stack) within SwiftUI Views: With a @StateObject (e.g. in the main App view) definition and subsequent environment method @StateObject var persistenceController = DataModel.shared ..... ContentView()                 .environment(\.managedObjectContext, persistenceController.persistentContainer.viewContext) Reference to the data model as an ObservedObject. @ObservedObject var dataModel = DataModel.shared ... List(dataModel.getBucketList(), id:\.name) { item in Direct reference to DataModel properties and methods  DataModel.shared.addCostItem(bucket: "RTW Trip", name: "Lots", cost: 40000.0) Which to use depends on the need and the volatility/mutability of the data. I hope this helps.  Regards, Michaela
Replies
Boosts
Views
Activity
Sep ’21
Reply to Use CoreDate with swift
This solution is for a single platform (iOS) with no CloudKit synchronisation:  if you need the latter, then I can provide a sample but it’s a bit more complex.  I’m not sure how far you’ve got with using Coredata, so I’ve included all that’s necessary to get a solution working. If you’re creating a new project, in Xcode’s project options select SwiftUI interface, Swift language, but not Use Core Data: if you do, Xcode creates template code that differs from this solution and needs to be gotten rid of. Create your Xcode data model:  in this example, the model name is “BucketListModel” with 2 entities “BucketItem” and “CostItem”.  Create a one-to-many relationship between BucketItem and CostItem, with a one-to-one inverse of CostItem to BucketItem. NOTE: These one-to-many and inverse relationships are important in understanding some of the quirks of using CoreData with SwiftUI/Swift. While still in Xcode’s data model editor, select both entities (BucketItem and CostItem) then in the Inspector panel (right-hand side of screen) change the Codegen option from Class Definition to Manual/None. Then, still in Xcode’s data model editor, use Editor > Create NSManagedObject Subclass then follow the prompts to select both entities and generate code that goes into your project folder.  You should end up with 4 additional Swift files:  “CostItem+CoreDataClass”, “CostItem+CoreDataProperties”, “BucketItem+CoreDataClass”, “BucketItem+CoreDataProperties”. In Xcode, select the BucketItem+CoreDataProperties file and notice that there’s a property called “costItems” which is of type NSSet.  SwiftUI cannot use this in a List, so it must be converted into a Swift array.  Also, an NSSet is unordered, so it’s best to apply some default sort-order - doing so also creates a Swift array.  I created a computed property called costArray, which performs the sort and set conversion.  Note the other computed property, totalCost, which performs a reduce operation on the costItems.  This is an example of how you add functionality by extending the code in each entitiy’s class. Create a Swift file (e.g. called “DataModel”) with your (not Xcode's) DataModel class as per the sample below.  Key features of this class are:  ObservableObject, a singleton (only one instance in the app), loads and manages the Coredata stack, and publishes the bucketList. import Foundation import CoreData class DataModel : ObservableObject {     static let shared = DataModel()     let persistentContainer : NSPersistentContainer     @Published var bucketList = [BucketItem]() private init() {         self.persistentContainer = {             let container = NSPersistentContainer(name: "BucketListModel")             container.loadPersistentStores(completionHandler: { description, error in                 if let error = error {                     print("ERROR loading persistent store \(error)")                 }             })             return container         }()     }     func saveContext() {         let context = persistentContainer.viewContext         if context.hasChanges {             do {                 try context.save()             } catch {                 print("ERROR: Unable to save context")             }         }     }     func addCostItem(bucket: String,name: String,cost: Double) {         let costItem = CostItem(context: persistentContainer.viewContext)         costItem.costName = name         costItem.costAmount = cost         guard let thisBucketItem = getBucketItemFor(bucket) else { return }         thisBucketItem.addToCostItems(costItem)         saveContext()         bucketList = getBucketList()     }     func addBucketItem(name: String,priority: Int, cost: Double, location: String) {         let bucketItem = BucketItem(context: persistentContainer.viewContext)         bucketItem.name = name         bucketItem.priority = Int16(priority)         bucketItem.cost = cost         bucketItem.location = location         bucketItem.date = Date()         saveContext()         bucketList = getBucketList()     }          func getBucketItemFor(_ name: String) -> BucketItem? {         let context = persistentContainer.viewContext         var request = NSFetchRequest<BucketItem>()         request = BucketItem.fetchRequest()         request.fetchLimit = 1         request.entity = NSEntityDescription.entity(forEntityName: "BucketItem", in: context)         request.predicate = NSPredicate(format: "name == %@", name)         do {             let bucketItems = try context.fetch(request)             if bucketItems.count != 1 { return nil}             return bucketItems.first         } catch {             print("ERROR: course fetch failed \(error)")             return nil         }     }     func getBucketList() -> [BucketItem] {         var tempList = [BucketItem]()         let context = persistentContainer.viewContext         let request = NSFetchRequest<BucketItem>()         request.sortDescriptors = [NSSortDescriptor(keyPath: \BucketItem.priority, ascending: true)]         request.entity = NSEntityDescription.entity(forEntityName: "BucketItem", in: context)         do {             if !bucketList.isEmpty { bucketList.removeAll()}             tempList = try context.fetch(request)         } catch {             print("ERROR: fetch failed \(error)")         }         return tempList          }     func getCostTotal() -> Double {         var total = 0.0         let tempList = getBucketList()         total = tempList.reduce(0.0, {$0 + $1.cost})         return total     } }``
Replies
Boosts
Views
Activity
Sep ’21