Several years ago, I modified an [Apple Obj-c Time Zone] example (illustrated image below) to enrich my non-existent knowledge with Time Zones. The modified application illustrates the current [Time Zone Date and Time] for my current MDT location, and will illustrate another available [Time Zone Location] from a popup button.
So, I decided to see whether I could convert the Obj-c code to Swift code. The converted Swift code built properly, but the application will repeatedly crash when I engage the Swift [clockAndCalendar binding].
The Swift code application generates the following error message:
I understand the error message listed above to be: "EXC_BAD_ACCESS" error message (code=1, address=0x0) occurs when the application tries to access memory, which the application cannot access, or does not exist.
Examining the error shows the MDT’s as the first encountered location:
When I examine the additional [information] button, Xcode generates the following message, stating a [Foundation] incompatibility exists. Again, I wonder whether this issue can be resolved.
So, for the moment, I believe I cannot produce an equivalent Swift file just yet without more knowledge, where I unwittingly, and most likely applied the Swift [clockAndCalendar binding] code incorrectly … ?
I am curious about what I should do next, and how I might properly approach this error message ... ?
NSTimeZone (Obj-c) and TimeZone (Swift) produce different [Time Zone] results, as noted in the associated [Print Statements].
Your possible suggestions for me to explore would be very welcome … :]
Again, I must thank you for your time with this issue.
Best regards,
jim_k
// The modified Obj-c View:
// Obj-c [setupCityClock] code:
- (void)setupCityClock:(NSView *)containerView timeZone:(NSTimeZone *)timeZone {
NSDatePickerElementFlags flags = 0;
flags |= NSDatePickerElementFlagHourMinute;
flags |= NSDatePickerElementFlagHourMinuteSecond;
flags |= NSDatePickerElementFlagTimeZone;
flags |= NSDatePickerElementFlagYearMonth;
flags |= NSDatePickerElementFlagYearMonthDay;
flags |= NSDatePickerElementFlagEra;
// Setup the city block and bind its value to a single NSDate property.
NSDatePicker *theIdentifiedTimeZoneLocationCalendarAndClock = [[NSDatePicker alloc] initWithFrame:containerView.bounds];
// Draw the [Focus Ring] around the [Date Picker]
theIdentifiedTimeZoneLocationCalendarAndClock.focusRingType = NO;
// Draw the [datePicker Background colour] which makes the [background colour darker]
theIdentifiedTimeZoneLocationCalendarAndClock.drawsBackground = YES;
// Set the [datePicker Background Colour] to [TEXT BACKGROUND COLOUR]
theIdentifiedTimeZoneLocationCalendarAndClock.backgroundColor = NSColor.blackColor;
// Set the [datePicker Text Colour] to [WHITE]
theIdentifiedTimeZoneLocationCalendarAndClock.textColor = NSColor.controlTextColor;
// If the function is set to [YES] the [BORDER] is not [DRAWN] properly.
// Setting the function to [NO] will not draw the [BORDER]
theIdentifiedTimeZoneLocationCalendarAndClock.bordered = NO;
// Set the [DATE PICKER STYLE]
theIdentifiedTimeZoneLocationCalendarAndClock.datePickerStyle = NSDatePickerStyleClockAndCalendar;
// Set the [DATE PICKER ELEMENTS]
theIdentifiedTimeZoneLocationCalendarAndClock.datePickerElements = flags;
// Set the [TIME ZONE]
theIdentifiedTimeZoneLocationCalendarAndClock.timeZone = timeZone;
// Check the results:
NSLog(@"The [CURRENT TIME ZONE FOR (setupCityClock)] :: %@", timeZone);
// Prints: - [America/Edmonton (MDT) offset -21600 (Daylight)]
// Prints: - [Europe/London (GMT+1) offset 3600 (Daylight)]
NSLog(@"The [CURRENT TIME ZONE FOR (setupCityClock) again] :: %@", theIdentifiedTimeZoneLocationCalendarAndClock.timeZone);
// Prints: - [America/Edmonton (MDT) offset -21600 (Daylight)]
// Prints: - [Europe/London (GMT+1) offset 3600 (Daylight)]
// Bind the [TIME ZONES] to the [CURRENT TIME]
[theIdentifiedTimeZoneLocationCalendarAndClock bind:NSValueBinding toObject:self withKeyPath:@"currentTime" options:nil];
// Add the [CITY CLOCK] to the [CONTAINER VIEW]
[containerView addSubview:theIdentifiedTimeZoneLocationCalendarAndClock];
} // End of [- (void)setupCityClock:]
// The modified Swift View [Without the [clockAndCalendar binding] engaged]: // NOTE: The clock’s static time differential is correct with each popup button selection.
// Swift [setupCityClock] code:
func setupCityClock(containerView: NSView, timeZone: TimeZone) {
var flags = NSDatePicker.ElementFlags()
flags.insert(.hourMinute)
flags.insert(.hourMinuteSecond)
flags.insert(.timeZone)
flags.insert(.yearMonth)
flags.insert(.yearMonthDay)
flags.insert(.era)
// Setup the city clock and bind its value to a single NSDate property.
let theIdentifiedTimeZoneLocationCalendarAndClock = NSDatePicker(frame: containerView.bounds)
// Draw the [Focus Ring] around the [Date Picker]
theIdentifiedTimeZoneLocationCalendarAndClock.focusRingType = .none
// Draw the [datePicker Background colour] which makes the [background colour darker]
theIdentifiedTimeZoneLocationCalendarAndClock.drawsBackground = true
// Set the [datePicker Background Colour] to [TEXT BACKGROUND COLOUR]
theIdentifiedTimeZoneLocationCalendarAndClock.backgroundColor = NSColor.black
// Set the [datePicker Text Colour] to [WHITE]
theIdentifiedTimeZoneLocationCalendarAndClock.textColor = NSColor.controlTextColor
// If the function is set to [YES] the [BORDER] is not [DRAWN] properly.
// Setting the function to [NO] will [NOT] draw the [BORDER]
theIdentifiedTimeZoneLocationCalendarAndClock.isBordered = false
// Set the [DATE PICKER STYLE]
theIdentifiedTimeZoneLocationCalendarAndClock.datePickerStyle = .clockAndCalendar
// Set the [DATE PICKER ELEMENTS]
theIdentifiedTimeZoneLocationCalendarAndClock.datePickerElements = flags
// Set the [TIME ZONE]
theIdentifiedTimeZoneLocationCalendarAndClock.timeZone = timeZone
print("The [CURRENT TIME ZONE FOR (setupCityClock)] :: \(timeZone)")
// Prints: - [America/Edmonton (fixed (equal to current))]
// Prints: - [Europe/London (fixed)]
// Bind the [TIME ZONES] to the [CURRENT TIME] <=== (The error occurs here)
theIdentifiedTimeZoneLocationCalendarAndClock.bind(.value, to: self, withKeyPath: "currentTime", options: nil)
// Add the [CITY CLOCK] to the [CONTAINER VIEW]
containerView.addSubview(theIdentifiedTimeZoneLocationCalendarAndClock)
} // End of [func setupCityClock(containerView: NSView, timeZone: TimeZone)]
After scouring the internet to see whether I could discover a solution with better key words, such as [Binding and NSView], I noticed a quote posted more than five years ago, stating the following:
Since KVC and KVO are built on the Objective-C runtime, and since Cocoa Bindings is built on top of KVC and KVO, any properties you want to use Cocoa Bindings with need to be exposed to Objective-C. At the bare minimum, that means adding @objc to the declaration.
So, my original [Date] property was set to: var currentTime: Date?
I changed the [Date] property to: @objc var currentTime: Date?
The application did not crash, but did not show the [Clock and the Calendar] updating.
Further reading stated: However, if the property can be changed at runtime, there's an additional hurdle you need to jump through; you need to make sure that the KVO notifications will fire whenever the property's setter is called. Apple's implementation of KVO will use Objective-C magic to automatically add the needed notifications to the setter, but since Swift property accesses aren't guaranteed to go through the Objective-C runtime, you need to add the dynamic keyword for this to work reliably.
I changed the [Date] property to: @objc dynamic var currentTime: Date?
The application did not crash, and the [Clock and the Calendar] updated properly.
Perfect, I now have that knowledge ... :]
Again, thank you for your time ...
Best regards,
jim_k
London:
Sydney: