From 360a0a2130fc814f0cd2bda62b48eb17eedcc57e Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Tue, 19 Mar 2024 21:04:46 +1100 Subject: [PATCH 01/12] Add tests for the `NSDate` extension implementing RFC3339 --- WordPressKit.xcodeproj/project.pbxproj | 4 ++ WordPressKitTests/NSDate+RFC3339Tests.swift | 43 +++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 WordPressKitTests/NSDate+RFC3339Tests.swift diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index d6d1b331..2d5d6a33 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ 3F758FD324F6C68200BBA2FC /* AnnouncementServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */; }; 3F8308A729EE683500354497 /* ActivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8308A629EE683500354497 /* ActivityTests.swift */; }; 3FB8642C2888089F003A86BE /* BuildkiteTestCollector in Frameworks */ = {isa = PBXBuildFile; productRef = 3FB8642B2888089F003A86BE /* BuildkiteTestCollector */; }; + 3FFCC0412BA995290051D229 /* NSDate+RFC3339Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */; }; 40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; }; 40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; }; 404057C5221B30400060250C /* StatsSearchTermTimeIntervalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */; }; @@ -798,6 +799,7 @@ 3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementServiceRemote.swift; sourceTree = ""; }; 3F8308A629EE683500354497 /* ActivityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTests.swift; sourceTree = ""; }; 3FB8642D288813E9003A86BE /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; + 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+RFC3339Tests.swift"; sourceTree = ""; }; 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = ""; }; 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = ""; }; 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSearchTermTimeIntervalData.swift; sourceTree = ""; }; @@ -1904,6 +1906,7 @@ FFA4D4A82423B10A00BF5180 /* WordPressOrgRestApiTests.swift */, 74B335DB1F06F4180053A184 /* WordPressOrgXMLRPCApiTests.swift */, 740B23D51F17F7C100067A2A /* XMLRPCTestable.swift */, + 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */, ); name = Tests; sourceTree = ""; @@ -3611,6 +3614,7 @@ F4B0F4802ACB4EA9003ABC61 /* AllDomainsResultDomainTests.swift in Sources */, 74585B901F0D51F900E7E667 /* DomainsServiceRemoteRESTTests.swift in Sources */, BAFA775624ADAB3C000F0D3A /* MockPluginDirectoryEntryProvider.swift in Sources */, + 3FFCC0412BA995290051D229 /* NSDate+RFC3339Tests.swift in Sources */, 3F8308A729EE683500354497 /* ActivityTests.swift in Sources */, 9AB6D64A218727D60008F274 /* PostServiceRemoteRESTRevisionsTest.swift in Sources */, 01438D382B6A35FB0097D60A /* stats-summary.json in Sources */, diff --git a/WordPressKitTests/NSDate+RFC3339Tests.swift b/WordPressKitTests/NSDate+RFC3339Tests.swift new file mode 100644 index 00000000..2804b612 --- /dev/null +++ b/WordPressKitTests/NSDate+RFC3339Tests.swift @@ -0,0 +1,43 @@ +@testable import WordPressKit +import XCTest + +// This is an incomplete test for implementing RFC 3339. +// It's purpose is to ensure our code "works". +// +// See also: +// +// - https://datatracker.ietf.org/doc/html/rfc3339 +class NSDateRFC3339Tests: XCTestCase { + + func testDateFormatterConfiguration() throws { + let rfc3339Formatter = try XCTUnwrap(NSDate.rfc3339DateFormatter()) + + XCTAssertEqual(rfc3339Formatter.timeZone, TimeZone(secondsFromGMT: 0)) + XCTAssertEqual(rfc3339Formatter.locale, Locale(identifier: "en_US_POSIX")) + XCTAssertEqual(rfc3339Formatter.dateFormat, "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ") + } + + func testValidRFC3339DateFromString() { + XCTAssertEqual( + NSDate(wordPressComJSONString: "2023-03-19T15:00:00Z"), + NSDate(timeIntervalSince1970: 1_679_238_000) + ) + } + + func testInvalidRFC3339DateFromString() { + XCTAssertNil(NSDate(wordPressComJSONString: "2024-01-01")) + } + + func testInvalidDateFromString() { + XCTAssertNil(NSDate(wordPressComJSONString: "not a date")) + } + + func testValidRFC3339StringFromDate() { + XCTAssertEqual( + NSDate(timeIntervalSince1970: 1_679_238_000).wordPressComJSONString(), + // Apparently, NSDateFormatter doesn't offer a way to specify Z vs +0000. + // This might go all the way back to the ISO 8601 and RFC 3339 specs overlap. + "2023-03-19T15:00:00+0000" + ) + } +} From 50916f6e8515e8d39e4cc75c08e787e289b6d51d Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 20 Mar 2024 12:13:17 +1100 Subject: [PATCH 02/12] Convert `-(NSString *)WordPressComJSONString` method to Swift --- WordPressKit.xcodeproj/project.pbxproj | 4 ++++ WordPressKit/NSDate+RFC3339.swift | 9 +++++++++ WordPressKit/NSDate+WordPressJSON.h | 2 -- WordPressKit/NSDate+WordPressJSON.m | 4 ---- 4 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 WordPressKit/NSDate+RFC3339.swift diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index 2d5d6a33..d5ebb72e 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -72,6 +72,7 @@ 3F8308A729EE683500354497 /* ActivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8308A629EE683500354497 /* ActivityTests.swift */; }; 3FB8642C2888089F003A86BE /* BuildkiteTestCollector in Frameworks */ = {isa = PBXBuildFile; productRef = 3FB8642B2888089F003A86BE /* BuildkiteTestCollector */; }; 3FFCC0412BA995290051D229 /* NSDate+RFC3339Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */; }; + 3FFCC0472BAA6EF40051D229 /* NSDate+RFC3339.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */; }; 40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; }; 40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; }; 404057C5221B30400060250C /* StatsSearchTermTimeIntervalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */; }; @@ -800,6 +801,7 @@ 3F8308A629EE683500354497 /* ActivityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTests.swift; sourceTree = ""; }; 3FB8642D288813E9003A86BE /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+RFC3339Tests.swift"; sourceTree = ""; }; + 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+RFC3339.swift"; sourceTree = ""; }; 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = ""; }; 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = ""; }; 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSearchTermTimeIntervalData.swift; sourceTree = ""; }; @@ -2523,6 +2525,7 @@ 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */, 93BD27751EE73944002BB00B /* NSDate+WordPressJSON.h */, 93BD27761EE73944002BB00B /* NSDate+WordPressJSON.m */, + 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */, 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */, 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */, 93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */, @@ -3462,6 +3465,7 @@ E632D7781F6E047400297F6D /* SocialLogin2FANonceInfo.swift in Sources */, 32FC1D29255C91ED00CD0A7B /* JetpackScanServiceRemote.swift in Sources */, 9F3E0B9B208732B3009CB5BA /* RemoteReaderSiteInfoSubscription.swift in Sources */, + 3FFCC0472BAA6EF40051D229 /* NSDate+RFC3339.swift in Sources */, 7403A2E41EF06ED500DED7DC /* AccountSettingsRemote.swift in Sources */, 3236F77824AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift in Sources */, FE20A6A4282A96C00025E975 /* RemoteBloggingPromptsSettings.swift in Sources */, diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+RFC3339.swift new file mode 100644 index 00000000..64d03bf4 --- /dev/null +++ b/WordPressKit/NSDate+RFC3339.swift @@ -0,0 +1,9 @@ +import Foundation + +extension NSDate { + + @objc(WordPressComJSONString) + public func wordPressComJSONString() -> String { + NSDate.rfc3339DateFormatter().string(from: self as Date) + } +} diff --git a/WordPressKit/NSDate+WordPressJSON.h b/WordPressKit/NSDate+WordPressJSON.h index 5d9c2171..06bd2684 100644 --- a/WordPressKit/NSDate+WordPressJSON.h +++ b/WordPressKit/NSDate+WordPressJSON.h @@ -16,6 +16,4 @@ + (instancetype)dateWithWordPressComJSONString:(NSString *)string; -- (NSString *)WordPressComJSONString; - @end diff --git a/WordPressKit/NSDate+WordPressJSON.m b/WordPressKit/NSDate+WordPressJSON.m index b3774998..cac99a5e 100644 --- a/WordPressKit/NSDate+WordPressJSON.m +++ b/WordPressKit/NSDate+WordPressJSON.m @@ -19,8 +19,4 @@ + (instancetype)dateWithWordPressComJSONString:(NSString *)string { return [[self rfc3339DateFormatter] dateFromString:string]; } -- (NSString *)WordPressComJSONString { - return [[[self class] rfc3339DateFormatter] stringFromDate:self]; -} - @end From 51f0616ab94a0052a1edd0c11eac2a8fd1680f1d Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 20 Mar 2024 12:20:26 +1100 Subject: [PATCH 03/12] Convert `rfc3339DateFormatter` method to Swift --- WordPressKit/NSDate+RFC3339.swift | 17 +++++++++++++++++ WordPressKit/NSDate+WordPressJSON.h | 2 -- WordPressKit/NSDate+WordPressJSON.m | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+RFC3339.swift index 64d03bf4..332e9c74 100644 --- a/WordPressKit/NSDate+RFC3339.swift +++ b/WordPressKit/NSDate+RFC3339.swift @@ -2,8 +2,25 @@ import Foundation extension NSDate { + // TODO: Make `static let` after conversion since it's not used outside WordPressKit + @objc + static func rfc3339DateFormatter() -> DateFormatter { + DateFormatter.rfc3339Formatter + } + @objc(WordPressComJSONString) public func wordPressComJSONString() -> String { NSDate.rfc3339DateFormatter().string(from: self as Date) } } + +extension DateFormatter { + + static let rfc3339Formatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ" + formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone + formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale + return formatter + }() +} diff --git a/WordPressKit/NSDate+WordPressJSON.h b/WordPressKit/NSDate+WordPressJSON.h index 06bd2684..2c1b4228 100644 --- a/WordPressKit/NSDate+WordPressJSON.h +++ b/WordPressKit/NSDate+WordPressJSON.h @@ -2,8 +2,6 @@ @interface NSDate (WordPressJSON) -+ (NSDateFormatter *)rfc3339DateFormatter; - /** Parses a date string diff --git a/WordPressKit/NSDate+WordPressJSON.m b/WordPressKit/NSDate+WordPressJSON.m index cac99a5e..bd819be5 100644 --- a/WordPressKit/NSDate+WordPressJSON.m +++ b/WordPressKit/NSDate+WordPressJSON.m @@ -2,6 +2,7 @@ @implementation NSDate (WordPressJSON) +// FIXME: This is here only for the benefit of dateWithWordPressComJSONString. Remove together with that. + (NSDateFormatter *)rfc3339DateFormatter { static NSDateFormatter *rfc3339DateFormatter; if (rfc3339DateFormatter == nil) { From 71fa95137a6687c6f28ea18e78322de3622ed997 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 20 Mar 2024 12:29:07 +1100 Subject: [PATCH 04/12] Convert `dateWithWordPressComJSONString:` to Swift This resulted in a change in the Swift API. I think this is acceptable because the method was not used outside WordPressKit. See: - WordPressAuthenticator https://github.com/search?q=repo%3Awordpress-mobile%2FWordPressAuthenticator-iOS%20dateWithWordPressComJSONString&type=code - WordPress-iOS https://github.com/search?q=repo%3Awordpress-mobile%2FWordPress-iOS+dateWithWordPressComJSONString&type=code --- WordPressKit/NSDate+RFC3339.swift | 15 +++++++++++++++ WordPressKit/NSDate+WordPressJSON.h | 2 +- WordPressKit/RemoteCommentV2.swift | 2 +- .../CommentServiceRemoteREST+APIv2Tests.swift | 2 +- .../CommentServiceRemoteRESTTests.swift | 4 ++-- WordPressKitTests/NSDate+RFC3339Tests.swift | 8 ++++---- .../PostServiceRemoteRESTAutosaveTests.swift | 2 +- 7 files changed, 25 insertions(+), 10 deletions(-) diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+RFC3339.swift index 332e9c74..8b29da58 100644 --- a/WordPressKit/NSDate+RFC3339.swift +++ b/WordPressKit/NSDate+RFC3339.swift @@ -8,6 +8,21 @@ extension NSDate { DateFormatter.rfc3339Formatter } + /// Parses a date string + /// + /// Dates in the format specified in http://www.w3.org/TR/NOTE-datetime should be OK. + /// The kind of dates returned by the REST API should match that format, even if the doc promises ISO 8601. + /// + /// Parsing the full ISO 8601, or even RFC 3339 is more complex than this, and makes no sense right now. + /// + /// - Warning: This method doesn't support fractional seconds or dates with leap seconds (23:59:60 turns into 23:59:00) + // + // Needs to be `public` because of the usages in the Objective-C code. + @objc(dateWithWordPressComJSONString:) + public static func with(wordPressComJSONString jsonString: String) -> Date? { + self.rfc3339DateFormatter().date(from: jsonString) + } + @objc(WordPressComJSONString) public func wordPressComJSONString() -> String { NSDate.rfc3339DateFormatter().string(from: self as Date) diff --git a/WordPressKit/NSDate+WordPressJSON.h b/WordPressKit/NSDate+WordPressJSON.h index 2c1b4228..caa1cb55 100644 --- a/WordPressKit/NSDate+WordPressJSON.h +++ b/WordPressKit/NSDate+WordPressJSON.h @@ -12,6 +12,6 @@ @warning this method doesn't support fractional seconds or dates with leap seconds (23:59:60 turns into 23:59:00) */ -+ (instancetype)dateWithWordPressComJSONString:(NSString *)string; +//+ (instancetype)dateWithWordPressComJSONString:(NSString *)string; @end diff --git a/WordPressKit/RemoteCommentV2.swift b/WordPressKit/RemoteCommentV2.swift index b407bf6d..a80eaea2 100644 --- a/WordPressKit/RemoteCommentV2.swift +++ b/WordPressKit/RemoteCommentV2.swift @@ -65,7 +65,7 @@ extension RemoteCommentV2: Decodable { // since `date_gmt` is already in GMT timezone, manually add the timezone string to make the rfc3339 formatter happy (or it will throw otherwise). guard let dateString = try? container.decode(String.self, forKey: .date), - let date = NSDate(wordPressComJSONString: dateString + "+00:00") as Date? else { + let date = NSDate.with(wordPressComJSONString: dateString + "+00:00") else { throw DecodingError.dataCorruptedError(forKey: .date, in: container, debugDescription: "Date parsing failed") } self.date = date diff --git a/WordPressKitTests/CommentServiceRemoteREST+APIv2Tests.swift b/WordPressKitTests/CommentServiceRemoteREST+APIv2Tests.swift index 613959d6..1cdb540e 100644 --- a/WordPressKitTests/CommentServiceRemoteREST+APIv2Tests.swift +++ b/WordPressKitTests/CommentServiceRemoteREST+APIv2Tests.swift @@ -41,7 +41,7 @@ final class CommentServiceRemoteREST_APIv2Tests: RemoteTestCase, RESTTestable { XCTAssertEqual(firstComment.authorID, 135) XCTAssertEqual(firstComment.authorName, "John Doe") XCTAssertEqual(firstComment.authorURL, "https://example.com/john-doe") - XCTAssertEqual(firstComment.date, NSDate(wordPressComJSONString: "2021-07-01T10:50:11+00:00") as Date) + XCTAssertEqual(firstComment.date, NSDate.with(wordPressComJSONString: "2021-07-01T10:50:11+00:00")) XCTAssertEqual(firstComment.content, "

