Repeating the caller book is tedious and error-prone. In Kotlin, there is with block which is handy to access the receiver properties and methods without referring to it.
Calling mergeChanges on a managed object context will automatically refresh any managed objects that have changed. This ensures that your context always contains all the latest information. Note that you don’t have to call mergeChanges on a viewContext when you set its automaticallyMergesChangesFromParent property to true. In that case, Core Data will handle the merge on your behalf.
While you can fetch data from Core Data with @FetchRequest just fine, I tend to avoid it in my apps. The main reason for this is that I’ve always tried to separate my Core Data code from the rest of my application as much as possible.
This can be misleading. When one see parent store he can understand the parent persitent store of my context hierarchy. However, what is meant by parent store is either: the persistentStoreCoordinator or the parentContext So, if your context was setup with a parent context, changes are commited to his parent but no further. To save it to the persistent store, you’ll need to call save() on contexts all the way up in the hierarchy until you reach the persistent store.
When you save changes in a context, the changes are only committed “one store up.” If you save a child context, changes are pushed to its parent. Changes are not saved to the persistent store until the root context is saved. (A root managed object context is one whose parent context is nil.)
An object that meets the criteria specified by request (it is an instance of the entity specified by the request, and it matches the request’s predicate if there is one) and that has been inserted into a context but which is not yet saved to a persistent store, is retrieved if the fetch request is executed on that context.
Changes are not reflected in the context”. So what you’re seeing is normal. Batch updates work directly on the persistent store file instead of going through the managed object context, so the context doesn’t know about them. When you delete the objects by fetching and then deleting, you’re working through the context, so it knows about the changes you’re making (in fact it’s performing those changes for you).
mainContext would fetch all the way to the persistent store. Fetches and objectWithID: only go as many levels as they need to. It’s important to remember that when a context is created it’s a “snapshot” of the state of it’s parent. Subsequent changes to the parent will not be visible in the child unless the child is somehow invalidated
RootContext (private queue) - saves to persistent store MainContext (main queue) child of RootContext - use for UI (FRC) WorkerContext (private queue) - child of MainContext - use for updates & inserts
If the import happens through a batch operation, the save to the store doesn’t generate an NSManagedObjectContextDidSave notification, and the view misses these relevant updates. Alternatively, the background context may save changes to the store that don’t affect the current view—for example, inserting, modifying, or deleting Shape objects. These changes do generate context save events, so your view context processes them even though it doesn’t need to.
Also, the doc mention NSPersistentStoreRemoteChangeNotificationOptionKey
1 2 3 4 5 6 7 8 9 10 11
let remoteChangeKey = "NSPersistentStoreRemoteChangeNotificationOptionKey" description?.setOption(trueasNSNumber, forKey: remoteChangeKey)
Informally, this set is the set of all characters used to represent the decimal values 0 through 9. These characters include, for example, the decimal digits of the Indic scripts and Arabic.
So decimalDigits does not only contain digits, but also some scripts in other languages. For normal cases this should not be a problem. As How many decimal digits are there, anyways?
there are 610 valid characters in CharacterSet.decimalDigits. So be aware
1 2 3 4 5 6 7 8 9 10 11 12 13
let s = CharacterSet.decimalDigits
// U+0031 DIGIT ONE s.contains("1") // true as expected
// U+1D7D9 MATHEMATICAL DOUBLE-STRUCK DIGIT ONE s.contains("𝟙") // true!
// U+0967 DEVANAGARI DIGIT ONE s.contains("१") // true!
// U+1811 MONGOLIAN DIGIT ONE s.contains("᠑") // true!
Trimming
Another method is trimmingCharacters. Note that this removes only characters at the start and end of the string.
With Xcode 12.4, macOS 11.0 app. Every time we switch the system dark and light mode, the CPU goes up to 100%. Instruments show that there’s an increasing number of ButtonBehavior
We need to use a custom Binding to trigger onChange as onEditingChanged is only called when the user selects the textField, and onCommit is only called when return or done button on keyboard is tapped.
Start by defining your quick actions. You can use UIApplicationShortcutIcon(type:) for predefined icons, or use UIApplicationShortcutIcon(systemImageName:) for SFSymbol
Set dynamic screen quick actions at any point, but the sample sets them in the sceneWillResignActive(_:) function of the scene delegate. During the transition to a background state is a good time to update any dynamic quick actions, because the system executes this code before the user returns to the Home Screen.
@main structPastePaliOSApp: App{ @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @Environment(\.scenePhase) var scenePhase
var body: some Scene { WindowGroup { main } .onChange(of: scenePhase) { scenePhase in switch scenePhase { case .background: addDynamicQuickActions() case .active: QuickActionService.shared.perform() default: break } } }
If the app isn’t already loaded, it’s launched and passes details of the shortcut item in through the connectionOptions parameter of the scene(_:willConnectTo:options:) function in AppDelegate
If your app is already loaded, the system calls the windowScene(_:performActionFor:completionHandler:) function of your SceneDelegate
SwiftUI assumes any Equatable.== is a true equality check, so for POD views it compares each field directly instead (via reflection). For non-POD views it prefers the view’s == but falls back to its own field compare if no ==. EqView is a way to force the use of ==.
When it does the per-field comparison the same rules are applied recursively to each field (to choose direct comparison or == if defined). (POD = plain data, see Swift’s _isPOD() function.)
I use Codable structs in my apps for preferences, and bind them to SwiftUI views. If we add new properties to existing Codable, it can’t decode with old saved json as we require new properties. We can either do cutom decoding with container, but this can result in lots more code and mistakes if we have many properties inside our struct.
The quick workaround is to declare new properties as optional, and use a computed property to wrap that. The good news is Binding works with computed properties too, from the outside all looks like struct properties to SwiftUI
1 2 3 4 5 6
structPreference: Codable{ var _redacts: Bool? = false var redacts: Bool { get { _redacts ?? false } set { _redacts = newValue } }
If we place ScrollView inside HStack or VStack, it takes all remaining space. To fit ScrollView to its content, we need to get its content size and constrain ScrollView size.
Use a GeometryReader as Scrollview content background, and get the local frame
publicfunccontextMenu<MenuItems>(@ViewBuilder menuItems: () -> MenuItems) -> some ViewwhereMenuItems : View
In these ViewBuilder enabled places we can perform conditional logic to construct views. For example here in our SampleView, we have switch statement in body
structSampleView: View{ enumPosition{ case top, bottom, left, right }
let position: Position
var body: some View { switch position { case .top: Image(systemName: SFSymbol.person.rawValue) default: EmptyView() } }
var profile: some View { iftrue { returnImage(systemName: SFSymbol.person.rawValue) } } }
ViewBuilder applies to both property and function. If we want to have the same logic style as in body in our custom property or methods, we can annotate with ViewBuilder. This works like magic, SwiftUI can determine the types of our expression.
1 2 3 4 5 6 7 8 9 10 11
extensionSampleView{ @ViewBuilder funcprofile2(position: Position) -> some View { switch position { case .top: Image(systemName: SFSymbol.person.rawValue) default: EmptyView() } } }
Use ViewBuilder to construct View
We can use ViewBuiler as our parameter that constructs View. For example we can build an IfLet that construct View with optional check.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
publicstructIfLet<T, Content: View>: View{ let value: T? let content: (T) -> Content
I usually break down a big struct into smaller views and extensions. For example I have a ClipboardCell that has a lot of onReceive so I want to move these to another component.
but then when we want to use this, we get some View has no member onReceiveKeyboard as self after some Swift modifier becomes some View, unless we call onReceiveKeyboard first
1 2 3 4 5 6 7
structClipboardCell: View{ var body: some View { self .padding() .onReceiveKeyboard() } }
Use ViewModifier
The SwiftUI is to use ViewModifier where we can inject Binding and functions
Another way is to use an ObservableObject and encapsulate logic and state in there, and share this across views that want to consume this set of data, just like a ViewModel
Notice that sparkle:version="2.0" is CFBundleVersion which is your build number. You need to also specify sparkle:shortVersionString which is CFBundleShortVersionString your version number
<?xml version="1.0" encoding="utf-8"?> <rssversion="2.0"xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"xmlns:dc="http://purl.org/dc/elements/1.1/"> <channel> <title>Sparkle Test App Changelog</title> <link>http://sparkle-project.org/files/sparkletestcast.xml</link> <description>Most recent changes with links to updates.</description> <language>en</language> <item> <title>Version 2.0</title> <description> <![CDATA[ <ul> <li>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</li> <li>Suspendisse sed felis ac ante ultrices rhoncus. Etiam quis elit vel nibh placerat facilisis in id leo.</li> <li>Vestibulum nec tortor odio, nec malesuada libero. Cras vel convallis nunc.</li> <li>Suspendisse tristique massa eget velit consequat tincidunt. Praesent sodales hendrerit pretium.</li> </ul> ]]> </description> <pubDate>Sat, 26 Jul 2014 15:20:11 +0000</pubDate> <enclosureurl="https://sparkle-project.org/files/Sparkle%20Test%20App.zip"sparkle:version="2.0"length="107758"type="application/octet-stream"sparkle:dsaSignature="MCwCFCdoW13VBGJWIfIklKxQVyetgxE7AhQTVuY9uQT0KOV1UEk21epBsGZMPg==" /> </item> </channel> </rss>
finalclassAppDelegate: NSObject, NSApplicationDelegate{ funcapplicationDidFinishLaunching(_ notification: Notification) { let bundleId = Bundle.main.bundleIdentifier! // TODO: Make this more strict by only replacing at the end let mainBundleId = bundleId.replacingOccurrences(of: "-LaunchAtLoginHelper", with: "")
// Ensure the app is not already running guardNSRunningApplication.runningApplications(withBundleIdentifier: mainBundleId).isEmpty else { NSApp.terminate(nil) return }
let pathComponents = (Bundle.main.bundlePath asNSString).pathComponents let mainPath = NSString.path(withComponents: Array(pathComponents[0...(pathComponents.count - 5)])) NSWorkspace.shared.launchApplication(mainPath) NSApp.terminate(nil) } }
As far as I can tell, this bug only shows up if you: 1) have the navigation title displayMode of a destination view set to .large and 2) have added items to the navigation bar using the .navigationBarItems modifier.