Post

Replies

Boosts

Views

Activity

Reply to Dynamic height for TextEditor
This solution is adapted from responses (primarily Mojtaba Hosseini) to a couple of similar questions on StackOverflow and also from Alan Quatermain's well written blog titled "SwiftUI Bindings with CoreData". The cross platform appearance is not identical and the need to add the hacks is annoying, but it works. I use this solution for both macOS and iOS targets (with Core Data) and an @ObservedObject var. I've adapted this answer to work independently with an @State var. This builds and runs in both targets but some of the view characteristics are "out-of-sync". Essentially TextEditor and Text views must be drawn in the same way - must have identical view modifier characteristics, e.g. .font modifier, etc. The Text view "ghosts" the TextEditor and performs two roles: presents the placeholder text when the Optional<String> (@State private var objectDescription: String?) is nil. dynamically expands the height of the stacked views to accommodate multiple lines of text in the TextEditor. If anyone understands the mechanics behind what makes the "ghosting" technique work in the ZStack, please add an answer, because I'm still trying to figure that out. struct TextEditorView: View {    @State private var objectDescription: String?   var body: some View {         VStack(alignment: .leading) {             let placeholder = "enter detailed Description"             Text("Description")             ZStack(alignment: .topLeading) {                 TextEditor(text: Binding($objectDescription, replacingNilWith: ""))                     .frame(minHeight: 30, alignment: .leading)                     // following line is a hack to force TextEditor to appear                     //  similar to .textFieldStyle(RoundedBorderTextFieldStyle())...                     .cornerRadius(6.0)                     .foregroundColor(Color(.labelColor))                     .multilineTextAlignment(.leading)                 Text(objectDescription ?? placeholder)                     // following line is a hack to create an inset similar to the TextEditor inset...                     .padding(.leading, 4)                     .foregroundColor(Color(.placeholderTextColor))                     .opacity(objectDescription == nil ? 1 : 0)             }             .font(.body) // if you'd prefer the font to appear the same for both iOS and macOS         }     } } // Full credit for this extension to Binding to Alan Quatermain. public extension Binding where Value: Equatable {     init(_ source: Binding<Value?>, replacingNilWith nilProxy: Value) {         self.init(             get: { source.wrappedValue ?? nilProxy },             set: { newValue in                 if newValue == nilProxy {                     source.wrappedValue = nil                 }                 else {                     source.wrappedValue = newValue                 }         })     }
Jul ’20
Reply to How to add placeholder text to TextEditor in SwiftUI?
This solution is adapted from responses (primarily Mojtaba Hosseini) to a couple of similar questions on StackOverflow and also from Alan Quatermain's well written blog titled "SwiftUI Bindings with CoreData". I use this solution in a macOS target (with Core Data) and an @ObservedObject var. I've adapted this answer to work independently with an @State var. Please be aware that I have not attempted to build and run this with an iOS target yet, but it works in the SwiftUI canvas in Xcode 12 beta 1 and using live preview for the macOS target. Essentially TextEditor and Text views must be drawn in the same way - must have identical characteristics, e.g. .font modifier, etc. The Text view "ghosts" the TextEditor and performs two roles: presents the placeholder text when the Optional<String> (@State private var objectDescription: String?) is nil. dynamically expands the height of the stacked views to accommodate multiple lines of text in the TextEditor. If anyone understands the mechanics behind what makes the "ghosting" technique work in the ZStack, please add an answer, because I'm still trying to figure that out. Note that TextEditor seems to be a little unstable in Xcode 12 beta 1 under macOS 11 beta 2. I include the .frame modifier to ensure the TextEditor view presents itself as I expect it to - without this modifier it occasionally and inexplicably "disappears". public extension Binding where Value: Equatable {     init(_ source: Binding<Value?>, replacingNilWith nilProxy: Value) {         self.init(             get: { source.wrappedValue ?? nilProxy },             set: { newValue in                 if newValue == nilProxy {                     source.wrappedValue = nil                 }                 else {                     source.wrappedValue = newValue                 }         })     } } struct YourSwiftUIView: View { 		@State private var objectDescription: String? 		var body: some View { 				VStack(alignment: .leading) {             let placeholder = "enter detailed Description"             Text("Description")          	ZStack(alignment: .topLeading) {                 TextEditor(text: Binding($objectDescription, replacingNilWith: ""))                     .frame(minHeight: 30, alignment: .leading) 										 // following line is a hack to force TextEditor to appear 										 //	similar to .textFieldStyle(RoundedBorderTextFieldStyle())...                     .cornerRadius(6.0)                     .foregroundColor(Color(.labelColor))                     .multilineTextAlignment(.leading)                 Text(objectDescription ?? placeholder) 										 // following line is a hack to create an inset similar to the TextEditor inset...                     .padding(.leading, 5)                     .foregroundColor(Color(.placeholderTextColor))                     .opacity(objectDescription == nil ? 1 : 0)             }             .font(.body)         } 		} }
Jul ’20
Reply to Using Core Data with SwiftUI App Protocol
My two cents... My Core Data implementation for SwiftUI using the new App and Scene protocols and Window container! Credit to: mtsrodrigues for how to deal with change of Scene in his answer in this thread. KrakenDev's article on how to write a one line singleton My SwiftUI App... import SwiftUI @main struct YourApp: App {     @Environment(\.scenePhase) private var scenePhase     @StateObject private var persistentStore = PersistentStore.shared     var body: some Scene {         WindowGroup {             ContentView()                 .environment(\.managedObjectContext, persistentStore.context)         }         .onChange(of: scenePhase) { phase in             switch phase {             case .active:                 print("\(#function) REPORTS - App change of scenePhase to ACTIVE")             case .inactive:                 print("\(#function) REPORTS - App change of scenePhase to INACTIVE")             case .background:                 print("\(#function) REPORTS - App change of scenePhase to BACKGROUND")                 savePersistentStore()             @unknown default:                 fatalError("\(#function) REPORTS - fatal error in switch statement for .onChange modifier")             }         }     }     func savePersistentStore() {         persistentStore.save()     } } My "Core Data stack" - essentially Apple's template code embedded within a custom class that conforms to ObservableObject. import SwiftUI import CoreData class PersistentStore: ObservableObject {     var context: NSManagedObjectContext { persistentContainer.viewContext }      		// One line singleton     static let shared = PersistentStore() 		// Mark the class private so that it is only accessible through the singleton `shared` static property     private init() {}     private let persistentStoreName: String = "YourPersistentStoreName"     // MARK: - Core Data stack     lazy var persistentContainer: NSPersistentContainer = {         let container = NSPersistentContainer(name: persistentStoreName) 				// OR - Include the following line for use with CloudKit - NSPersistentCloudKitContainer         // let container = NSPersistentCloudKitContainer(name: persistentStoreName)         // Enable history tracking         // (to facilitate previous NSPersistentCloudKitContainer's to load as NSPersistentContainer's)         // (not required when only using NSPersistentCloudKitContainer)         guard let persistentStoreDescriptions = container.persistentStoreDescriptions.first else {             fatalError("\(#function): Failed to retrieve a persistent store description.")         }         persistentStoreDescriptions.setOption(true as NSNumber,                                               forKey: NSPersistentHistoryTrackingKey)         persistentStoreDescriptions.setOption(true as NSNumber,                                               forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)         container.loadPersistentStores(completionHandler: { (storeDescription, error) in             if let error = error {                 // Replace this implementation with code to handle the error appropriately.                 fatalError("Unresolved error \(error)")             }         }) 				// Include the following line for use with CloudKit - NSPersistentCloudKitContainer         container.viewContext.automaticallyMergesChangesFromParent = true 				// Include the following line for use with CloudKit and to set your merge policy, for example... 				container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy          return container     }()     // MARK: - Core Data Saving and "other future" support (such as undo)     func save() {         let context = persistentContainer.viewContext         if !context.commitEditing() {             NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing before saving")         }         if context.hasChanges {             do {                 try context.save()             } catch {                 // Customize this code block to include application-specific recovery steps.                 let nserror = error as NSError                 NSApplication.shared.presentError(nserror)             }         }     } } To use the context within a Preview... struct YourSwiftUIStruct_Previews: PreviewProvider {     static var previews: some View {         let context = PersistentStore.shared.context         return YourSwiftUIStruct()             .environment(\.managedObjectContext, context)     } }
Jul ’20
Reply to Aspiring Developer
I recommend the first three as mandatory... Paul Hegarty's Stanford course is excellent CS193p Developing Apps for iOS - cs193p.sites.stanford.edu The Apple Developer SwiftUI tutorial is the best learning tool Apple has ever released - developer.apple.com/tutorials/swiftui/ Watch ALL the WWDC videos - I try one a day - start with Swift, SwiftUI and Combine Then refer to these websites for good reliable well written articles: Others have recommended Swift by Sundell, and so do I - swiftbysundell.com NSHipster for many excellent articles on Swift - nshipster.com For some more in-depth articles on certain aspects of SwiftUI, try: The SwiftUI-Lab - swiftui-lab.com Swift with Majid - swiftwithmajid.com
Jul ’20
Reply to New toolbar appearance in macOS 11
So for any passers-by that are interested in how I eventually achieved this... (Credit to mtsrodrigues for how to deal with change of Scene in his answer in this thread... https://developer.apple.com/forums/thread/650876) My Core Data implementation for SwiftUI and the new App and Scene protocols and Window container! My SwiftUI App... struct MyApp: App {     @Environment(\.scenePhase) private var scenePhase     @StateObject private var persistentStore = PersistentStore.shared     var body: some Scene {         WindowGroup {             ContentView()                 .environment(\.managedObjectContext, persistentStore.context)         }         .onChange(of: scenePhase) { phase in             switch phase {             case .active:                 print("\(#function) REPORTS - App change of scenePhase to ACTIVE")             case .inactive:                 print("\(#function) REPORTS - App change of scenePhase to INACTIVE")             case .background:                 print("\(#function) REPORTS - App change of scenePhase to BACKGROUND")                 savePersistentStore()             @unknown default:                 fatalError("\(#function) REPORTS - fatal error in switch statement for .onChange modifier")             }         }     }     func savePersistentStore() {         persistentStore.save()     } } and my custom class for PersistentStore (noting the one line singleton as documented by KrakenDev)... import SwiftUI import CoreData class PersistentStore: ObservableObject {     var context: NSManagedObjectContext { persistentContainer.viewContext } 		 		// One line singleton     static let shared = PersistentStore() 		 		// Mark the class private so that it is only accessible through the singleton `shared` static property     private init() {}     private let persistentStoreName: String = "YourPersistentStoreName"     // MARK: - Core Data stack     lazy var persistentContainer: NSPersistentContainer = {         let container = NSPersistentContainer(name: persistentStoreName) 				         // Enable history tracking         // (to facilitate previous NSPersistentCloudKitContainer to load as NSPersistentContainer)         // (not required when using NSPersistentCloudKitContainer)         guard let persistentStoreDescriptions = container.persistentStoreDescriptions.first else {             fatalError("\(#function): Failed to retrieve a persistent store description.")         }         persistentStoreDescriptions.setOption(true as NSNumber,                                               forKey: NSPersistentHistoryTrackingKey)         persistentStoreDescriptions.setOption(true as NSNumber,                                               forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)         container.loadPersistentStores(completionHandler: { (storeDescription, error) in             if let error = error {                 fatalError("Unresolved error \(error)")             }         })         container.viewContext.automaticallyMergesChangesFromParent = true 				// Add the following line to set your merge policy... 				container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy         return container     }()         // MARK: - Core Data Saving and Undo support     func save() {         let context = persistentContainer.viewContext         if !context.commitEditing() {             NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing before saving")         }         if context.hasChanges {             do {                 try context.save()             } catch {                 let nserror = error as NSError                 NSApplication.shared.presentError(nserror)             }         }     } }
Jul ’20
Reply to How do I create a simple toggle button in the macOS toolbar with SwiftUI
I'm currently using this code within the .toolbar modifier for a view that is embedded within a NavigationView...         .toolbar {             ToolbarItem(placement: .primaryAction) {                 Button(action: appData.isEditing.toggle()) {                     Label(appData.isEditing ? "Done" : "Edit",                           systemImage: appData.isEditing ? "pencil.circle.fill" : "pencil.circle")                 }                 .disabled(&lt;< insert your own logic here &gt;>)                 .help("Edit the selected record")             }         } and this is my AppData class that conforms to ObservableObject that I use to store the value for isEditing... final class AppData: ObservableObject {     @Published var isEditing: Bool = false } Alternatively you could use the @State property wrapper within your View struct that contains the NavigationView... struct yourView: View { &#9;&#9;@State private var isEditing = false &#9;&#9;... } I haven't attempted to use the Toggle View before, but to answer this question I just used it successfully in a macOS target I'm writing in SwiftUI...                 Toggle(isOn: $appData.isEditing) {                     Label(appData.isEditing ? "Done" : "Edit",                           systemImage: appData.isEditing ? "pencil.circle.fill" : "pencil.circle")                 }                 .disabled(&lt;< insert your own logic here &gt;>)                 .help("Edit the selected record") The benefit of using Toggle over Button, is that when the toggle value for isOn = true, the Toggle has a slightly darker background to demonstrate the selected state. This would have to be added manually to Button to achieve the same effect. I'll be using Toggle.
Jul ’20
Reply to SwiftUI Toolbar
I have it working in macOS 11 beta. I admit that it took some time for me to find the correct location to apply the .toolbar modifier though. It needs to be applied as a modifier to a View that is contained within a NavigationView.
Jul ’20
Reply to Using Core Data with SwiftUI App Protocol
@micho2, to achieve CloudKit integration you should be able to change the persistentContainer property like so...     var persistentContainer: NSPersistentContainer = {        &#9;&#9;&#9;&#9;let container = NSPersistentCloudKitContainer(name: "SampleApp")     &#9;&#9;&#9;&#9;&#9;&#9; // change this line          container.loadPersistentStores(completionHandler: { (storeDescription, error) in             if let error = error as NSError? {                &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;fatalError("Unresolved error \(error), \(error.userInfo)")             }         }) &#9;&#9;&#9;&#9;container.viewContext.automaticallyMergesChangesFromParent = true&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; // add this line         return container     }() although I've not tested this yet!
Jun ’20
Reply to Connecting SwiftUI app to CoreData
I found a much better implementation of Core Data with new App and Scene protocols that provides access to change of app state through the .onChange(of: scenePhase) modifier. I'd recommend using that. Search Apple Developer Forums for "Using Core Data with SwiftUI App Protocol" under tag wwdc20-10041 Here is the thread link... look to the accepted answer... https://developer.apple.com/forums/thread/650876
Jun ’20
Reply to Connecting SwiftUI app to CoreData
Not so much an answer, more a report of where I've got to with this so far... in the hope that it may prompt further discussion and frankly help me find a stable solution. I've successfully hooked up a Core Data persistent store in the following manner... @main struct CoreDataApp: App {     @StateObject var persistentStore = PersistentStore.shared     var body: some Scene {         WindowGroup {             ContentView(store: persistentStore)                 .environment(\.managedObjectContext, persistentStore.context)         }     } } where the persistent store is... import Cocoa import CoreData class PersistentStore: ObservableObject {     var context: NSManagedObjectContext { persistentContainer.viewContext } &#9;&#9;static let shared = PersistentStore()     private init() {}     private let persistentStoreName: String = "TheNameOfMySQLiteStore"     // MARK: - Core Data stack     lazy var persistentContainer: NSPersistentContainer = { &#9;&#9;// ... per Apple's Core Data stack persistent container template ... &#9;&#9;} }     (Acknowledgement to KrakenDev for the one line singleton.) and then I carry the reference to the PersistentStore into the ContentView as an @ObservedObject... struct ContentView: View {     @ObservedObject var store: PersistentStore &#9;&#9;...etc. } The most important part of this process IMHO is the injection of the NSManagedObjectContext (viewContext) from a single instance of the NSPersistentContainer into each and any view that requires access to the context. NSManagedObjects by default adopt the ObservableObject protocol so I don't need to apply this to each Core Data subclassed NSManagedObject entities. Although for the use in List, it is worth adopting the Identifiable protocol. Any change or addition or deletion to an NSManagedObject within the context therefore remains in the context, until such time that the App closes. If at any stage I call "do try context.save()", then any create, update or delete will persist. My advice - don't focus on @ObservableObject and @ObservedObject - focus on injecting an NSManagedObjectContext into the views where you need to create, read, update or delete. So exactly what happens when the App closes is not clear to me and is possibly why Apple still recommend the use of AppDelegate in it's SwiftUI Core Data template, because it provides API such as...     func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {} ... which helps us manage Core Data related activity. I'm finding it relatively easy to implement CRUD processes. I'm using @FetchRequest to grab data from the     @Environment(\.managedObjectContext) var context that I include in each View struct as required, and I've begun to experiment with dynamic fetch requests that take a generic NSManagedObject. What I'm finding extremely difficult is the process of maintaining stable views using the new App &amp; Scene protocols and WindowGroup container. I'm putting that down to two things... my lack of knowledge/understanding; if the new API were ready for Core Data, then Xcode would provide a template to use these with Core Data. Which leads me to guess that AppDelegate instantiated apps with Core Data may be the safest path for the immediate future. Not that I'm heading down that path yet!
Jun ’20
Reply to MapKit is logging messages..
Using MBP, macOS 10.15.5, Xcode 11.5 (11E608c).Writing SwiftUI for macOS 10.15 deployment target.SwiftUI, using MapKit with NSViewRepresentable.Also seeing the same error message.(Incidentally also see the same error messages in the macOS app in SwiftUI tutorial Creating a macOS app.Console error... at breakpoint...Calling IOPPFGetProperty thermally_optimized_maps!Then console dumps a huge number of these messages...=== AttributeGraph: cycle detected through attribute xx ===[SwiftUI] NSHostingView is being laid out reentrantly while rendering its SwiftUI content. This is not supported and the current layout pass will be skipped.Issue Navigator under Runtime (only this 1 issue)...runtime: SwiftUI: NSHostingView is being laid out reentrantly while rendering its SwiftUI content. This is not supported and the current layout pass will be skipped.Copied from the Debug Navigator...Thread 7 Queue : com.apple.CoreAnalytics::Client (serial)#0 0x00007fff6f3da1ac in __cxa_begin_catch ()#1 0x00007fff4f962dbf in FrameworkConfiguration::init() ()#2 0x00007fff4f962a98 in FrameworkConfiguration::create(applesauce::xpc::object) ()#3 0x00007fff4f962742 in CoreAnalytics::Client::handleConfigurationChange_sync(applesauce::xpc::dict const&amp;) ()#4 0x00007fff4f962542 in CoreAnalytics::Client::handleServerMessage_sync(applesauce::xpc::dict) ()#5 0x00007fff4f9748cf in ___ZZN13CoreAnalytics6Client4initEvENK3$_1clEv_block_invoke ()#6 0x00007fff722ee2bc in _xpc_connection_call_event_handler ()#7 0x00007fff722ed1cb in _xpc_connection_mach_event ()#8 0x00000001004f28c6 in _dispatch_client_callout4 ()#9 0x000000010050fbcc in _dispatch_mach_msg_invoke ()#10 0x00000001004f9be6 in _dispatch_lane_serial_drain ()#11 0x0000000100510f6c in _dispatch_mach_invoke ()#12 0x00000001004f9be6 in _dispatch_lane_serial_drain ()#13 0x00000001004fab90 in _dispatch_lane_invoke ()#14 0x0000000100507fe0 in _dispatch_workloop_worker_thread ()#15 0x0000000100580361 in _pthread_wqthread ()#16 0x000000010057f49b in start_wqthread ()
Jun ’20
Reply to OutlineView in SwiftUI
I'm working through this same exercise at the moment...So far I've found this one of the better articles to read - short and concise - https://dev.to/hugh_jeremy/adding-an-nstableview-to-a-swiftui-view-212pSeems we need to create a wrapper with NSViewControllerRepresentableThis is a work-in-progress for me...
May ’20
Reply to NSSecureUnarchiveFromData is now necessary for transformable attributes but does not work with Core Data CloudKit?
I'm experiencing the same or similar problem.I suspect, but I am not certain, that it is a bug and I'll try to explain my reasons why... but first...My scenario:Xcode 11.3 project developing for iOS 13.x and beginning to implement NSPersistentCloudKitContainer.I have `Transformable` type attributes for two Entities in my data model (for some time now). Incidentally both these attributes are used to store an optional value for Measurement&lt;UnitMass&gt;, which I understand from my reseach, conforms to `Encodable` and is therefore able to "just work" with a Core Data attribute of type `Transformable`. Others have written more on the capabilities of the Foundation `Measurement` class, using more accurate language than me, in this and other forums, so I'll leave this part of my description short.As I am still in development with my Xcode project, I regularly switch between NSPersistentContainer and NSPersistentCloudKitContainer for testing purposes. This requires regularly resetting the development environment using the CloudKit Dashboard.To ensure my app loads and runs regardless of which type of persistent container I am currently using, I also enable `NSPersistentHistoryTrackingKey = true`. This ensures that records persisted while my project's "container" is of type NSPersistentContainer are also available while my project's "container" is of type NSPersistentCloudKitContainer.I recently updated my .xcdatamodel to add new attributes and new relationships to three entities. Two fo these entities contained the aforementioned attributes used to store optional values for Measurement&lt;UnitMass&gt;. Of these two, both contained renamed attributes and implemented the Renaming ID offered by lightweight migration. I reiterate here for clarity that these renamed attributes were not those used to store optional values for Measurement&lt;UnitMass&gt;, but did belong to the same entity.It seems to me that I was experiencing compounding problems...Intially my errors were related to your first error code: "One or more models in this application are using transformable properties with transformer names that are either unset, or set to NSKeyedUnarchiveFromDataTransformerName..." etc.I admit here that I'm not proficient in the use of value transformers or the use of the `Transformable` attribute type... So I hacked away for some time, attempting to apply NSSecureUnarchiveFromData in the Transformer setting in the Attribute Inspector for each of the two attributes that were of type `Transformable`. This didn't seem to have any effect, so I began testing this on other "devices".To make matters more confusing, I was experiencing different errors depending on what testing platform I was using and when I last updated that app's model - so in fact I was experiencing three different errors - Simulator with iPhone 8 Plus running iOS 13.3, Simulator with iPad Pro 12" 3rd generation running iOS 13.3, on device (iPhone 8 Plus) running iOS 13.3. I eventually deleted the instances of the app on the Simulators, but was extremely reluctant to do so on device due to the large amount of test data installed. Yes I am aware that I could download that and reinstall, but honestly I wanted to try to "fix" this problem.One of the errors included commentary on the inability of Core Data to rename attributes that are related to CloudKit records.So my three errors in fact this led me in part to my solution... roll back the data model. I reverted back to an earlier stable data model file that I knew did not cause any errors and ran it succesfully on the previously mentioned devices.Going forward, I didn't attempt to change/update the attribute names as I had previously... for my next change in the data model I added the "new" attributes, then in the subsequent change in the data model I deleted the "old" attribute. Each time I ran the app on all three devices and received no errors. (Note: I accepted some minor data loss in records where old relationships were nullified on deletion.)I reiterate here that all this was completed in a development environment in CloudKit.I regret not recording the exact details of the three different types of errors... but they all began with "One or more models in this application are using transformable properties with transformer names that are either unset, or set to NSKeyedUnarchiveFromDataTransformerName." etc.Hope this essay is of some help.
Feb ’20