Some example comment.

\n") XCTAssertEqual(firstComment.link, "https://example.com/2021/05/25/example-post/comment-page-1/#comment-2") XCTAssertEqual(firstComment.status, "approve") // verify that it's converted correctly. diff --git a/WordPressKitTests/CommentServiceRemoteRESTTests.swift b/WordPressKitTests/CommentServiceRemoteRESTTests.swift index cc1df856..b1b00f35 100644 --- a/WordPressKitTests/CommentServiceRemoteRESTTests.swift +++ b/WordPressKitTests/CommentServiceRemoteRESTTests.swift @@ -50,7 +50,7 @@ final class CommentServiceRemoteRESTTests: RemoteTestCase, RESTTestable { XCTAssertEqual(comment.authorEmail, "author@email.com") XCTAssertEqual(comment.authorUrl, "author URL") XCTAssertEqual(comment.authorIP, "000.0.00.000") - XCTAssertEqual(comment.date, NSDate(wordPressComJSONString: "2021-08-04T07:58:49+00:00") as Date) + XCTAssertEqual(comment.date, NSDate.with(wordPressComJSONString: "2021-08-04T07:58:49+00:00")) XCTAssertEqual(comment.link, "comment URL") XCTAssertEqual(comment.parentID, nil) XCTAssertEqual(comment.postID, NSNumber(value: 1)) @@ -91,7 +91,7 @@ final class CommentServiceRemoteRESTTests: RemoteTestCase, RESTTestable { XCTAssertEqual(comment.authorEmail, "author@email.com") XCTAssertEqual(comment.authorUrl, "author URL") XCTAssertEqual(comment.authorIP, "000.0.00.000") - XCTAssertEqual(comment.date, NSDate(wordPressComJSONString: "2021-08-04T07:58:49+00:00") as Date) + XCTAssertEqual(comment.date, NSDate.with(wordPressComJSONString: "2021-08-04T07:58:49+00:00")) XCTAssertEqual(comment.link, "comment URL") XCTAssertEqual(comment.parentID, nil) XCTAssertEqual(comment.postID, NSNumber(value: 1)) diff --git a/WordPressKitTests/NSDate+RFC3339Tests.swift b/WordPressKitTests/NSDate+RFC3339Tests.swift index 2804b612..dd5e3f3f 100644 --- a/WordPressKitTests/NSDate+RFC3339Tests.swift +++ b/WordPressKitTests/NSDate+RFC3339Tests.swift @@ -19,17 +19,17 @@ class NSDateRFC3339Tests: XCTestCase { func testValidRFC3339DateFromString() { XCTAssertEqual( - NSDate(wordPressComJSONString: "2023-03-19T15:00:00Z"), - NSDate(timeIntervalSince1970: 1_679_238_000) + NSDate.with(wordPressComJSONString: "2023-03-19T15:00:00Z"), + Date(timeIntervalSince1970: 1_679_238_000) ) } func testInvalidRFC3339DateFromString() { - XCTAssertNil(NSDate(wordPressComJSONString: "2024-01-01")) + XCTAssertNil(NSDate.with(wordPressComJSONString: "2024-01-01")) } func testInvalidDateFromString() { - XCTAssertNil(NSDate(wordPressComJSONString: "not a date")) + XCTAssertNil(NSDate.with(wordPressComJSONString: "not a date")) } func testValidRFC3339StringFromDate() { diff --git a/WordPressKitTests/PostServiceRemoteRESTAutosaveTests.swift b/WordPressKitTests/PostServiceRemoteRESTAutosaveTests.swift index 8b85dfbd..0883b4c4 100644 --- a/WordPressKitTests/PostServiceRemoteRESTAutosaveTests.swift +++ b/WordPressKitTests/PostServiceRemoteRESTAutosaveTests.swift @@ -41,7 +41,7 @@ class PostServiceRemoteRESTAutosaveTests: RemoteTestCase, RESTTestable { XCTAssertEqual(remotePost.autosave.content, "\n

