Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 118fd9a

Browse files
authored
Convert "WordPress JSON date" logic from Objective-C to Swift (#758)
2 parents 7a19262 + 4ab30ee commit 118fd9a

17 files changed

+172
-64
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ _None._
3434

3535
### Breaking Changes
3636

37-
_None._
37+
- Reworked the `NSDate` RFC3339 / WordPress.com JSON conversions API [#759]
3838

3939
### New Features
4040

WordPressKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Pod::Spec.new do |s|
44
s.name = 'WordPressKit'
5-
s.version = '14.1.0'
5+
s.version = '15.0.0-beta.1'
66

77
s.summary = 'WordPressKit offers a clean and simple WordPress.com and WordPress.org API.'
88
s.description = <<-DESC

WordPressKit.xcodeproj/project.pbxproj

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@
7171
3F758FD324F6C68200BBA2FC /* AnnouncementServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */; };
7272
3F8308A729EE683500354497 /* ActivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8308A629EE683500354497 /* ActivityTests.swift */; };
7373
3FB8642C2888089F003A86BE /* BuildkiteTestCollector in Frameworks */ = {isa = PBXBuildFile; productRef = 3FB8642B2888089F003A86BE /* BuildkiteTestCollector */; };
74+
3FFCC0412BA995290051D229 /* Date+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */; };
75+
3FFCC0472BAA6EF40051D229 /* NSDate+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */; };
76+
3FFCC0492BAB98130051D229 /* DateFormatter+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */; };
77+
3FFCC04B2BABA5220051D229 /* DateFormatter+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */; };
78+
3FFCC04D2BABA6980051D229 /* NSDate+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC04C2BABA6980051D229 /* NSDate+WordPressComTests.swift */; };
79+
3FFCC04F2BABA6E60051D229 /* Date+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC04E2BABA6E60051D229 /* Date+WordPressCom.swift */; };
7480
40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; };
7581
40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; };
7682
404057C5221B30400060250C /* StatsSearchTermTimeIntervalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */; };
@@ -485,8 +491,6 @@
485491
93BD27711EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 93BD276D1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h */; settings = {ATTRIBUTES = (Public, ); }; };
486492
93BD27721EE737A9002BB00B /* ServiceRemoteWordPressXMLRPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BD276E1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.m */; };
487493
93BD277C1EE73944002BB00B /* HTTPAuthenticationAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */; };
488-
93BD277D1EE73944002BB00B /* NSDate+WordPressJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 93BD27751EE73944002BB00B /* NSDate+WordPressJSON.h */; settings = {ATTRIBUTES = (Public, ); }; };
489-
93BD277E1EE73944002BB00B /* NSDate+WordPressJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27761EE73944002BB00B /* NSDate+WordPressJSON.m */; };
490494
93BD277F1EE73944002BB00B /* WordPressComOAuthClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */; };
491495
93BD27801EE73944002BB00B /* WordPressComRestApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */; };
492496
93BD27811EE73944002BB00B /* WordPressOrgXMLRPCApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */; };
@@ -798,6 +802,12 @@
798802
3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementServiceRemote.swift; sourceTree = "<group>"; };
799803
3F8308A629EE683500354497 /* ActivityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTests.swift; sourceTree = "<group>"; };
800804
3FB8642D288813E9003A86BE /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = "<group>"; };
805+
3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+WordPressComTests.swift"; sourceTree = "<group>"; };
806+
3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+WordPressCom.swift"; sourceTree = "<group>"; };
807+
3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressCom.swift"; sourceTree = "<group>"; };
808+
3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressComTests.swift"; sourceTree = "<group>"; };
809+
3FFCC04C2BABA6980051D229 /* NSDate+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+WordPressComTests.swift"; sourceTree = "<group>"; };
810+
3FFCC04E2BABA6E60051D229 /* Date+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+WordPressCom.swift"; sourceTree = "<group>"; };
801811
40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = "<group>"; };
802812
40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = "<group>"; };
803813
404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSearchTermTimeIntervalData.swift; sourceTree = "<group>"; };
@@ -1218,8 +1228,6 @@
12181228
93BD276D1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServiceRemoteWordPressXMLRPC.h; sourceTree = "<group>"; };
12191229
93BD276E1EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServiceRemoteWordPressXMLRPC.m; sourceTree = "<group>"; };
12201230
93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPAuthenticationAlertController.swift; sourceTree = "<group>"; };
1221-
93BD27751EE73944002BB00B /* NSDate+WordPressJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+WordPressJSON.h"; sourceTree = "<group>"; };
1222-
93BD27761EE73944002BB00B /* NSDate+WordPressJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+WordPressJSON.m"; sourceTree = "<group>"; };
12231231
93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordPressComOAuthClient.swift; sourceTree = "<group>"; };
12241232
93BD27781EE73944002BB00B /* WordPressComRestApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordPressComRestApi.swift; sourceTree = "<group>"; };
12251233
93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordPressOrgXMLRPCApi.swift; sourceTree = "<group>"; };
@@ -1904,6 +1912,9 @@
19041912
FFA4D4A82423B10A00BF5180 /* WordPressOrgRestApiTests.swift */,
19051913
74B335DB1F06F4180053A184 /* WordPressOrgXMLRPCApiTests.swift */,
19061914
740B23D51F17F7C100067A2A /* XMLRPCTestable.swift */,
1915+
3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */,
1916+
3FFCC04C2BABA6980051D229 /* NSDate+WordPressComTests.swift */,
1917+
3FFCC04A2BABA5220051D229 /* DateFormatter+WordPressComTests.swift */,
19071918
);
19081919
name = Tests;
19091920
sourceTree = "<group>";
@@ -2518,8 +2529,9 @@
25182529
4A05E7952B2FCB6400C25E3B /* NonceRetrieval.swift */,
25192530
4A05E7992B2FDC3200C25E3B /* WordPressOrgRestApi.swift */,
25202531
93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */,
2521-
93BD27751EE73944002BB00B /* NSDate+WordPressJSON.h */,
2522-
93BD27761EE73944002BB00B /* NSDate+WordPressJSON.m */,
2532+
3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */,
2533+
3FFCC04E2BABA6E60051D229 /* Date+WordPressCom.swift */,
2534+
3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */,
25232535
93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */,
25242536
93BD27781EE73944002BB00B /* WordPressComRestApi.swift */,
25252537
93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */,
@@ -2797,7 +2809,6 @@
27972809
93BD273C1EE73282002BB00B /* AccountServiceRemoteREST.h in Headers */,
27982810
93BD27711EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h in Headers */,
27992811
93BD276F1EE737A8002BB00B /* ServiceRemoteWordPressComREST.h in Headers */,
2800-
93BD277D1EE73944002BB00B /* NSDate+WordPressJSON.h in Headers */,
28012812
93BD273B1EE73282002BB00B /* AccountServiceRemote.h in Headers */,
28022813
93BD27691EE736A8002BB00B /* RemoteUser.h in Headers */,
28032814
74B5F0DC1EF829B800B411E7 /* BlogServiceRemote.h in Headers */,
@@ -3393,6 +3404,7 @@
33933404
93BD277F1EE73944002BB00B /* WordPressComOAuthClient.swift in Sources */,
33943405
740B23B91F17EC7300067A2A /* PostServiceRemoteREST.m in Sources */,
33953406
93BD27801EE73944002BB00B /* WordPressComRestApi.swift in Sources */,
3407+
3FFCC04F2BABA6E60051D229 /* Date+WordPressCom.swift in Sources */,
33963408
404057D2221C56AB0060250C /* StatsTopCountryTimeIntervalData.swift in Sources */,
33973409
E11C2AD21FA77FB90023BDE2 /* SitePlugin.swift in Sources */,
33983410
4A68E3DF29407100004AC3DC /* RemoteReaderTopic.swift in Sources */,
@@ -3427,7 +3439,6 @@
34273439
9311A68B1F22625A00704AC9 /* TaxonomyServiceRemoteXMLRPC.m in Sources */,
34283440
C797196E2679007B0072F984 /* SelfHostedPluginManagementClient.swift in Sources */,
34293441
40414060220F9F1F00CF7C5B /* StatsAllTimesInsight.swift in Sources */,
3430-
93BD277E1EE73944002BB00B /* NSDate+WordPressJSON.m in Sources */,
34313442
8B2F4BED24ABCAEF0056C08A /* Decodable+Dictionary.swift in Sources */,
34323443
E194CB731FBDEF6500B0A8B8 /* PluginState.swift in Sources */,
34333444
4A57A6882B54C68C008D0660 /* Constants.m in Sources */,
@@ -3459,6 +3470,7 @@
34593470
E632D7781F6E047400297F6D /* SocialLogin2FANonceInfo.swift in Sources */,
34603471
32FC1D29255C91ED00CD0A7B /* JetpackScanServiceRemote.swift in Sources */,
34613472
9F3E0B9B208732B3009CB5BA /* RemoteReaderSiteInfoSubscription.swift in Sources */,
3473+
3FFCC0472BAA6EF40051D229 /* NSDate+WordPressCom.swift in Sources */,
34623474
7403A2E41EF06ED500DED7DC /* AccountSettingsRemote.swift in Sources */,
34633475
3236F77824AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift in Sources */,
34643476
FE20A6A4282A96C00025E975 /* RemoteBloggingPromptsSettings.swift in Sources */,
@@ -3517,6 +3529,7 @@
35173529
74C473AC1EF2F75E009918F2 /* SiteManagementServiceRemote.swift in Sources */,
35183530
74585B971F0D54B400E7E667 /* RemoteDomain.swift in Sources */,
35193531
0C1C08432B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift in Sources */,
3532+
3FFCC0492BAB98130051D229 /* DateFormatter+WordPressCom.swift in Sources */,
35203533
74A44DD01F13C64B006CD8F4 /* RemoteNotification.swift in Sources */,
35213534
8B52B901257AC5A200221663 /* Date+endOfDay.swift in Sources */,
35223535
E1D6B558200E473A00325669 /* TimeZoneServiceRemote.swift in Sources */,
@@ -3611,11 +3624,13 @@
36113624
F4B0F4802ACB4EA9003ABC61 /* AllDomainsResultDomainTests.swift in Sources */,
36123625
74585B901F0D51F900E7E667 /* DomainsServiceRemoteRESTTests.swift in Sources */,
36133626
BAFA775624ADAB3C000F0D3A /* MockPluginDirectoryEntryProvider.swift in Sources */,
3627+
3FFCC0412BA995290051D229 /* Date+WordPressComTests.swift in Sources */,
36143628
3F8308A729EE683500354497 /* ActivityTests.swift in Sources */,
36153629
9AB6D64A218727D60008F274 /* PostServiceRemoteRESTRevisionsTest.swift in Sources */,
36163630
01438D382B6A35FB0097D60A /* stats-summary.json in Sources */,
36173631
7430C9BD1F192C0F0051B8E6 /* ReaderPostServiceRemoteTests.m in Sources */,
36183632
1DC837C229B9F04F009DCD4B /* RemoteVideoPressVideoTests.swift in Sources */,
3633+
3FFCC04D2BABA6980051D229 /* NSDate+WordPressComTests.swift in Sources */,
36193634
FAD1345125909DEA00A8FEB1 /* JetpackBackupServiceRemoteTests.swift in Sources */,
36203635
8B2F4BE924ABC9DC0056C08A /* ReaderPostServiceRemote+CardsTests.swift in Sources */,
36213636
40F9880C221ACEEE00B7B369 /* StatsRemoteV2Tests.swift in Sources */,
@@ -3643,6 +3658,7 @@
36433658
4A05E79C2B2FDC6100C25E3B /* WordPressOrgAPITests.swift in Sources */,
36443659
3297E1DE2564653A00287D21 /* JetpackScanServiceRemoteTests.swift in Sources */,
36453660
01438D352B6A2B2C0097D60A /* stats-visits-month-unit-week.json in Sources */,
3661+
3FFCC04B2BABA5220051D229 /* DateFormatter+WordPressComTests.swift in Sources */,
36463662
9F3E0BAC20873785009CB5BA /* ServiceRequestTest.swift in Sources */,
36473663
4624223E2548C26D002B8A12 /* SiteDesignServiceRemoteTests.swift in Sources */,
36483664
4A1DEF44293051BC00322608 /* LoggingTests.swift in Sources */,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
extension Date {
2+
3+
/// Parses a date string
4+
///
5+
/// Dates in the format specified in http://www.w3.org/TR/NOTE-datetime should be OK.
6+
/// The kind of dates returned by the REST API should match that format, even if the doc promises ISO 8601.
7+
///
8+
/// Parsing the full ISO 8601, or even RFC 3339 is more complex than this, and makes no sense right now.
9+
///
10+
/// - SeeAlso: [WordPress.com REST API docs](https://developer.wordpress.com/docs/api/)
11+
/// - Warning: This method doesn't support fractional seconds or dates with leap seconds (23:59:60 turns into 23:59:00)
12+
static func with(wordPressComJSONString jsonString: String) -> Date? {
13+
DateFormatter.wordPressCom.date(from: jsonString)
14+
}
15+
16+
var wordPressComJSONString: String {
17+
DateFormatter.wordPressCom.string(from: self)
18+
}
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
extension DateFormatter {
2+
3+
/// A `DateFormatter` configured to manage dates compatible with the WordPress.com API.
4+
///
5+
/// - SeeAlso: [https://developer.wordpress.com/docs/api/](https://developer.wordpress.com/docs/api/)
6+
static let wordPressCom: DateFormatter = {
7+
let formatter = DateFormatter()
8+
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
9+
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone
10+
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
11+
return formatter
12+
}()
13+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Foundation
2+
3+
// This `NSDate` extension wraps the `Date` implementation.
4+
//
5+
// It's done in two types because we cannot expose the `Date` methods to Objective-C, since `Date` is not a class:
6+
//
7+
// `@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes`
8+
extension NSDate {
9+
10+
/// Parses a date string
11+
///
12+
/// Dates in the format specified in http://www.w3.org/TR/NOTE-datetime should be OK.
13+
/// The kind of dates returned by the REST API should match that format, even if the doc promises ISO 8601.
14+
///
15+
/// Parsing the full ISO 8601, or even RFC 3339 is more complex than this, and makes no sense right now.
16+
///
17+
/// - SeeAlso: [WordPress.com REST API docs](https://developer.wordpress.com/docs/api/)
18+
/// - Warning: This method doesn't support fractional seconds or dates with leap seconds (23:59:60 turns into 23:59:00)
19+
//
20+
// Needs to be `public` because of the usages in the Objective-C code.
21+
@objc(dateWithWordPressComJSONString:)
22+
public static func with(wordPressComJSONString jsonString: String) -> Date? {
23+
Date.with(wordPressComJSONString: jsonString)
24+
}
25+
26+
@objc(WordPressComJSONString)
27+
public func wordPressComJSONString() -> String {
28+
(self as Date).wordPressComJSONString
29+
}
30+
}

WordPressKit/NSDate+WordPressJSON.h

Lines changed: 0 additions & 21 deletions
This file was deleted.

WordPressKit/NSDate+WordPressJSON.m

Lines changed: 0 additions & 26 deletions
This file was deleted.

WordPressKit/PostServiceRemoteREST+Extended.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ private func decodePost(from object: AnyObject) async throws -> RemotePost {
4040

4141
private func makeParameters<T: Encodable>(from value: T) throws -> [String: AnyObject] {
4242
let encoder = JSONEncoder()
43-
encoder.dateEncodingStrategy = .formatted(NSDate.rfc3339DateFormatter())
43+
encoder.dateEncodingStrategy = .formatted(.wordPressCom)
4444
let data = try encoder.encode(value)
4545
let object = try JSONSerialization.jsonObject(with: data)
4646
guard let dictionary = object as? [String: AnyObject] else {

WordPressKit/RemoteCommentV2.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ extension RemoteCommentV2: Decodable {
6565

6666
// since `date_gmt` is already in GMT timezone, manually add the timezone string to make the rfc3339 formatter happy (or it will throw otherwise).
6767
guard let dateString = try? container.decode(String.self, forKey: .date),
68-
let date = NSDate(wordPressComJSONString: dateString + "+00:00") as Date? else {
68+
let date = NSDate.with(wordPressComJSONString: dateString + "+00:00") else {
6969
throw DecodingError.dataCorruptedError(forKey: .date, in: container, debugDescription: "Date parsing failed")
7070
}
7171
self.date = date

0 commit comments

Comments
 (0)