Skip to content

Commit 91abe2a

Browse files
authored
Merge pull request swiftlang#2154 from millenomi/dateintervalformatter-nscoding
Parity: NSCoding: DateIntervalFormatter
2 parents 8f05bbb + 0758cc4 commit 91abe2a

12 files changed

+349
-14
lines changed

CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c

+74-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ CF_INLINE void __CFReleaseIfNotNull(CFTypeRef object) {
3232
}
3333
}
3434

35+
CF_INLINE CFTypeRef __CFRetainIfNotNull(CFTypeRef object) {
36+
if (object) {
37+
CFRetain(object);
38+
}
39+
40+
return object;
41+
}
42+
3543
struct __CFDateIntervalFormatter {
3644
CFRuntimeBase _base;
3745
CFLocaleRef _locale;
@@ -184,7 +192,7 @@ CFDateIntervalFormatterRef CFDateIntervalFormatterCreate(CFAllocatorRef allocato
184192
__CFGenericValidateType(allocator, CFAllocatorGetTypeID());
185193
memory = (struct __CFDateIntervalFormatter *)_CFRuntimeCreateInstance(allocator, _kCFRuntimeIDCFDateIntervalFormatter, size, NULL);
186194
if (!memory) {
187-
return NULL;
195+
return (CFDateIntervalFormatterRef _Nonnull)NULL;
188196
}
189197

190198
switch (dateStyle) {
@@ -210,6 +218,9 @@ CFDateIntervalFormatterRef CFDateIntervalFormatterCreate(CFAllocatorRef allocato
210218
break;
211219
}
212220

221+
memory->_dateStyle = dateStyle;
222+
memory->_timeStyle = timeStyle;
223+
213224
memory->_locale = locale ? CFRetain(locale) : NULL;
214225

215226
memory->_calendar = NULL;
@@ -225,6 +236,67 @@ CFDateIntervalFormatterRef CFDateIntervalFormatterCreate(CFAllocatorRef allocato
225236
return (CFDateIntervalFormatterRef)memory;
226237
}
227238

239+
void _CFDateIntervalFormatterInitializeFromCoderValues(CFDateIntervalFormatterRef formatter,
240+
int64_t dateStyle,
241+
int64_t timeStyle,
242+
CFStringRef _Nullable dateTemplate,
243+
CFStringRef _Nullable dateTemplateFromStyles,
244+
bool modified,
245+
bool useTemplate,
246+
CFLocaleRef _Nullable locale,
247+
CFCalendarRef _Nullable calendar,
248+
CFTimeZoneRef _Nullable timeZone) {
249+
LOCK();
250+
formatter->_dateStyle = dateStyle;
251+
formatter->_timeStyle = timeStyle;
252+
253+
#define __CFSetObjectField(field, value) \
254+
{ \
255+
__auto_type _value = value; \
256+
if (field != _value) { \
257+
__CFReleaseIfNotNull(field); \
258+
field = (__typeof(_value))__CFRetainIfNotNull(_value); \
259+
} \
260+
}
261+
262+
__CFSetObjectField(formatter->_dateTemplate, dateTemplate);
263+
__CFSetObjectField(formatter->_dateTemplateFromStyles, dateTemplateFromStyles);
264+
265+
formatter->_modified = modified;
266+
formatter->_useTemplate = useTemplate;
267+
268+
__CFSetObjectField(formatter->_locale, locale);
269+
__CFSetObjectField(formatter->_calendar, calendar);
270+
__CFSetObjectField(formatter->_timeZone, timeZone);
271+
272+
UNLOCK();
273+
}
274+
275+
void _CFDateIntervalFormatterCopyCoderValues(CFDateIntervalFormatterRef formatter,
276+
int64_t *dateStyle,
277+
int64_t *timeStyle,
278+
CFStringRef _Nullable *dateTemplate,
279+
CFStringRef _Nullable *dateTemplateFromStyles,
280+
bool *modified,
281+
bool *useTemplate,
282+
CFLocaleRef _Nullable *locale,
283+
CFCalendarRef _Nullable *calendar,
284+
CFTimeZoneRef _Nullable *timeZone) {
285+
LOCK();
286+
287+
*dateStyle = formatter->_dateStyle;
288+
*timeStyle = formatter->_timeStyle;
289+
*dateTemplate = __CFRetainIfNotNull(formatter->_dateTemplate);
290+
*dateTemplateFromStyles = __CFRetainIfNotNull(formatter->_dateTemplateFromStyles);
291+
*modified = formatter->_modified;
292+
*useTemplate = formatter->_useTemplate;
293+
*locale = __CFRetainIfNotNull(formatter->_locale);
294+
*calendar = (CFCalendarRef)__CFRetainIfNotNull(formatter->_calendar);
295+
*timeZone = __CFRetainIfNotNull(formatter->_timeZone);
296+
297+
UNLOCK();
298+
}
299+
228300
CFDateIntervalFormatterRef CFDateIntervalFormatterCreateCopy(CFAllocatorRef _Nullable allocator, CFDateIntervalFormatterRef formatter) {
229301
LOCK();
230302
CFDateIntervalFormatterRef newFormatter = CFDateIntervalFormatterCreate(allocator, formatter->_locale, formatter->_dateStyle, formatter->_timeStyle);
@@ -294,7 +366,7 @@ void CFDateIntervalFormatterSetLocale(CFDateIntervalFormatterRef formatter, CFLo
294366

295367
CFCalendarRef CFDateIntervalFormatterCopyCalendar(CFDateIntervalFormatterRef formatter) {
296368
LOCK();
297-
CFCalendarRef calendar = formatter->_calendar;
369+
CFCalendarRef calendar = (CFCalendarRef)__CFRetainIfNotNull(formatter->_calendar);
298370
if (!calendar) {
299371
if (formatter->_locale) {
300372
calendar = (CFCalendarRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendar);

CoreFoundation/Locale.subproj/CFDateIntervalFormatter.h

+22
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,28 @@ CF_EXPORT CFStringRef CFDateIntervalFormatterCreateStringFromDateInterval(CFDate
6666
CF_EXPORT _CFDateIntervalFormatterBoundaryStyle _CFDateIntervalFormatterGetBoundaryStyle(CFDateIntervalFormatterRef formatter);
6767
CF_EXPORT void _CFDateIntervalFormatterSetBoundaryStyle(CFDateIntervalFormatterRef formatter, _CFDateIntervalFormatterBoundaryStyle boundaryStyle);
6868

69+
CF_EXPORT void _CFDateIntervalFormatterInitializeFromCoderValues(CFDateIntervalFormatterRef formatter,
70+
int64_t dateStyle,
71+
int64_t timeStyle,
72+
CFStringRef _Nullable dateTemplate,
73+
CFStringRef _Nullable dateTemplateFromStyles,
74+
bool modified,
75+
bool useTemplate,
76+
CFLocaleRef _Nullable locale,
77+
CFCalendarRef _Nullable calendar,
78+
CFTimeZoneRef _Nullable timeZone);
79+
80+
CF_EXPORT void _CFDateIntervalFormatterCopyCoderValues(CFDateIntervalFormatterRef formatter,
81+
int64_t *dateStyle,
82+
int64_t *timeStyle,
83+
CFStringRef _Nullable *_Nonnull dateTemplate,
84+
CFStringRef _Nullable *_Nonnull dateTemplateFromStyles,
85+
bool *modified,
86+
bool *useTemplate,
87+
CFLocaleRef _Nullable *_Nonnull locale,
88+
CFCalendarRef _Nullable *_Nonnull calendar,
89+
CFTimeZoneRef _Nullable *_Nonnull timeZone);
90+
6991
CF_EXTERN_C_END
7092
CF_IMPLICIT_BRIDGING_DISABLED
7193
CF_ASSUME_NONNULL_END

Foundation/Data.swift

+23-1
Original file line numberDiff line numberDiff line change
@@ -562,11 +562,33 @@ internal class __NSSwiftData : NSData {
562562
var _backing: __DataStorage!
563563
var _range: Range<Data.Index>!
564564

565+
override var classForCoder: AnyClass {
566+
return NSData.self
567+
}
568+
569+
override init() {
570+
fatalError()
571+
}
572+
573+
private init(_correctly: Void) {
574+
super.init()
575+
}
576+
565577
convenience init(backing: __DataStorage, range: Range<Data.Index>) {
566-
self.init()
578+
self.init(_correctly: ())
567579
_backing = backing
568580
_range = range
569581
}
582+
583+
public required init?(coder aDecoder: NSCoder) {
584+
fatalError("This should have been encoded as NSData.")
585+
}
586+
587+
override func encode(with aCoder: NSCoder) {
588+
// This should encode this object just like NSData does, and .classForCoder should do the rest.
589+
super.encode(with: aCoder)
590+
}
591+
570592
override var length: Int {
571593
return _range.upperBound - _range.lowerBound
572594
}

Foundation/DateIntervalFormatter.swift

+73-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ open class DateIntervalFormatter: Formatter {
8888
let core: CFDateIntervalFormatter
8989

9090
public override init() {
91-
core = CFDateIntervalFormatterCreate(nil, nil, kCFDateIntervalFormatterMediumStyle, kCFDateIntervalFormatterMediumStyle)
91+
core = CFDateIntervalFormatterCreate(nil, nil, kCFDateIntervalFormatterShortStyle, kCFDateIntervalFormatterShortStyle)
9292
super.init()
9393
}
9494

@@ -98,7 +98,78 @@ open class DateIntervalFormatter: Formatter {
9898
}
9999

100100
public required init?(coder: NSCoder) {
101-
NSUnimplemented()
101+
guard coder.allowsKeyedCoding else { fatalError("Requires a keyed coding-capable archiver.") }
102+
103+
func cfObject<T: NSObject & _CFBridgeable>(of aClass: T.Type, from coder: NSCoder, forKey key: String) -> T.CFType? {
104+
if coder.containsValue(forKey: key) {
105+
let object = coder.decodeObject(forKey: key) as? T
106+
return object?._cfObject
107+
} else {
108+
return nil
109+
}
110+
}
111+
112+
let core = CFDateIntervalFormatterCreate(nil, nil, kCFDateIntervalFormatterMediumStyle, kCFDateIntervalFormatterMediumStyle)
113+
_CFDateIntervalFormatterInitializeFromCoderValues(core,
114+
coder.decodeInt64(forKey: "NS.dateStyle"),
115+
coder.decodeInt64(forKey: "NS.timeStyle"),
116+
cfObject(of: NSString.self, from: coder, forKey: "NS.dateTemplate"),
117+
cfObject(of: NSString.self, from: coder, forKey: "NS.dateTemplateFromStyle"),
118+
coder.decodeBool(forKey: "NS.modified"),
119+
coder.decodeBool(forKey: "NS.useTemplate"),
120+
cfObject(of: NSLocale.self, from: coder, forKey: "NS.locale"),
121+
cfObject(of: NSCalendar.self, from: coder, forKey: "NS.calendar"),
122+
cfObject(of: NSTimeZone.self, from: coder, forKey: "NS.timeZone"))
123+
self.core = core
124+
125+
super.init(coder: coder)
126+
}
127+
128+
open override func encode(with aCoder: NSCoder) {
129+
guard aCoder.allowsKeyedCoding else { fatalError("Requires a keyed coding-capable archiver.") }
130+
super.encode(with: aCoder)
131+
132+
var dateStyle: Int64 = 0
133+
var timeStyle: Int64 = 0
134+
var dateTemplate: Unmanaged<CFString>?
135+
var dateTemplateFromStyles: Unmanaged<CFString>?
136+
var modified: Bool = false
137+
var useTemplate: Bool = false
138+
var locale: Unmanaged<CFLocale>?
139+
var calendar: Unmanaged<CFCalendar>?
140+
var timeZone: Unmanaged<CFTimeZone>?
141+
142+
_CFDateIntervalFormatterCopyCoderValues(core,
143+
&dateStyle,
144+
&timeStyle,
145+
&dateTemplate,
146+
&dateTemplateFromStyles,
147+
&modified,
148+
&useTemplate,
149+
&locale,
150+
&calendar,
151+
&timeZone);
152+
153+
aCoder.encode(dateStyle, forKey: "NS.dateStyle")
154+
aCoder.encode(timeStyle, forKey: "NS.timeStyle")
155+
156+
let dateTemplateNS = dateTemplate?.takeRetainedValue()._nsObject
157+
aCoder.encode(dateTemplateNS, forKey: "NS.dateTemplate")
158+
159+
let dateTemplateFromStylesNS = dateTemplateFromStyles?.takeRetainedValue()._nsObject
160+
aCoder.encode(dateTemplateFromStylesNS, forKey: "NS.dateTemplateFromStyles")
161+
162+
aCoder.encode(modified, forKey: "NS.modified");
163+
aCoder.encode(useTemplate, forKey: "NS.useTemplate")
164+
165+
let localeNS = locale?.takeRetainedValue()._nsObject
166+
aCoder.encode(localeNS, forKey: "NS.locale")
167+
168+
let calendarNS = calendar?.takeRetainedValue()._nsObject
169+
aCoder.encode(calendarNS, forKey: "NS.calendar")
170+
171+
let timeZoneNS = timeZone?.takeRetainedValue()._nsObject
172+
aCoder.encode(timeZoneNS, forKey: "NS.timeZone")
102173
}
103174

104175
/*@NSCopying*/ open var locale: Locale! {

Foundation/NSCalendar.swift

+13-6
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,23 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding {
174174

175175
self.init(identifier: NSCalendar.Identifier.init(rawValue: calendarIdentifier._swiftObject))
176176

177-
if let timeZone = aDecoder.decodeObject(of: NSTimeZone.self, forKey: "NS.timezone") {
178-
self.timeZone = timeZone._swiftObject
177+
if aDecoder.containsValue(forKey: "NS.timezone") {
178+
if let timeZone = aDecoder.decodeObject(of: NSTimeZone.self, forKey: "NS.timezone") {
179+
self.timeZone = timeZone._swiftObject
180+
}
179181
}
180-
if let locale = aDecoder.decodeObject(of: NSLocale.self, forKey: "NS.locale") {
181-
self.locale = locale._swiftObject
182+
if aDecoder.containsValue(forKey: "NS.locale") {
183+
if let locale = aDecoder.decodeObject(of: NSLocale.self, forKey: "NS.locale") {
184+
self.locale = locale._swiftObject
185+
}
182186
}
183187
self.firstWeekday = aDecoder.decodeInteger(forKey: "NS.firstwkdy")
184188
self.minimumDaysInFirstWeek = aDecoder.decodeInteger(forKey: "NS.mindays")
185-
if let startDate = aDecoder.decodeObject(of: NSDate.self, forKey: "NS.gstartdate") {
186-
self.gregorianStartDate = startDate._swiftObject
189+
190+
if aDecoder.containsValue(forKey: "NS.gstartdate") {
191+
if let startDate = aDecoder.decodeObject(of: NSDate.self, forKey: "NS.gstartdate") {
192+
self.gregorianStartDate = startDate._swiftObject
193+
}
187194
}
188195
}
189196

Foundation/NSLocale.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import CoreFoundation
1212

13-
open class NSLocale: NSObject, NSCopying, NSSecureCoding {
13+
open class NSLocale: NSObject, NSCopying, NSSecureCoding, _CFBridgeable {
1414
typealias CFType = CFLocale
1515
private var _base = _CFInfo(typeID: CFLocaleGetTypeID())
1616
private var _identifier: UnsafeMutableRawPointer? = nil

Foundation/NSTimeZone.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,14 @@ open class NSTimeZone : NSObject, NSCopying, NSSecureCoding, NSCoding {
4242
#endif
4343
}
4444
#endif
45+
4546
super.init()
46-
if !_CFTimeZoneInit(_cfObject, tzName._cfObject, aData?._cfObject) {
47+
48+
/* From https://developer.apple.com/documentation/foundation/nstimezone/1387250-init:
49+
"Discussion
50+
As of macOS 10.6, the underlying implementation of this method has been changed to ignore the specified data parameter."
51+
*/
52+
if !_CFTimeZoneInit(_cfObject, tzName._cfObject, nil) {
4753
return nil
4854
}
4955
}

0 commit comments

Comments
 (0)