Is there a way to understand what is causing a crash involving `_NSTouchBarFinderObservation`?
Turns out the culprit was Firebase Analytics.
The solution to prevent this crash was to add the FirebaseAppDelegateProxyEnabled
to the info plist and give it a Boolean
value of NO
. Seems like SwiftUI, macOS and Firebase Analytics don't play nicely together.
Understand DispatchTime on M1 machines
The difference between Intel and ARM code is precision.
With Intel code, DispatchTime
internally works with nanoseconds. With ARM code, it works with nanoseconds * 3 / 125 (plus some integer rounding). The same applies to DispatchQueue.SchedulerTimeType
.
DispatchTimeInterval
and DispatchQueue.SchedulerTimeType.Stride
internally use nanoseconds on both platforms.
So the ARM code uses lower precision for calculations but full precision when comparing distances. In addition, precision is lost when converting from nanoseconds to the internal unit.
The exact formula for the DispatchTime
conversions are (executed as integer operations):
rawValue = (nanoseconds * 3 + 124) / 125
nanoseconds = rawValue * 125 / 3
As an example, let's take this code:
let time1 = DispatchQueue.SchedulerTimeType(.init(uptimeNanoseconds: 10000))
let time2 = DispatchQueue.SchedulerTimeType(.init(uptimeNanoseconds: 10431))
XCTAssertEqual(time1.distance(to: time2), .nanoseconds(431))
It results in the calculation:
(10000 * 3 + 124) / 125 -> 240
(10431 * 3 + 124) / 125 -> 251
251 - 240 -> 11
11 * 125 / 3 -> 458
The resulting comparison between 458 and 431 then fails.
So the main fix would be to allow for small differences (I haven't verified if 42 is the maximum difference):
XCTAssertEqual(time1.distance(to: time2), .nanoseconds(431), accuracy: .nanoseconds(42))
XCTAssertEqual(time2.distance(to: time1), .nanoseconds(-431), accuracy: .nanoseconds(42))
And there are more surprises: Other than with Intel code, distantFuture
and notSoDistantFuture
are equal with ARM code. It has probably been implemented like so to protect from an overflow when multiplying with 3. (The actual calculation would be: 0xFFFFFFFFFFFFFFFF * 3). And the conversion from the internal unit to nanoseconds would result in 0xFFFFFFFFFFFFFFFF * 125 / 3, a value to big to be represented with 64 bits.
Furthermore I think that you are relying on implementation specific behavior when calculating the distance between time stamps at or close to 0 and time stamps at or close to distant future. The tests rely on the fact the distant future internally uses 0xFFFFFFFFFFFFFFFF and that the unsigned subtraction wraps around and produces a result as if the internal value was -1.
How can I prevent this crash when updating the Touch Bar's escape key?
Sorry not an answer, but too long for a comment.
I built a debug version for our users that experienced crashes, that swizzles the _updateEscapeKeyItem method, in an attempt to pinpoint the crash origin, which isn't visible due to the bottom of the stack being blown away by the infinite recursion.
My guess is they changed the 10.13 implementation radically (they mentioned something about "KVO running amok"), and might not look into 10.12 crashes unless they have a solid clue.
This is the quick and dirty code in you can drop in (mostly borrowed from How to swizzle a method of a private class), that stops the infinite recursion. The resulting crash reports might be more useful.
@interface NSObject (swizzle_touchbar_stuff)
@end
static NSInteger updateEscapeKeyItem = 0;
@implementation NSObject (swizzle_touchbar_stuff)
+ (void)load
{
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
Method original, swizzled;
original = class_getInstanceMethod(objc_getClass("NSApplicationFunctionRowController"), NSSelectorFromString(@"_updateEscapeKeyItem"));
swizzled = class_getInstanceMethod(self, @selector(sparkle_updateEscapeKeyItem));
method_exchangeImplementations(original, swizzled);
});
}
- (void)sparkle_updateEscapeKeyItem
{
updateEscapeKeyItem++;
NSLog(@"sparkle_updateEscapeKeyItem %ld", updateEscapeKeyItem);
assert(updateEscapeKeyItem < 10);
[(NSObject *)self sparkle_updateEscapeKeyItem];
updateEscapeKeyItem--;
}
@end
Allow user customization of Touch Bar in macCatalyst
In my Swift Catalyst app (macOS 10.15.3, Xcode 11.3.1), I am able to call
NSTouchBar.isAutomaticCustomizeTouchBarMenuItemEnabled = true
in my override func makeTouchBar()
method, and the "Customize Touch Bar..." menu item appears in the "View" menu tab. For the missing labels, the following works:
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == yourIdentifier {
let item = NSPickerTouchBarItem(...)
item.customizationLabel = "View Segmented Control"
return item
}
return nil
}
Related Topics
iOS 11 PDFkit Not Updating Annotation Position
Why Do We Need to Explicitly Cast the Optional to Any
Remove Programmatically Added Uiimageview
How to Import Modules Without an Xcode Project in Swift
How Does Let X Where X.Hassuffix("Pepper") Work
Scenekit Physics Add Velocity in Local Space
Hide Status Bar in Launch Screen
How to Use Key-Value Observing with Smart Keypaths in Swift 4
Xcode 8 Shell Script Invocation Error
Converting Docx Files to Text in Swift
Appending Text to Nstextview in Swift 3
Syncconfiguration Deprecated, What Is the Proper Use of Syncuser.Configuration()
Rename Default Rendered Headings Like "Example" in Swift Markup Language