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

Commit fe8537f

Browse files
authored
Remove redundant JSON encoding and decoding (#722)
2 parents 92c1536 + 66e5392 commit fe8537f

10 files changed

+120
-106
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ _None._
5050
- When enabled, `WrodPressOrgXMLRPCApi` sends HTTP requests using URLSession instead of Alamofire. [#719]
5151
- Refactor API requests that ask for SMS code during WP.com authentication. [#683]
5252
- Refactor BlazeServiceRemote to use URLSession-backed API. [#721]
53+
- Refactor some WP.com API endpoints to use URLSession-backed API. [#722]
5354

5455
## 13.0.0
5556

WordPressKit/BloggingPromptsServiceRemote.swift

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,23 @@ open class BloggingPromptsServiceRemote: ServiceRemoteWordPressComREST {
4343
return params
4444
}()
4545

46-
wordPressComRestApi.GET(path, parameters: requestParameter as [String: AnyObject]) { result, _ in
47-
switch result {
48-
case .success(let responseObject):
49-
do {
50-
let data = try JSONSerialization.data(withJSONObject: responseObject, options: [])
51-
let decoder = JSONDecoder.apiDecoder
52-
// our API decoder assumes that we're converting from snake case.
53-
// revert it to default so the CodingKeys match the actual response keys.
54-
decoder.keyDecodingStrategy = .useDefaultKeys
55-
let response = try decoder.decode([String: [RemoteBloggingPrompt]].self, from: data)
56-
completion(.success(response.values.first ?? []))
57-
} catch {
58-
completion(.failure(error))
59-
}
60-
61-
case .failure(let error):
62-
completion(.failure(error))
63-
}
46+
let decoder = JSONDecoder.apiDecoder
47+
// our API decoder assumes that we're converting from snake case.
48+
// revert it to default so the CodingKeys match the actual response keys.
49+
decoder.keyDecodingStrategy = .useDefaultKeys
50+
51+
Task { @MainActor in
52+
await self.wordPressComRestApi
53+
.perform(
54+
.get,
55+
URLString: path,
56+
parameters: requestParameter as [String: AnyObject],
57+
jsonDecoder: decoder,
58+
type: [String: [RemoteBloggingPrompt]].self
59+
)
60+
.map { $0.body.values.first ?? [] }
61+
.mapError { error -> Error in error.asNSError() }
62+
.execute(completion)
6463
}
6564
}
6665

@@ -71,19 +70,11 @@ open class BloggingPromptsServiceRemote: ServiceRemoteWordPressComREST {
7170
/// - completion: Closure that will be called when the request completes.
7271
open func fetchSettings(for siteID: NSNumber, completion: @escaping (Result<RemoteBloggingPromptsSettings, Error>) -> Void) {
7372
let path = path(forEndpoint: "sites/\(siteID)/blogging-prompts/settings", withVersion: ._2_0)
74-
wordPressComRestApi.GET(path, parameters: nil) { result, _ in
75-
switch result {
76-
case .success(let responseObject):
77-
do {
78-
let data = try JSONSerialization.data(withJSONObject: responseObject)
79-
let settings = try JSONDecoder().decode(RemoteBloggingPromptsSettings.self, from: data)
80-
completion(.success(settings))
81-
} catch {
82-
completion(.failure(error))
83-
}
84-
case .failure(let error):
85-
completion(.failure(error))
86-
}
73+
Task { @MainActor in
74+
await self.wordPressComRestApi.perform(.get, URLString: path, type: RemoteBloggingPromptsSettings.self)
75+
.map { $0.body }
76+
.mapError { error -> Error in error.asNSError() }
77+
.execute(completion)
8778
}
8879
}
8980

WordPressKit/CommentServiceRemoteREST+ApiV2.swift

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,17 @@ public extension CommentServiceRemoteREST {
3636
}
3737
}()
3838

39-
wordPressComRestApi.GET(path, parameters: requestParameters as [String: AnyObject]) { result, _ in
40-
switch result {
41-
case .success(let responseObject):
42-
do {
43-
let data = try JSONSerialization.data(withJSONObject: responseObject, options: [])
44-
let comments = try JSONDecoder().decode([RemoteCommentV2].self, from: data)
45-
success(comments)
46-
} catch {
47-
failure(error)
48-
}
49-
50-
case .failure(let error):
51-
failure(error)
52-
}
39+
Task { @MainActor in
40+
await self.wordPressComRestApi
41+
.perform(
42+
.get,
43+
URLString: path,
44+
parameters: requestParameters as [String: AnyObject],
45+
type: [RemoteCommentV2].self
46+
)
47+
.map { $0.body }
48+
.mapError { error -> Error in error.asNSError() }
49+
.execute(onSuccess: success, onFailure: failure)
5350
}
5451
}
5552

WordPressKit/Domains/DomainsServiceRemote+AllDomains.swift

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extension DomainsServiceRemote {
1212
/// - `locale` of type `string`. Used for string localization.
1313
public func fetchAllDomains(params: AllDomainsEndpointParams? = nil, completion: @escaping (AllDomainsEndpointResult) -> Void) {
1414
let path = self.path(forEndpoint: "all-domains", withVersion: ._1_1)
15-
var parameters: [String: AnyObject]?
15+
let parameters: [String: AnyObject]?
1616

1717
do {
1818
parameters = try queryParameters(from: params)
@@ -21,21 +21,20 @@ extension DomainsServiceRemote {
2121
return
2222
}
2323

24-
self.wordPressComRestApi.GET(path, parameters: parameters) { result, _ in
25-
do {
26-
switch result {
27-
case .success(let result):
28-
let data = try JSONSerialization.data(withJSONObject: result, options: [])
29-
let decoder = JSONDecoder()
30-
decoder.dateDecodingStrategy = .iso8601
31-
let result = try decoder.decode(AllDomainsEndpointResponse.self, from: data)
32-
completion(.success(result.domains))
33-
case .failure(let error):
34-
throw error
35-
}
36-
} catch let error {
37-
completion(.failure(error))
38-
}
24+
let decoder = JSONDecoder()
25+
decoder.dateDecodingStrategy = .iso8601
26+
Task { @MainActor in
27+
await self.wordPressComRestApi
28+
.perform(
29+
.get,
30+
URLString: path,
31+
parameters: parameters,
32+
jsonDecoder: decoder,
33+
type: AllDomainsEndpointResponse.self
34+
)
35+
.map { $0.body.domains }
36+
.mapError { error -> Error in error.asNSError() }
37+
.execute(completion)
3938
}
4039
}
4140

WordPressKit/JetpackSocialServiceRemote.swift

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,22 @@ public class JetpackSocialServiceRemote: ServiceRemoteWordPressComREST {
1111
public func fetchPublicizeInfo(for siteID: Int,
1212
completion: @escaping (Result<RemotePublicizeInfo?, Error>) -> Void) {
1313
let path = path(forEndpoint: "sites/\(siteID)/jetpack-social", withVersion: ._2_0)
14-
wordPressComRestApi.GET(path, parameters: nil) { result, response in
15-
switch result {
16-
case .success(let responseObject):
17-
do {
18-
let data = try JSONSerialization.data(withJSONObject: responseObject)
19-
let info = try? JSONDecoder.apiDecoder.decode(RemotePublicizeInfo.self, from: data)
20-
completion(.success(info))
21-
} catch {
22-
completion(.failure(error))
14+
Task { @MainActor in
15+
await self.wordPressComRestApi
16+
.perform(
17+
.get,
18+
URLString: path,
19+
jsonDecoder: .apiDecoder,
20+
type: RemotePublicizeInfo.self
21+
)
22+
.map { $0.body }
23+
.flatMapError { original -> Result<RemotePublicizeInfo?, Error> in
24+
if case let .endpointError(endpointError) = original, endpointError.response?.statusCode == 200, endpointError.code == .responseSerializationFailed {
25+
return .success(nil)
26+
}
27+
return .failure(original.asNSError())
2328
}
24-
25-
case .failure(let error):
26-
completion(.failure(error))
27-
}
29+
.execute(completion)
2830
}
2931
}
3032
}

WordPressKit/Result+Callback.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ extension Swift.Result {
88
}
99
}
1010

11+
func execute(_ completion: (Self) -> Void) {
12+
completion(self)
13+
}
14+
1115
func eraseToError() -> Result<Success, Error> {
1216
mapError { $0 }
1317
}

WordPressKit/ShareAppContentServiceRemote.swift

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,18 @@ open class ShareAppContentServiceRemote: ServiceRemoteWordPressComREST {
1111
let requestURLString = path(forEndpoint: endpoint, withVersion: ._2_0)
1212
let params: [String: AnyObject] = [Constants.appNameParameterKey: appName.rawValue as AnyObject]
1313

14-
wordPressComRestApi.GET(requestURLString, parameters: params) { result, _ in
15-
switch result {
16-
case .success(let responseObject):
17-
do {
18-
let data = try JSONSerialization.data(withJSONObject: responseObject, options: [])
19-
let content = try JSONDecoder.apiDecoder.decode(RemoteShareAppContent.self, from: data)
20-
completion(.success(content))
21-
} catch {
22-
completion(.failure(error))
23-
}
24-
25-
case .failure(let error):
26-
completion(.failure(error))
27-
}
14+
Task { @MainActor in
15+
await self.wordPressComRestApi
16+
.perform(
17+
.get,
18+
URLString: requestURLString,
19+
parameters: params,
20+
jsonDecoder: .apiDecoder,
21+
type: RemoteShareAppContent.self
22+
)
23+
.map { $0.body }
24+
.mapError { error -> Error in error.asNSError() }
25+
.execute(completion)
2826
}
2927
}
3028
}

WordPressKit/WordPressComRestApi.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,7 @@ private extension CharacterSet {
865865
}()
866866
}
867867

868-
private extension WordPressAPIError<WordPressComRestApiEndpointError> {
868+
extension WordPressAPIError<WordPressComRestApiEndpointError> {
869869
func asNSError() -> NSError {
870870
// When encoutering `URLError`, return `URLError` to avoid potentially breaking existing error handling code in the apps.
871871
if case let .connection(urlError) = self {

WordPressKitTests/BloggingPromptsServiceRemoteTests.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import XCTest
2+
import OHHTTPStubs
23

34
@testable import WordPressKit
45

@@ -81,24 +82,35 @@ class BloggingPromptsServiceRemoteTests: RemoteTestCase, RESTTestable {
8182
wait(for: [expect], timeout: timeout)
8283
}
8384

84-
func test_fetchPrompts_correctlyAddsParametersToRequest() {
85+
func test_fetchPrompts_correctlyAddsParametersToRequest() throws {
86+
let requestReceived = expectation(description: "HTTP request is received")
87+
var request: URLRequest?
88+
stub(condition: isHost("public-api.wordpress.com")) {
89+
request = $0
90+
requestReceived.fulfill()
91+
return HTTPStubsResponse(error: URLError(.networkConnectionLost))
92+
}
93+
8594
let expectedNumber = 10
8695
let expectedDateString = "2022-01-02"
8796
let expectedDate = dateFormatter.date(from: expectedDateString)
88-
let customMockApi = MockWordPressComRestApi()
89-
service = BloggingPromptsServiceRemote(wordPressComRestApi: customMockApi)
97+
service = BloggingPromptsServiceRemote(wordPressComRestApi: WordPressComRestApi())
9098

9199
// no-op; we just need to check the passed params.
92100
service.fetchPrompts(for: siteID, number: expectedNumber, fromDate: expectedDate, completion: { _ in })
101+
wait(for: [requestReceived], timeout: 0.3)
93102

94-
XCTAssertNotNil(customMockApi.parametersPassedIn as? [String: AnyObject])
95-
let params = customMockApi.parametersPassedIn! as! [String: AnyObject]
103+
let url = try XCTUnwrap(request?.url)
104+
let queryItems = try XCTUnwrap(URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems)
105+
let params = queryItems.reduce(into: [String: String]()) { result, query in
106+
result[query.name] = query.value
107+
}
96108

97109
XCTAssertNotNil(params[.numberKey])
98-
XCTAssertEqual(params[.numberKey] as! Int, expectedNumber)
110+
XCTAssertEqual(params[.numberKey], expectedNumber.description)
99111

100112
XCTAssertNotNil(params[.dateKey])
101-
XCTAssertEqual(params[.dateKey] as! String, expectedDateString)
113+
XCTAssertEqual(params[.dateKey], expectedDateString)
102114
}
103115

104116
func test_fetchSettings_returnsRemoteSettings() {

WordPressKitTests/CommentServiceRemoteREST+APIv2Tests.swift

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import XCTest
3+
import OHHTTPStubs
34

45
@testable import WordPressKit
56

@@ -67,8 +68,15 @@ final class CommentServiceRemoteREST_APIv2Tests: RemoteTestCase, RESTTestable {
6768
wait(for: [expect], timeout: timeout)
6869
}
6970

70-
func test_getCommentsV2_correctlyPassesCustomParameters() {
71-
let mockApi = MockWordPressComRestApi()
71+
func test_getCommentsV2_correctlyPassesCustomParameters() throws {
72+
let requestReceived = expectation(description: "HTTP request is received")
73+
var request: URLRequest?
74+
stub(condition: isHost("public-api.wordpress.com")) {
75+
request = $0
76+
requestReceived.fulfill()
77+
return HTTPStubsResponse(error: URLError(.networkConnectionLost))
78+
}
79+
7280
let expectedParentId = 4
7381
let expectedAuthorId = 5
7482
let expectedContext = "edit"
@@ -77,20 +85,22 @@ final class CommentServiceRemoteREST_APIv2Tests: RemoteTestCase, RESTTestable {
7785
.author: expectedAuthorId,
7886
.context: expectedContext
7987
]
80-
remote = CommentServiceRemoteREST(wordPressComRestApi: mockApi, siteID: NSNumber(value: siteId))
81-
88+
remote = CommentServiceRemoteREST(wordPressComRestApi: WordPressComRestApi(), siteID: NSNumber(value: siteId))
8289
remote.getCommentsV2(for: siteId, parameters: parameters, success: { _ in }, failure: { _ in })
90+
wait(for: [requestReceived], timeout: 0.3)
8391

84-
XCTAssertNotNil(mockApi.parametersPassedIn)
85-
XCTAssertTrue((mockApi.parametersPassedIn! as? [String: AnyObject]) != nil)
92+
let url = try XCTUnwrap(request?.url)
93+
let queryItems = try XCTUnwrap(URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems)
94+
let params = queryItems.reduce(into: [String: String]()) { result, query in
95+
result[query.name] = query.value
96+
}
8697

87-
let params = mockApi.parametersPassedIn! as! [String: AnyObject]
8898
XCTAssertNotNil(params[CommentServiceRemoteREST.RequestKeys.parent.rawValue])
89-
XCTAssertEqual(params[CommentServiceRemoteREST.RequestKeys.parent.rawValue] as! Int, expectedParentId)
99+
XCTAssertEqual(params[CommentServiceRemoteREST.RequestKeys.parent.rawValue], expectedParentId.description)
90100
XCTAssertNotNil(params[CommentServiceRemoteREST.RequestKeys.author.rawValue])
91-
XCTAssertEqual(params[CommentServiceRemoteREST.RequestKeys.author.rawValue] as! Int, expectedAuthorId)
101+
XCTAssertEqual(params[CommentServiceRemoteREST.RequestKeys.author.rawValue], expectedAuthorId.description)
92102
XCTAssertNotNil(params[CommentServiceRemoteREST.RequestKeys.context.rawValue])
93-
XCTAssertEqual(params[CommentServiceRemoteREST.RequestKeys.context.rawValue] as! String, expectedContext)
103+
XCTAssertEqual(params[CommentServiceRemoteREST.RequestKeys.context.rawValue], expectedContext)
94104
}
95105

96106
func test_getCommentsV2_givenEditContext_parsesAdditionalFields() {

0 commit comments

Comments
 (0)