Posts

Post not yet marked as solved
4 Replies
I was having the same problem with Xcode 15 betas on an M1MacMini with Ventura betas. For me it seems to have been a space problem: Developer files on the main HD kept ballooning to over 60GB on an already constrained machine. When I moved Xcode to an external USB-C SSD and set Locations in Xcode Settings to that drive - the download and registration worked fine, plus there's more free space for other uses.
Post not yet marked as solved
4 Replies
Fixed the problem by these steps: Created a backup of the MacMini's persistent store, using code for a temporary persistent store coordinator.migratePersistentStore. Also created CSV file backups of all objects of all entities (as a fall-back if the migrate didn't work correctly). Deleted all objects of all Entities in the MacMini persistent store. Waited for deletions (Step 3) to be propagated to the CloudKit container. Checked the CloudKit container (Web Console) and manually deleted any Entity Objects not deleted by Steps 3 and 4 - there were quite a few. Restored the MacMini persistent store by code for: a persistentStoreCoordinator.destroyPersistentStore of the original MacMini store b persistentStoreCoordinator.addPersistentStore based on the backup store c persistentStoreCoordinator.migratePersistentStore from the backup to the "new" store (step 6 b) Waited for the "new store" Entity Objects to be propagated to the CloudKit container. Checked the CloudKit container (Web Console) - all good Checked that additions/deletions worked correctly whether on the MacMini or via The CloudKit Console - all good. Somewhat of a pain, but necessary given the amount of data and its complexity. An advantage is that I now have backup and restore functions in the file menu of the MacMini App. Regards, Michaela
Post not yet marked as solved
4 Replies
I've created a test app, with CoreData records synced via CloudKit. Records created on the Mac get synced to the iPad Pro. It's a very simple app, just Listing records from a FetchedResults. I then Reset development environment in the CloudKit console Web Portal, with both devices' app closed. This Reset, of course, deleted all records, schema and coredata.cloudkit zone from the Development environment. On restarting each app, CoreData records on both devices remained and the app worked correctly, fetching existing records and also recreated the CloudKit zone, schema and records. Thereafter new records on the Mac were correctly synced to the iPad Pro. I'll do some more testing, making the test app more complex with the sort of relationship I was trying to achieve in the messed-up app, then might be bold enough to use this approach to fix the problem app. However, it would still be better if I could somehow purge the CloudKit update queue - although I can't be sure that the schema is correct anyway: probably not. Regards, Michaela
Post not yet marked as solved
1 Replies
This what I use in my apps (during the initialisation of my Data Controller): self.mainContainer = { let container = NSPersistentCloudKitContainer(name: "MyDataModel") container.loadPersistentStores(completionHandler: { description, error in if let error = error { print("**** ERROR loading persistent store \(error)") } //Setup auto merge of Cloudkit data container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //Set the Query generation to .current. for dynamically updating views from Cloudkit try? container.viewContext.setQueryGenerationFrom(.current) }) return container }() The key lines are the 2 below //Setup auto merge. Also, be sure to have enabled Remote Notifications in Background modes of the App's Signing and Capabilities. I hope this helps. Regards, Michaela
Post marked as solved
2 Replies
If the app referenced by Claude31 is available to you/your university, then this would be the way to go in getting data from a variety of volunteer subjects who have an ECG capable Apple Watch and an iPhone. However, if you're a developer with an Apple Watch & iPhone and/or have colleagues with them and you're only collecting your data (or your colleagues') , then a fairly simple Swift App will suffice to get the raw data, which are a timestamp and voltage. The Apple documentation is https://developer.apple.com/documentation/healthkit/hkelectrocardiogram I use my such app to create a CSV file, which I then import to an ECG Viewer. I use this setup quite frequently, as well as data from a Polar H10 ECG app, to monitor my various arrhythmias and provide data to EDs as required (unfortunately, too often for comfort!). Regards, Michaela
Post not yet marked as solved
7 Replies
I'm about to implement a similar use-case: I already chart data for the current week, current month, quarter and year, as selected by a picker. The key to the paging solution is to replace the chart dataset (series), when the user swipes left or right, with the appropriate next or previous set (period) of data. In my case, I'll use my existing Coredata fetch function with startDate, endDate parameters - called from within the Gesture Recogniser. We'll need a var with the currently displayed period in order to set the required retrieval startDate, endDate. And there also needs be a @State var to trigger a chart redraw once the new dataset has been returned (maybe the dataset itself or the period or a flag). Or, the period and dataset creation/recreation can be handled in the DataModel (my usual approach) with @Published vars. This should work fine for discrete periods (i.e. week, month, etc), but continuous scrolling over a large dataset is a different proposition. I've done it with a SwiftUI Canvas app, but it was hard to get smooth scrolling with many fetches from a large database. I'll post more when my paging solution works (or doesn't......). Regards, Michaela
Post not yet marked as solved
2 Replies
I can confirm that current versions of iOS and Watch OS (14+ and 7+) provide access to ECG classifications (e.g. Atrial Fibrillation). The relevant code is: let ecgType = HKObjectType.electrocardiogramType() let ecgPredicate = HKQuery.predicateForElectrocardiograms(classification: .atrialFibrillation) let ecgQuery = HKSampleQuery(sampleType: ecgType,                                      predicate: ecgPredicate,                                      limit: HKObjectQueryNoLimit,                                      sortDescriptors: nil) { (query, samples, error) in ............... If you then need to access the actual ECG Voltages (500 measurements per second, I think) for a sample: let voltageQuery = HKElectrocardiogramQuery(ecgSample) { (query, result) in             switch(result) {             case .measurement(let measurement):                 if let voltageQuantity = measurement.quantity(for: .appleWatchSimilarToLeadI) { // process each voltage measurement ........... } // Execute the query.         healthStore.execute(voltageQuery) Regards, Michaela
Post marked as solved
2 Replies
Create a function in your PersistenceController class to detect and remove duplicates: func deDup(_ items: [Item]) { let context = container.viewContext var prevItem : Item? for item in items { if prevItem == nil { prevItem = item continue } if item.name! == prevItem!.name! { // this is a duplicate prevItem!.value += item.value context.delete(item) } else { prevItem = item } } contextSave() } Change you combineItemsButton action to be Button(action:{ //combine duplicates here             persistence.deDup(items)             items = persistence.getItems() I have not tested this within any app (i.e. just written the code here) and have done it in a hurry, but I think that the logic is correct. I hope it works!!!! Regards, Michaela
Post marked as solved
2 Replies
Main Struct @main struct TestForWeagleWeagleApp: App {     let persistence = PersistenceController.shared  // initiates the CoreData stack     var body: some Scene {         WindowGroup {             ContentView()         }     } } Persistence import Foundation import CoreData class PersistenceController : ObservableObject {     static let shared = PersistenceController()     let container: NSPersistentContainer     init(inMemory: Bool = false) {         container = NSPersistentContainer(name: "Test")         if inMemory {             container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")         }         container.loadPersistentStores(completionHandler: { (storeDescription, error) in             if let error = error as NSError? {                 fatalError("Unresolved error \(error), \(error.userInfo)")             }         })         container.viewContext.automaticallyMergesChangesFromParent = true     }     func addItem(date: Date){         let context = container.viewContext         let item = Item(context: context)         item.timestamp = date         item.word = nil         contextSave()     }     func addToItem(item: Item) {         item.word = "Test"         contextSave()     }     func removeFromItem(item: Item){         item.word = nil         contextSave()     }     func contextSave() {         let context = container.viewContext         if context.hasChanges {             do {                 try context.save()                 self.objectWillChange.send()             } catch {                 print("**** ERROR: Unable to save context \(error)")             }         }     }     func getItemsFor(_ date: Date) -> [Item] {         let context = container.viewContext         var request = NSFetchRequest<Item>()         request = Item.fetchRequest()         //request.fetchLimit = 1         request.entity = NSEntityDescription.entity(forEntityName: "Item", in: context)         request.predicate = NSPredicate(format: "timestamp >= %@ and timestamp <= %@", Calendar.current.startOfDay(for: date) as CVarArg, Calendar.current.startOfDay(for: date).addingTimeInterval(86399.0) as CVarArg)         do {             let items = try context.fetch(request)             if items.count == 0 { return []}                   return items.sorted(by: {$0.timestamp! > $1.timestamp!})         } catch {             print("**** ERROR: items fetch failed \(error)")             return []         }     } } ContentView import SwiftUI struct ContentView: View {     @ObservedObject var persistence = PersistenceController.shared     @State private var items = PersistenceController.shared.getItemsFor(Date())     @State private var date = Date.now     var body: some View {         NavigationView{                 VStack {                     DatePicker("Calendar", selection: $date, in: Date.now...,displayedComponents: [.date])                     .datePickerStyle(.graphical)                     .onAppear(perform: {                         if items.isEmpty {                             persistence.addItem(date: date)                             items = persistence.getItemsFor(date)                         }                     })                 if !(items.isEmpty) {                     PlannedMealsView(item: items.last!)                     Spacer()                 }             }                  .navigationBarTitle("My Planner")         }         .onChange(of: date){ newDate in             items = persistence.getItemsFor(newDate)             if items.isEmpty {                 persistence.addItem(date: newDate)                 items = persistence.getItemsFor(newDate)             }         }     }     func getTitle(date: Date)->String{         let formatter = DateFormatter()         formatter.dateStyle = .medium         return formatter.string(from: date)     } } PerformanceMealsView import SwiftUI struct PlannedMealsView: View {     @ObservedObject var persistence = PersistenceController.shared     var item: Item     @State private var forceRefresh : Bool = false     var body: some View {         VStack{             Text(item.timestamp!, style: .date)                 .font(.title2)                 .bold()             Section("Word"){                 if(item.word != nil){                     HStack{                         Spacer()                         Text(item.word!)                         Spacer()                         Button(action: {                             persistence.removeFromItem(item: item)                         }){                             Image(systemName: "minus.circle").bold()                         }                         Spacer()                     }                 } else {                     Button(action: {                         persistence.addToItem(item: item)                         forceRefresh.toggle()                     }){                         Image(systemName: "plus.circle").bold()                             .padding(.vertical, 10)                             .padding(.horizontal, 20)                     }                 }             }             Spacer()         }     } } This works on my system, except that (probably unwisely) I used my development environment, which uses beta Xcode and beta iOS. I couldn't backwards convert the Xcode project (new format) to test on my production equipment without redoing everything. I hope this works for you too!!! Regards, Michaela
Post not yet marked as solved
2 Replies
As per my comment on your other post, I've had a good look at your code and make the following observations: Your PersistenceController is a struct and you reference it a number of times in your Views e.g. PersistenceController().removeFromItem(item: items[0], context: managedObjContext), which means that your CoreData stack is being recreated each time (ie numerous copies) - with unpredictable results. The PersistenceController needs to be an ObservableObject class singleton, i.e. with let shared =PersistenceController(), and then refer to the shared instance. the date that you set from the calendar picker is the time of picking, i.e. date and time, so your predicate, which also uses the current date and time, will probably never match. I assume that you're looking for an item (or items) that occur on a selected day (date only, not time). The predicate therefore needs to search for a timestamp that occurs within the start and end of a day (date). It's not clear where, or if, you created the @StateObject for the @Environment(\.managedObjectContext) var managedObjContext, without which the @FetchedResults are unlikely to work (plus the problem of multiple CoreData stack instances). When the above issues are resolved, there remains the problem of getting SwiftUI to re-execute the Fetch on a change of date i.e. a dynamic predicate with immediate effect. I've created a working version of your code, but without the FetchRequest and Results in ContentView: I use a fetch function in PersistenceController to return Items with a timestamp that falls within the specified day (midnight to 11:59:59pm). When the selected date changes, the function gets called to return the item(s) for that date. That's the item (or item array) that then gets used in your existing code. I'll post the full solution tomorrow morning (about 00:00 UTC Sunday 26 June ) after I've further tested it. Regards, Michaela
Post not yet marked as solved
1 Replies
I haven’t done this in UIKit, but in SwiftUI I pass the entity to the second view, modify the attribute on that entity, then perform a Context save of the Entity (by using a function in my CodeData stack). CoreData entities are classes and therefore get passed by reference. Having modified and saved the Entity + attribute, you then probably need to force a view refresh, which can be tricky depending on your implementation of CoreData in your views and whether UIKit or SwiftUI. regards, Michaela
Post not yet marked as solved
2 Replies
I’ve recently started looking at retrieving ECG data in HealthKit, as currently recorded by my Watch (mine’s on 8.7). I already have a database of ECG recordings from my Polar H10 strap and want to incorporate the Watch data (from a different lead position). i note that HealthKit now has an HKElectrocardiogram sample [https://developer.apple.com/documentation/healthkit/hkelectrocardiogram), which has a property for classification e.g. “atrialFibrillation”. So, we should be able, now, to do an HKQuery to retrieve AFib classifications with or without the voltage data. I haven’t tried yet, but soon will. I hope this helps. Regards, Michaela PS I’ve developed an ECG Viewer with SwiftUI Canvas for the voltage time series ( currently Polar H10 data) and will be adding the capability for plotting the Watch data.
Post marked as solved
3 Replies
I had similar problems with an expenses management app, where I have a hierarchy of Groupings (master) with Transactions (detail) against the lowest Grouping in the hierarchy.  The app uses CoreData with CloudKit sync and is multi-platform., both of which add complexity to the design approach. My solution is this: In the CoreData model there are two entities, Grouping and Transaction: with a one-to-many relationship from Grouping to Transaction and one-to-one reciprocal from Transaction to Grouping.  A Fetch of a Grouping (master), or set of Groupings, therefore has the transactions (detail) as an NSManaged NSSet on each Grouping (master) record.  In the Xcode editor of the CoreData model I use Manual Codegen to put the NSManagedObject entity, properties and accessors in my project.  This allows for easier coding of extensions to create computed properties on the CoreData entities, e.g. summing transactions for a Grouping. I have a DataStore ObservableObject class singleton that handles all CoreData / CloudKit setup and data processing, e.g. all Fetches, insertions, deletions etc.  For example, there is a DataStore method to fetch all Groupings (master records) “getAllGroupings” which returns an array of CoreData Grouping objects, each of which has its own set of Transaction records (detail).  So, in a SwiftUI View, I have a @ObservedObject var dataStore = DataStore.shared, which means that I can therefore refer to the Groupings array in a List by using List(dataStore.getAllGroupings()) …..  To get the list of Transactions in another (detail) view, I pass the required Grouping (master record) to the detail view as a var, then use List(grouping.transactionsArray) - where “transactionsArray” is a computed property on my Grouping CoreData extension that turns an NSSet into a Swift Array. **** This particular solution doesn't need datastore to be an ObservedObject, but I use that for other reasons in the app. To delete a transaction in detail view, I call a method on dataStore e.g. deleteTransaction(transaction: Transaction) which does a context.delete(transaction) and then a context save.  This deletes the transaction and its entry in the Grouping’s transaction NSSet (according to the delete rules I set in CoreData). HOWEVER: this delete method (or add a transaction) does not update the view, because the changes are made to the CoreData context, which is not directly observable by SwiftUI. So, in DataStore I have a Combine property to send a notification public let transactionDeleted = PassthroughSubject<(String), Never>()   then in the “deleteTransaction” method, I use transactionDeleted.send(“txn deleted”) - you can set up the PassthroughSubject to send whatever you want. Either in your Master View or DetailView (depends on your processing - need to experiment), have .onReceive((dataStore.transactionDeleted), perform: { transaction in             self.statusTime = Date()         }) This updates a @State var statusTime which, unfortunately, needs to be used somewhere in the view: this triggers a View refresh and, voila, the deletion no longer appears in the View. NOTE: I use public for my methods and properties in DataStore because this is a Multiplatform app and DataStore is therefore in a framework. This is also why I use Manual CodeGen in Xcode's CoreData models, i.e. to use in a framework. I also use the PassthroughSubject in non-CoreData apps where there’s complex data processing, which if ObservedObjects are used can cause unnecessary view refreshes.  In these cases, once all processing has completed I then issue the PassthroughSubject send to update the view. I hope this helps.  Regards, Michaela
Post not yet marked as solved
1 Replies
I recently wrote an HTTP Listener (i.e. TCP based) app in Swift for MacOS and at first I didn't know how to go about it, so I posted a question here on the Forum.. I ended up using the Network framework (3rd answer in the post), which is based on a WWDC video (as referenced in the post). My code and that video should help get you started. Regards, Michaela
Post not yet marked as solved
3 Replies
OMG, the solution is very easy - at least in a prototype state. Now let's see how it performs in reality, with code for handling state changes. import Foundation import Network class HTTPServer {     static let shared = HTTPServer()     var listener : NWListener     var queue : DispatchQueue     var connected : Bool = false     let nwParms = NWParameters.tcp          init() {         queue = DispatchQueue(label: "HTTP Server Queue")         listener = try! NWListener(using: nwParms, on: 80)         listener.newConnectionHandler = { [weak self] (newConnection ) in             print("**** New Connection added")             if let strongSelf = self {                 newConnection.start(queue: strongSelf.queue)                 strongSelf.receive(on: newConnection)             }         }         listener.stateUpdateHandler = { (newState) in             print("**** Listener changed state to \(newState)")         }         listener.start(queue: queue)     }     func receive(on connection: NWConnection) {         connection.receive(minimumIncompleteLength: 0, maximumLength: 4096) { (content, context, isComplete, error) in             guard let receivedData = content else {                 print("**** content is nil")                 return             }             let dataString = String(decoding: receivedData, as: UTF8.self)             print("**** received data = \(dataString)")             connection.cancel()         }     } } This solution is a modification of a Bonjour Listener demo app in a WWDC presentation. As I said in my original question, this is for listening for infrequent HTTP packets on an internal, secure network. I wouldn't dare use it as a fully open web-server. For my purposes, the information in the incoming HTTP command is all I need: it contains the new state of the WiFi switch that is on the local network. Regards, Michaela