Uno.

\n") XCTAssertEqual(remotePost.autosave.excerpt, "abc") XCTAssertEqual(remotePost.autosave.previewURL, "https://hello.wordpress.com/2019/10/28/hello-world/?preview=true&preview_nonce=07346f4e5d") - XCTAssertEqual(remotePost.autosave.modifiedDate, NSDate(wordPressComJSONString: "2019-10-28T02:06:39+00:00") as Date?) + XCTAssertEqual(remotePost.autosave.modifiedDate, NSDate.with(wordPressComJSONString: "2019-10-28T02:06:39+00:00")) expect.fulfill() }, failure: { _ in XCTFail("This callback shouldn't get called") From b0e6dcfba64595e8b9c6c9d241aa7ff1802c70f6 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 20 Mar 2024 12:31:31 +1100 Subject: [PATCH 05/12] Delete unused `NSDate+WordPressJSON` Objective-C file --- WordPressKit.xcodeproj/project.pbxproj | 8 -------- WordPressKit/NSDate+WordPressJSON.h | 17 ----------------- WordPressKit/NSDate+WordPressJSON.m | 23 ----------------------- WordPressKit/WordPressKit.h | 1 - 4 files changed, 49 deletions(-) delete mode 100644 WordPressKit/NSDate+WordPressJSON.h delete mode 100644 WordPressKit/NSDate+WordPressJSON.m diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index d5ebb72e..427f8ed3 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -487,8 +487,6 @@ 93BD27711EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 93BD276D1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h */; settings = {ATTRIBUTES = (Public, ); }; }; 93BD27721EE737A9002BB00B /* ServiceRemoteWordPressXMLRPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BD276E1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.m */; }; 93BD277C1EE73944002BB00B /* HTTPAuthenticationAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */; }; - 93BD277D1EE73944002BB00B /* NSDate+WordPressJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 93BD27751EE73944002BB00B /* NSDate+WordPressJSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 93BD277E1EE73944002BB00B /* NSDate+WordPressJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27761EE73944002BB00B /* NSDate+WordPressJSON.m */; }; 93BD277F1EE73944002BB00B /* WordPressComOAuthClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */; }; 93BD27801EE73944002BB00B /* WordPressComRestApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */; }; 93BD27811EE73944002BB00B /* WordPressOrgXMLRPCApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */; }; @@ -1222,8 +1220,6 @@ 93BD276D1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServiceRemoteWordPressXMLRPC.h; sourceTree = ""; }; 93BD276E1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServiceRemoteWordPressXMLRPC.m; sourceTree = ""; }; 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPAuthenticationAlertController.swift; sourceTree = ""; }; - 93BD27751EE73944002BB00B /* NSDate+WordPressJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+WordPressJSON.h"; sourceTree = ""; }; - 93BD27761EE73944002BB00B /* NSDate+WordPressJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+WordPressJSON.m"; sourceTree = ""; }; 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordPressComOAuthClient.swift; sourceTree = ""; }; 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordPressComRestApi.swift; sourceTree = ""; }; 93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordPressOrgXMLRPCApi.swift; sourceTree = ""; }; @@ -2523,8 +2519,6 @@ 4A05E7952B2FCB6400C25E3B /* NonceRetrieval.swift */, 4A05E7992B2FDC3200C25E3B /* WordPressOrgRestApi.swift */, 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */, - 93BD27751EE73944002BB00B /* NSDate+WordPressJSON.h */, - 93BD27761EE73944002BB00B /* NSDate+WordPressJSON.m */, 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */, 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */, 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */, @@ -2803,7 +2797,6 @@ 93BD273C1EE73282002BB00B /* AccountServiceRemoteREST.h in Headers */, 93BD27711EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h in Headers */, 93BD276F1EE737A8002BB00B /* ServiceRemoteWordPressComREST.h in Headers */, - 93BD277D1EE73944002BB00B /* NSDate+WordPressJSON.h in Headers */, 93BD273B1EE73282002BB00B /* AccountServiceRemote.h in Headers */, 93BD27691EE736A8002BB00B /* RemoteUser.h in Headers */, 74B5F0DC1EF829B800B411E7 /* BlogServiceRemote.h in Headers */, @@ -3433,7 +3426,6 @@ 9311A68B1F22625A00704AC9 /* TaxonomyServiceRemoteXMLRPC.m in Sources */, C797196E2679007B0072F984 /* SelfHostedPluginManagementClient.swift in Sources */, 40414060220F9F1F00CF7C5B /* StatsAllTimesInsight.swift in Sources */, - 93BD277E1EE73944002BB00B /* NSDate+WordPressJSON.m in Sources */, 8B2F4BED24ABCAEF0056C08A /* Decodable+Dictionary.swift in Sources */, E194CB731FBDEF6500B0A8B8 /* PluginState.swift in Sources */, 4A57A6882B54C68C008D0660 /* Constants.m in Sources */, diff --git a/WordPressKit/NSDate+WordPressJSON.h b/WordPressKit/NSDate+WordPressJSON.h deleted file mode 100644 index caa1cb55..00000000 --- a/WordPressKit/NSDate+WordPressJSON.h +++ /dev/null @@ -1,17 +0,0 @@ -#import - -@interface NSDate (WordPressJSON) - -/** - Parses a date string - - Dates in the format specified in http://www.w3.org/TR/NOTE-datetime should be OK. - The kind of dates returned by the REST API should match that format, even if the doc promises ISO 8601. - - Parsing the full ISO 8601, or even RFC 3339 is more complex than this, and makes no sense right now. - @warning this method doesn't support fractional seconds or dates with leap seconds (23:59:60 turns into 23:59:00) - */ - -//+ (instancetype)dateWithWordPressComJSONString:(NSString *)string; - -@end diff --git a/WordPressKit/NSDate+WordPressJSON.m b/WordPressKit/NSDate+WordPressJSON.m deleted file mode 100644 index bd819be5..00000000 --- a/WordPressKit/NSDate+WordPressJSON.m +++ /dev/null @@ -1,23 +0,0 @@ -#import "NSDate+WordPressJSON.h" - -@implementation NSDate (WordPressJSON) - -// FIXME: This is here only for the benefit of dateWithWordPressComJSONString. Remove together with that. -+ (NSDateFormatter *)rfc3339DateFormatter { - static NSDateFormatter *rfc3339DateFormatter; - if (rfc3339DateFormatter == nil) { - rfc3339DateFormatter = [[NSDateFormatter alloc] init]; - NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; - - [rfc3339DateFormatter setLocale:enUSPOSIXLocale]; - [rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"]; - [rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; - } - return rfc3339DateFormatter; -} - -+ (instancetype)dateWithWordPressComJSONString:(NSString *)string { - return [[self rfc3339DateFormatter] dateFromString:string]; -} - -@end diff --git a/WordPressKit/WordPressKit.h b/WordPressKit/WordPressKit.h index bec245f2..f498488e 100644 --- a/WordPressKit/WordPressKit.h +++ b/WordPressKit/WordPressKit.h @@ -47,7 +47,6 @@ FOUNDATION_EXPORT const unsigned char WordPressKitVersionString[]; #import #import -#import #import #import From b0ed5855c4a23dbe39a09a40b411a5eb5b7a5315 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 20 Mar 2024 12:33:51 +1100 Subject: [PATCH 06/12] Make new Swift `rfc3339DateFormatter` a `static let` instead of `func` It didn't work in Objective-C, but in Swift it's possible to do it and it makes more sense to have it implemented as `static let` so it's initialized only once. --- WordPressKit/NSDate+RFC3339.swift | 9 +++------ WordPressKit/PostServiceRemoteREST+Extended.swift | 2 +- WordPressKitTests/NSDate+RFC3339Tests.swift | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+RFC3339.swift index 8b29da58..5a194745 100644 --- a/WordPressKit/NSDate+RFC3339.swift +++ b/WordPressKit/NSDate+RFC3339.swift @@ -2,11 +2,8 @@ import Foundation extension NSDate { - // TODO: Make `static let` after conversion since it's not used outside WordPressKit @objc - static func rfc3339DateFormatter() -> DateFormatter { - DateFormatter.rfc3339Formatter - } + static let rfc3339DateFormatter = DateFormatter.rfc3339Formatter /// Parses a date string /// @@ -20,12 +17,12 @@ extension NSDate { // Needs to be `public` because of the usages in the Objective-C code. @objc(dateWithWordPressComJSONString:) public static func with(wordPressComJSONString jsonString: String) -> Date? { - self.rfc3339DateFormatter().date(from: jsonString) + self.rfc3339DateFormatter.date(from: jsonString) } @objc(WordPressComJSONString) public func wordPressComJSONString() -> String { - NSDate.rfc3339DateFormatter().string(from: self as Date) + NSDate.rfc3339DateFormatter.string(from: self as Date) } } diff --git a/WordPressKit/PostServiceRemoteREST+Extended.swift b/WordPressKit/PostServiceRemoteREST+Extended.swift index f84b49f2..e9c710a5 100644 --- a/WordPressKit/PostServiceRemoteREST+Extended.swift +++ b/WordPressKit/PostServiceRemoteREST+Extended.swift @@ -40,7 +40,7 @@ private func decodePost(from object: AnyObject) async throws -> RemotePost { private func makeParameters(from value: T) throws -> [String: AnyObject] { let encoder = JSONEncoder() - encoder.dateEncodingStrategy = .formatted(NSDate.rfc3339DateFormatter()) + encoder.dateEncodingStrategy = .formatted(NSDate.rfc3339DateFormatter) let data = try encoder.encode(value) let object = try JSONSerialization.jsonObject(with: data) guard let dictionary = object as? [String: AnyObject] else { diff --git a/WordPressKitTests/NSDate+RFC3339Tests.swift b/WordPressKitTests/NSDate+RFC3339Tests.swift index dd5e3f3f..6728a5e7 100644 --- a/WordPressKitTests/NSDate+RFC3339Tests.swift +++ b/WordPressKitTests/NSDate+RFC3339Tests.swift @@ -10,7 +10,7 @@ import XCTest class NSDateRFC3339Tests: XCTestCase { func testDateFormatterConfiguration() throws { - let rfc3339Formatter = try XCTUnwrap(NSDate.rfc3339DateFormatter()) + let rfc3339Formatter = try XCTUnwrap(NSDate.rfc3339DateFormatter) XCTAssertEqual(rfc3339Formatter.timeZone, TimeZone(secondsFromGMT: 0)) XCTAssertEqual(rfc3339Formatter.locale, Locale(identifier: "en_US_POSIX")) From 5686c933443fd1f2ae43f7be3e95dda96ef67c82 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 20 Mar 2024 14:00:58 +1100 Subject: [PATCH 07/12] Restore `rfc3339DateFormatter()` method for backwards compatibility --- WordPressKit/NSDate+RFC3339.swift | 11 ++++++++--- WordPressKit/PostServiceRemoteREST+Extended.swift | 2 +- WordPressKitTests/NSDate+RFC3339Tests.swift | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+RFC3339.swift index 5a194745..c3c87670 100644 --- a/WordPressKit/NSDate+RFC3339.swift +++ b/WordPressKit/NSDate+RFC3339.swift @@ -2,8 +2,13 @@ import Foundation extension NSDate { + static let rfc3339Formatter = DateFormatter.rfc3339Formatter + @objc - static let rfc3339DateFormatter = DateFormatter.rfc3339Formatter + @available(*, deprecated, message: "Please use the static property instead. This is here for backwards compatibility and will soon be removed.") + public static func rfc3339DateFormatter() -> DateFormatter { + rfc3339Formatter + } /// Parses a date string /// @@ -17,12 +22,12 @@ extension NSDate { // Needs to be `public` because of the usages in the Objective-C code. @objc(dateWithWordPressComJSONString:) public static func with(wordPressComJSONString jsonString: String) -> Date? { - self.rfc3339DateFormatter.date(from: jsonString) + self.rfc3339Formatter.date(from: jsonString) } @objc(WordPressComJSONString) public func wordPressComJSONString() -> String { - NSDate.rfc3339DateFormatter.string(from: self as Date) + NSDate.rfc3339Formatter.string(from: self as Date) } } diff --git a/WordPressKit/PostServiceRemoteREST+Extended.swift b/WordPressKit/PostServiceRemoteREST+Extended.swift index e9c710a5..e4937aca 100644 --- a/WordPressKit/PostServiceRemoteREST+Extended.swift +++ b/WordPressKit/PostServiceRemoteREST+Extended.swift @@ -40,7 +40,7 @@ private func decodePost(from object: AnyObject) async throws -> RemotePost { private func makeParameters(from value: T) throws -> [String: AnyObject] { let encoder = JSONEncoder() - encoder.dateEncodingStrategy = .formatted(NSDate.rfc3339DateFormatter) + encoder.dateEncodingStrategy = .formatted(NSDate.rfc3339Formatter) let data = try encoder.encode(value) let object = try JSONSerialization.jsonObject(with: data) guard let dictionary = object as? [String: AnyObject] else { diff --git a/WordPressKitTests/NSDate+RFC3339Tests.swift b/WordPressKitTests/NSDate+RFC3339Tests.swift index 6728a5e7..26c0fc53 100644 --- a/WordPressKitTests/NSDate+RFC3339Tests.swift +++ b/WordPressKitTests/NSDate+RFC3339Tests.swift @@ -10,7 +10,7 @@ import XCTest class NSDateRFC3339Tests: XCTestCase { func testDateFormatterConfiguration() throws { - let rfc3339Formatter = try XCTUnwrap(NSDate.rfc3339DateFormatter) + let rfc3339Formatter = try XCTUnwrap(NSDate.rfc3339Formatter) XCTAssertEqual(rfc3339Formatter.timeZone, TimeZone(secondsFromGMT: 0)) XCTAssertEqual(rfc3339Formatter.locale, Locale(identifier: "en_US_POSIX")) From 31292ae618eb7eda8ced96f31a01e36e869cc568 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 21 Mar 2024 09:14:34 +1100 Subject: [PATCH 08/12] Bump version to 15.0.0-beta.1 because of breaking change in `NSDate` API See 71fa95137a6687c6f28ea18e78322de3622ed997 --- WordPressKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressKit.podspec b/WordPressKit.podspec index 21a17789..56a26741 100644 --- a/WordPressKit.podspec +++ b/WordPressKit.podspec @@ -2,7 +2,7 @@ Pod::Spec.new do |s| s.name = 'WordPressKit' - s.version = '14.1.0' + s.version = '15.0.0-beta.1' s.summary = 'WordPressKit offers a clean and simple WordPress.com and WordPress.org API.' s.description = <<-DESC From f3bc0a07e6ea9e6c8f76a633d9fdbfbc52cfdd07 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 21 Mar 2024 09:16:25 +1100 Subject: [PATCH 09/12] =?UTF-8?q?Breaking=20change=20=E2=80=93=20Remove=20?= =?UTF-8?q?deprecated=20`rfc3339DateFormatter()`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WordPressKit/NSDate+RFC3339.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+RFC3339.swift index c3c87670..f03e9b0c 100644 --- a/WordPressKit/NSDate+RFC3339.swift +++ b/WordPressKit/NSDate+RFC3339.swift @@ -4,12 +4,6 @@ extension NSDate { static let rfc3339Formatter = DateFormatter.rfc3339Formatter - @objc - @available(*, deprecated, message: "Please use the static property instead. This is here for backwards compatibility and will soon be removed.") - public static func rfc3339DateFormatter() -> DateFormatter { - rfc3339Formatter - } - /// Parses a date string /// /// Dates in the format specified in http://www.w3.org/TR/NOTE-datetime should be OK. From ca4b342b5c785739f5201c43011c74e4b2e929d0 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 21 Mar 2024 10:07:58 +1100 Subject: [PATCH 10/12] Extract `DateFormatter` RFC-3339 to file and rename to `wordPressCom` --- WordPressKit.xcodeproj/project.pbxproj | 8 ++++++++ WordPressKit/DateFormatter+WordPressCom.swift | 13 +++++++++++++ WordPressKit/NSDate+RFC3339.swift | 13 +------------ .../DateFormatter+WordPressComTests.swift | 13 +++++++++++++ WordPressKitTests/NSDate+RFC3339Tests.swift | 8 -------- 5 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 WordPressKit/DateFormatter+WordPressCom.swift create mode 100644 WordPressKitTests/DateFormatter+WordPressComTests.swift diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index 427f8ed3..cf1667d8 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -73,6 +73,8 @@ 3FB8642C2888089F003A86BE /* BuildkiteTestCollector in Frameworks */ = {isa = PBXBuildFile; productRef = 3FB8642B2888089F003A86BE /* BuildkiteTestCollector */; }; 3FFCC0412BA995290051D229 /* NSDate+RFC3339Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */; }; 3FFCC0472BAA6EF40051D229 /* NSDate+RFC3339.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */; }; + 3FFCC0492BAB98130051D229 /* DateFormatter+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */; }; + 3FFCC04B2BABA5220051D229 /* DateFormatter+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */; }; 40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; }; 40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; }; 404057C5221B30400060250C /* StatsSearchTermTimeIntervalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */; }; @@ -800,6 +802,8 @@ 3FB8642D288813E9003A86BE /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+RFC3339Tests.swift"; sourceTree = ""; }; 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+RFC3339.swift"; sourceTree = ""; }; + 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressCom.swift"; sourceTree = ""; }; + 3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressComTests.swift"; sourceTree = ""; }; 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = ""; }; 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = ""; }; 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSearchTermTimeIntervalData.swift; sourceTree = ""; }; @@ -1905,6 +1909,7 @@ 74B335DB1F06F4180053A184 /* WordPressOrgXMLRPCApiTests.swift */, 740B23D51F17F7C100067A2A /* XMLRPCTestable.swift */, 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */, + 3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */, ); name = Tests; sourceTree = ""; @@ -2520,6 +2525,7 @@ 4A05E7992B2FDC3200C25E3B /* WordPressOrgRestApi.swift */, 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */, 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */, + 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */, 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */, 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */, 93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */, @@ -3516,6 +3522,7 @@ 74C473AC1EF2F75E009918F2 /* SiteManagementServiceRemote.swift in Sources */, 74585B971F0D54B400E7E667 /* RemoteDomain.swift in Sources */, 0C1C08432B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift in Sources */, + 3FFCC0492BAB98130051D229 /* DateFormatter+WordPressCom.swift in Sources */, 74A44DD01F13C64B006CD8F4 /* RemoteNotification.swift in Sources */, 8B52B901257AC5A200221663 /* Date+endOfDay.swift in Sources */, E1D6B558200E473A00325669 /* TimeZoneServiceRemote.swift in Sources */, @@ -3643,6 +3650,7 @@ 4A05E79C2B2FDC6100C25E3B /* WordPressOrgAPITests.swift in Sources */, 3297E1DE2564653A00287D21 /* JetpackScanServiceRemoteTests.swift in Sources */, 01438D352B6A2B2C0097D60A /* stats-visits-month-unit-week.json in Sources */, + 3FFCC04B2BABA5220051D229 /* DateFormatter+WordPressComTests.swift in Sources */, 9F3E0BAC20873785009CB5BA /* ServiceRequestTest.swift in Sources */, 4624223E2548C26D002B8A12 /* SiteDesignServiceRemoteTests.swift in Sources */, 4A1DEF44293051BC00322608 /* LoggingTests.swift in Sources */, diff --git a/WordPressKit/DateFormatter+WordPressCom.swift b/WordPressKit/DateFormatter+WordPressCom.swift new file mode 100644 index 00000000..1bb03baf --- /dev/null +++ b/WordPressKit/DateFormatter+WordPressCom.swift @@ -0,0 +1,13 @@ +extension DateFormatter { + + /// A `DateFormatter` configured to manage dates compatible with the WordPress.com API. + /// + /// - SeeAlso: [https://developer.wordpress.com/docs/api/](https://developer.wordpress.com/docs/api/) + static let wordPressCom: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ" + formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone + formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale + return formatter + }() +} diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+RFC3339.swift index f03e9b0c..c0ee39bd 100644 --- a/WordPressKit/NSDate+RFC3339.swift +++ b/WordPressKit/NSDate+RFC3339.swift @@ -2,7 +2,7 @@ import Foundation extension NSDate { - static let rfc3339Formatter = DateFormatter.rfc3339Formatter + static let rfc3339Formatter = DateFormatter.wordPressCom /// Parses a date string /// @@ -24,14 +24,3 @@ extension NSDate { NSDate.rfc3339Formatter.string(from: self as Date) } } - -extension DateFormatter { - - static let rfc3339Formatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ" - formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone - formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale - return formatter - }() -} diff --git a/WordPressKitTests/DateFormatter+WordPressComTests.swift b/WordPressKitTests/DateFormatter+WordPressComTests.swift new file mode 100644 index 00000000..e8642fab --- /dev/null +++ b/WordPressKitTests/DateFormatter+WordPressComTests.swift @@ -0,0 +1,13 @@ +@testable import WordPressKit +import XCTest + +class DateFormatterWordPressComTests: XCTestCase { + + func testDateFormatterConfiguration() throws { + let rfc3339Formatter = try XCTUnwrap(DateFormatter.wordPressCom) + + XCTAssertEqual(rfc3339Formatter.timeZone, TimeZone(secondsFromGMT: 0)) + XCTAssertEqual(rfc3339Formatter.locale, Locale(identifier: "en_US_POSIX")) + XCTAssertEqual(rfc3339Formatter.dateFormat, "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ") + } +} diff --git a/WordPressKitTests/NSDate+RFC3339Tests.swift b/WordPressKitTests/NSDate+RFC3339Tests.swift index 26c0fc53..b3359993 100644 --- a/WordPressKitTests/NSDate+RFC3339Tests.swift +++ b/WordPressKitTests/NSDate+RFC3339Tests.swift @@ -9,14 +9,6 @@ import XCTest // - https://datatracker.ietf.org/doc/html/rfc3339 class NSDateRFC3339Tests: XCTestCase { - func testDateFormatterConfiguration() throws { - let rfc3339Formatter = try XCTUnwrap(NSDate.rfc3339Formatter) - - XCTAssertEqual(rfc3339Formatter.timeZone, TimeZone(secondsFromGMT: 0)) - XCTAssertEqual(rfc3339Formatter.locale, Locale(identifier: "en_US_POSIX")) - XCTAssertEqual(rfc3339Formatter.dateFormat, "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ") - } - func testValidRFC3339DateFromString() { XCTAssertEqual( NSDate.with(wordPressComJSONString: "2023-03-19T15:00:00Z"), From e4f8fba16db5945162f5d068c708de1f3b0bb372 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 21 Mar 2024 10:25:23 +1100 Subject: [PATCH 11/12] =?UTF-8?q?Breaking=20change=20=E2=80=93=20Move=20`N?= =?UTF-8?q?SDate`=20WordPress.com=20implementation=20to=20`Date`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The breaking change is actually in having removed `NSDate.rfc3339Formatter`, which is no longer necessary. --- WordPressKit.xcodeproj/project.pbxproj | 24 ++++++++----- WordPressKit/Date+WordPressCom.swift | 19 ++++++++++ ...FC3339.swift => NSDate+WordPressCom.swift} | 12 ++++--- .../PostServiceRemoteREST+Extended.swift | 2 +- .../Date+WordPressComTests.swift | 36 +++++++++++++++++++ ...s.swift => NSDate+WordPressComTests.swift} | 8 +---- 6 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 WordPressKit/Date+WordPressCom.swift rename WordPressKit/{NSDate+RFC3339.swift => NSDate+WordPressCom.swift} (63%) create mode 100644 WordPressKitTests/Date+WordPressComTests.swift rename WordPressKitTests/{NSDate+RFC3339Tests.swift => NSDate+WordPressComTests.swift} (80%) diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index cf1667d8..c5e75da7 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -71,10 +71,12 @@ 3F758FD324F6C68200BBA2FC /* AnnouncementServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */; }; 3F8308A729EE683500354497 /* ActivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8308A629EE683500354497 /* ActivityTests.swift */; }; 3FB8642C2888089F003A86BE /* BuildkiteTestCollector in Frameworks */ = {isa = PBXBuildFile; productRef = 3FB8642B2888089F003A86BE /* BuildkiteTestCollector */; }; - 3FFCC0412BA995290051D229 /* NSDate+RFC3339Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */; }; - 3FFCC0472BAA6EF40051D229 /* NSDate+RFC3339.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */; }; + 3FFCC0412BA995290051D229 /* Date+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */; }; + 3FFCC0472BAA6EF40051D229 /* NSDate+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */; }; 3FFCC0492BAB98130051D229 /* DateFormatter+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */; }; 3FFCC04B2BABA5220051D229 /* DateFormatter+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */; }; + 3FFCC04D2BABA6980051D229 /* NSDate+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC04C2BABA6980051D229 /* NSDate+WordPressComTests.swift */; }; + 3FFCC04F2BABA6E60051D229 /* Date+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC04E2BABA6E60051D229 /* Date+WordPressCom.swift */; }; 40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; }; 40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; }; 404057C5221B30400060250C /* StatsSearchTermTimeIntervalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */; }; @@ -800,10 +802,12 @@ 3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementServiceRemote.swift; sourceTree = ""; }; 3F8308A629EE683500354497 /* ActivityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTests.swift; sourceTree = ""; }; 3FB8642D288813E9003A86BE /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; - 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+RFC3339Tests.swift"; sourceTree = ""; }; - 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+RFC3339.swift"; sourceTree = ""; }; + 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+WordPressComTests.swift"; sourceTree = ""; }; + 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+WordPressCom.swift"; sourceTree = ""; }; 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressCom.swift"; sourceTree = ""; }; 3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressComTests.swift"; sourceTree = ""; }; + 3FFCC04C2BABA6980051D229 /* NSDate+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+WordPressComTests.swift"; sourceTree = ""; }; + 3FFCC04E2BABA6E60051D229 /* Date+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+WordPressCom.swift"; sourceTree = ""; }; 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = ""; }; 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = ""; }; 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSearchTermTimeIntervalData.swift; sourceTree = ""; }; @@ -1908,7 +1912,8 @@ FFA4D4A82423B10A00BF5180 /* WordPressOrgRestApiTests.swift */, 74B335DB1F06F4180053A184 /* WordPressOrgXMLRPCApiTests.swift */, 740B23D51F17F7C100067A2A /* XMLRPCTestable.swift */, - 3FFCC0402BA995290051D229 /* NSDate+RFC3339Tests.swift */, + 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */, + 3FFCC04C2BABA6980051D229 /* NSDate+WordPressComTests.swift */, 3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */, ); name = Tests; @@ -2524,7 +2529,8 @@ 4A05E7952B2FCB6400C25E3B /* NonceRetrieval.swift */, 4A05E7992B2FDC3200C25E3B /* WordPressOrgRestApi.swift */, 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */, - 3FFCC0462BAA6EF40051D229 /* NSDate+RFC3339.swift */, + 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */, + 3FFCC04E2BABA6E60051D229 /* Date+WordPressCom.swift */, 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */, 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */, 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */, @@ -3398,6 +3404,7 @@ 93BD277F1EE73944002BB00B /* WordPressComOAuthClient.swift in Sources */, 740B23B91F17EC7300067A2A /* PostServiceRemoteREST.m in Sources */, 93BD27801EE73944002BB00B /* WordPressComRestApi.swift in Sources */, + 3FFCC04F2BABA6E60051D229 /* Date+WordPressCom.swift in Sources */, 404057D2221C56AB0060250C /* StatsTopCountryTimeIntervalData.swift in Sources */, E11C2AD21FA77FB90023BDE2 /* SitePlugin.swift in Sources */, 4A68E3DF29407100004AC3DC /* RemoteReaderTopic.swift in Sources */, @@ -3463,7 +3470,7 @@ E632D7781F6E047400297F6D /* SocialLogin2FANonceInfo.swift in Sources */, 32FC1D29255C91ED00CD0A7B /* JetpackScanServiceRemote.swift in Sources */, 9F3E0B9B208732B3009CB5BA /* RemoteReaderSiteInfoSubscription.swift in Sources */, - 3FFCC0472BAA6EF40051D229 /* NSDate+RFC3339.swift in Sources */, + 3FFCC0472BAA6EF40051D229 /* NSDate+WordPressCom.swift in Sources */, 7403A2E41EF06ED500DED7DC /* AccountSettingsRemote.swift in Sources */, 3236F77824AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift in Sources */, FE20A6A4282A96C00025E975 /* RemoteBloggingPromptsSettings.swift in Sources */, @@ -3617,12 +3624,13 @@ F4B0F4802ACB4EA9003ABC61 /* AllDomainsResultDomainTests.swift in Sources */, 74585B901F0D51F900E7E667 /* DomainsServiceRemoteRESTTests.swift in Sources */, BAFA775624ADAB3C000F0D3A /* MockPluginDirectoryEntryProvider.swift in Sources */, - 3FFCC0412BA995290051D229 /* NSDate+RFC3339Tests.swift in Sources */, + 3FFCC0412BA995290051D229 /* Date+WordPressComTests.swift in Sources */, 3F8308A729EE683500354497 /* ActivityTests.swift in Sources */, 9AB6D64A218727D60008F274 /* PostServiceRemoteRESTRevisionsTest.swift in Sources */, 01438D382B6A35FB0097D60A /* stats-summary.json in Sources */, 7430C9BD1F192C0F0051B8E6 /* ReaderPostServiceRemoteTests.m in Sources */, 1DC837C229B9F04F009DCD4B /* RemoteVideoPressVideoTests.swift in Sources */, + 3FFCC04D2BABA6980051D229 /* NSDate+WordPressComTests.swift in Sources */, FAD1345125909DEA00A8FEB1 /* JetpackBackupServiceRemoteTests.swift in Sources */, 8B2F4BE924ABC9DC0056C08A /* ReaderPostServiceRemote+CardsTests.swift in Sources */, 40F9880C221ACEEE00B7B369 /* StatsRemoteV2Tests.swift in Sources */, diff --git a/WordPressKit/Date+WordPressCom.swift b/WordPressKit/Date+WordPressCom.swift new file mode 100644 index 00000000..5619c59b --- /dev/null +++ b/WordPressKit/Date+WordPressCom.swift @@ -0,0 +1,19 @@ +extension Date { + + /// Parses a date string + /// + /// Dates in the format specified in http://www.w3.org/TR/NOTE-datetime should be OK. + /// The kind of dates returned by the REST API should match that format, even if the doc promises ISO 8601. + /// + /// Parsing the full ISO 8601, or even RFC 3339 is more complex than this, and makes no sense right now. + /// + /// - SeeAlso: [WordPress.com REST API docs](https://developer.wordpress.com/docs/api/) + /// - Warning: This method doesn't support fractional seconds or dates with leap seconds (23:59:60 turns into 23:59:00) + static func with(wordPressComJSONString jsonString: String) -> Date? { + DateFormatter.wordPressCom.date(from: jsonString) + } + + var wordPressComJSONString: String { + DateFormatter.wordPressCom.string(from: self) + } +} diff --git a/WordPressKit/NSDate+RFC3339.swift b/WordPressKit/NSDate+WordPressCom.swift similarity index 63% rename from WordPressKit/NSDate+RFC3339.swift rename to WordPressKit/NSDate+WordPressCom.swift index c0ee39bd..2170399c 100644 --- a/WordPressKit/NSDate+RFC3339.swift +++ b/WordPressKit/NSDate+WordPressCom.swift @@ -1,9 +1,12 @@ import Foundation +// This `NSDate` extension wraps the `Date` implementation. +// +// It's done in two types because we cannot expose the `Date` methods to Objective-C, since `Date` is not a class: +// +// `@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes` extension NSDate { - static let rfc3339Formatter = DateFormatter.wordPressCom - /// Parses a date string /// /// Dates in the format specified in http://www.w3.org/TR/NOTE-datetime should be OK. @@ -11,16 +14,17 @@ extension NSDate { /// /// Parsing the full ISO 8601, or even RFC 3339 is more complex than this, and makes no sense right now. /// + /// - SeeAlso: [WordPress.com REST API docs](https://developer.wordpress.com/docs/api/) /// - Warning: This method doesn't support fractional seconds or dates with leap seconds (23:59:60 turns into 23:59:00) // // Needs to be `public` because of the usages in the Objective-C code. @objc(dateWithWordPressComJSONString:) public static func with(wordPressComJSONString jsonString: String) -> Date? { - self.rfc3339Formatter.date(from: jsonString) + Date.with(wordPressComJSONString: jsonString) } @objc(WordPressComJSONString) public func wordPressComJSONString() -> String { - NSDate.rfc3339Formatter.string(from: self as Date) + (self as Date).wordPressComJSONString } } diff --git a/WordPressKit/PostServiceRemoteREST+Extended.swift b/WordPressKit/PostServiceRemoteREST+Extended.swift index e4937aca..0af670fb 100644 --- a/WordPressKit/PostServiceRemoteREST+Extended.swift +++ b/WordPressKit/PostServiceRemoteREST+Extended.swift @@ -40,7 +40,7 @@ private func decodePost(from object: AnyObject) async throws -> RemotePost { private func makeParameters(from value: T) throws -> [String: AnyObject] { let encoder = JSONEncoder() - encoder.dateEncodingStrategy = .formatted(NSDate.rfc3339Formatter) + encoder.dateEncodingStrategy = .formatted(.wordPressCom) let data = try encoder.encode(value) let object = try JSONSerialization.jsonObject(with: data) guard let dictionary = object as? [String: AnyObject] else { diff --git a/WordPressKitTests/Date+WordPressComTests.swift b/WordPressKitTests/Date+WordPressComTests.swift new file mode 100644 index 00000000..92c8cb5e --- /dev/null +++ b/WordPressKitTests/Date+WordPressComTests.swift @@ -0,0 +1,36 @@ +@testable import WordPressKit +import XCTest + +// This is an incomplete test for implementing RFC 3339. +// It's purpose is to ensure our code "works". +// +// See also: +// +// - https://developer.wordpress.com/docs/api/ +// - https://datatracker.ietf.org/doc/html/rfc3339 +class DateWordPressComTests: XCTestCase { + + func testValidRFC3339DateFromString() { + XCTAssertEqual( + Date.with(wordPressComJSONString: "2023-03-19T15:00:00Z"), + Date(timeIntervalSince1970: 1_679_238_000) + ) + } + + func testInvalidRFC3339DateFromString() { + XCTAssertNil(Date.with(wordPressComJSONString: "2024-01-01")) + } + + func testInvalidDateFromString() { + XCTAssertNil(Date.with(wordPressComJSONString: "not a date")) + } + + func testValidRFC3339StringFromDate() { + XCTAssertEqual( + Date(timeIntervalSince1970: 1_679_238_000).wordPressComJSONString, + // Apparently, NSDateFormatter doesn't offer a way to specify Z vs +0000. + // This might go all the way back to the ISO 8601 and RFC 3339 specs overlap. + "2023-03-19T15:00:00+0000" + ) + } +} diff --git a/WordPressKitTests/NSDate+RFC3339Tests.swift b/WordPressKitTests/NSDate+WordPressComTests.swift similarity index 80% rename from WordPressKitTests/NSDate+RFC3339Tests.swift rename to WordPressKitTests/NSDate+WordPressComTests.swift index b3359993..50c3e211 100644 --- a/WordPressKitTests/NSDate+RFC3339Tests.swift +++ b/WordPressKitTests/NSDate+WordPressComTests.swift @@ -1,13 +1,7 @@ @testable import WordPressKit import XCTest -// This is an incomplete test for implementing RFC 3339. -// It's purpose is to ensure our code "works". -// -// See also: -// -// - https://datatracker.ietf.org/doc/html/rfc3339 -class NSDateRFC3339Tests: XCTestCase { +class NSDateWordPressComTests: XCTestCase { func testValidRFC3339DateFromString() { XCTAssertEqual( From 4ab30eefa687c6389b401f0f95a0d9c1b7723223 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 21 Mar 2024 17:59:04 +1100 Subject: [PATCH 12/12] Add `CHANGELOG` entry for reworked WP.com `NSDate` conversion --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dd443dc..543863c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ _None._ ### Breaking Changes -_None._ +- Reworked the `NSDate` RFC3339 / WordPress.com JSON conversions API [#759] ### New Features