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

Commit c504256

Browse files
committed
Add PostServiceRemoteExtended
1 parent 9ab4b43 commit c504256

File tree

6 files changed

+165
-43
lines changed

6 files changed

+165
-43
lines changed

WordPressKit.xcodeproj/project.pbxproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
0152100C28EDA9E400DD6783 /* StatsAnnualAndMostPopularTimeInsightDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0152100B28EDA9E400DD6783 /* StatsAnnualAndMostPopularTimeInsightDecodingTests.swift */; };
1616
0847B92C2A4442730044D32F /* IPLocationRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0847B92B2A4442730044D32F /* IPLocationRemote.swift */; };
1717
08C7493E2A45EA11000DA0E2 /* IPLocationRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08C7493D2A45EA11000DA0E2 /* IPLocationRemoteTests.swift */; };
18-
0C1C08382B9B675400E52F8C /* StringCodingKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08372B9B675400E52F8C /* StringCodingKey.swift */; };
1918
0C1C08412B9CD79900E52F8C /* PostServiceRemoteExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */; };
2019
0C1C08432B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */; };
2120
0C1C08452B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */; };
@@ -740,7 +739,6 @@
740739
0152100B28EDA9E400DD6783 /* StatsAnnualAndMostPopularTimeInsightDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsAnnualAndMostPopularTimeInsightDecodingTests.swift; sourceTree = "<group>"; };
741740
0847B92B2A4442730044D32F /* IPLocationRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPLocationRemote.swift; sourceTree = "<group>"; };
742741
08C7493D2A45EA11000DA0E2 /* IPLocationRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPLocationRemoteTests.swift; sourceTree = "<group>"; };
743-
0C1C08372B9B675400E52F8C /* StringCodingKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringCodingKey.swift; sourceTree = "<group>"; };
744742
0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostServiceRemoteExtended.swift; sourceTree = "<group>"; };
745743
0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteREST+Extended.swift"; sourceTree = "<group>"; };
746744
0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteXMLRPC+Extended.swift"; sourceTree = "<group>"; };
@@ -2537,7 +2535,6 @@
25372535
4AE278432B2FAF6200E4D9B1 /* HTTPProtocolHelpers.swift */,
25382536
3F391E192B50F3EB007975C4 /* Result+Callback.swift */,
25392537
4A5BC1A72B59DE6600C7D037 /* Either.swift */,
2540-
0C1C08372B9B675400E52F8C /* StringCodingKey.swift */,
25412538
4A57A6852B54C68C008D0660 /* Constants.h */,
25422539
4A57A6862B54C68C008D0660 /* Constants.m */,
25432540
);
@@ -3476,7 +3473,6 @@
34763473
40819778221F00E600A298E4 /* StatsSummaryTimeIntervalData.swift in Sources */,
34773474
7430C9A81F1927180051B8E6 /* ReaderTopicServiceRemote.m in Sources */,
34783475
17CE77F120C6EB41001DEA5A /* ReaderFeed.swift in Sources */,
3479-
0C1C08382B9B675400E52F8C /* StringCodingKey.swift in Sources */,
34803476
93C674F21EE8351E00BFAF05 /* NSMutableDictionary+Helpers.m in Sources */,
34813477
4624222D2548BA0F002B8A12 /* RemoteSiteDesign.swift in Sources */,
34823478
74D67F061F1528470010C5ED /* PeopleServiceRemote.swift in Sources */,

WordPressKit/PostServiceRemoteExtended.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ public protocol PostServiceRemoteExtended: PostServiceRemote {
55
func createPost(with parameters: RemotePostCreateParameters) async throws -> RemotePost
66

77
/// Performs a partial update to the existing post.
8-
func patchPost(withID postID: Int, changes: RemotePostUpdateParameters) async throws -> RemotePost
8+
func patchPost(withID postID: Int, parameters: RemotePostUpdateParameters) async throws -> RemotePost
99
}

WordPressKit/PostServiceRemoteREST+Extended.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ extension PostServiceRemoteREST: PostServiceRemoteExtended {
1515
return try await decodePost(from: response)
1616
}
1717

18-
public func patchPost(withID postID: Int, changes: RemotePostUpdateParameters) async throws -> RemotePost {
18+
public func patchPost(withID postID: Int, parameters: RemotePostUpdateParameters) async throws -> RemotePost {
1919
let path = self.path(forEndpoint: "sites/\(siteID)/posts/\(postID)?context=edit", withVersion: ._1_2)
20-
let parameters = try makeParameters(from: RemotePostUpdateParametersWordPressComEncoder(parameters: changes))
20+
let parameters = try makeParameters(from: RemotePostUpdateParametersWordPressComEncoder(parameters: parameters))
2121

2222
let response = try await withUnsafeThrowingContinuation { continuation in
2323
wordPressComRestApi.POST(path, parameters: parameters) { object, _ in
Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,60 @@
11
import Foundation
2+
import wpxmlrpc
23

34
extension PostServiceRemoteXMLRPC: PostServiceRemoteExtended {
45
public func createPost(with parameters: RemotePostCreateParameters) async throws -> RemotePost {
5-
throw URLError(.unknown, userInfo: [NSLocalizedFailureErrorKey: "Unimplemented"])
6+
let dictionary = try makeParameters(from: RemotePostCreateParametersXMLRPCEncoder(parameters: parameters, type: .post))
7+
let parameters = xmlrpcArguments(withExtra: dictionary) as [AnyObject]
8+
let response = try await withUnsafeThrowingContinuation { continuation in
9+
api.callMethod("metaWeblog.newPost", parameters: parameters) { object, _ in
10+
continuation.resume(returning: object)
11+
} failure: { error, _ in
12+
continuation.resume(throwing: error)
13+
}
14+
}
15+
guard let postID = (response as? NSNumber) else {
16+
throw URLError(.unknown) // Should never happen
17+
}
18+
return try await getPost(withID: postID)
619
}
720

8-
public func patchPost(withID postID: Int, changes: RemotePostUpdateParameters) async throws -> RemotePost {
9-
throw URLError(.unknown, userInfo: [NSLocalizedFailureErrorKey: "Unimplemented"])
21+
public func patchPost(withID postID: Int, parameters: RemotePostUpdateParameters) async throws -> RemotePost {
22+
let dictionary = try makeParameters(from: RemotePostUpdateParametersXMLRPCEncoder(parameters: parameters, type: .post))
23+
var parameters = xmlrpcArguments(withExtra: dictionary) as [AnyObject]
24+
if parameters.count > 0 {
25+
parameters[0] = postID as NSNumber
26+
}
27+
try await withUnsafeThrowingContinuation { continuation in
28+
api.callMethod("metaWeblog.editPost", parameters: parameters) { _, _ in
29+
continuation.resume(returning: ())
30+
} failure: { error, _ in
31+
continuation.resume(throwing: error)
32+
}
33+
}
34+
return try await getPost(withID: postID as NSNumber)
1035
}
36+
37+
private func getPost(withID postID: NSNumber) async throws -> RemotePost {
38+
try await withUnsafeThrowingContinuation { continuation in
39+
getPostWithID(postID) { post in
40+
guard let post else {
41+
return continuation.resume(throwing: URLError(.unknown)) // Should never happen
42+
}
43+
continuation.resume(returning: post)
44+
} failure: { error in
45+
continuation.resume(throwing: error ?? URLError(.unknown))
46+
}
47+
}
48+
}
49+
}
50+
51+
private func makeParameters<T: Encodable>(from value: T) throws -> [String: AnyObject] {
52+
let encoder = PropertyListEncoder()
53+
encoder.outputFormat = .xml
54+
let data = try encoder.encode(value)
55+
let object = try PropertyListSerialization.propertyList(from: data, format: nil)
56+
guard let dictionary = object as? [String: AnyObject] else {
57+
throw URLError(.unknown) // This should never happen
58+
}
59+
return dictionary
1160
}

WordPressKit/RemotePostParameters.swift

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ extension RemotePostCreateParameters {
151151
}
152152
}
153153

154-
// MARK: - Encoding
154+
// MARK: - Encoding (WP.COM REST API)
155155

156-
private enum RemotePostCodingKeys: String, CodingKey {
156+
private enum RemotePostWordPressComCodingKeys: String, CodingKey {
157157
case ifNotModifiedSince = "if_not_modified_since"
158158
case status
159159
case date
@@ -177,7 +177,7 @@ struct RemotePostCreateParametersWordPressComEncoder: Encodable {
177177
let parameters: RemotePostCreateParameters
178178

179179
func encode(to encoder: Encoder) throws {
180-
var container = encoder.container(keyedBy: RemotePostCodingKeys.self)
180+
var container = encoder.container(keyedBy: RemotePostWordPressComCodingKeys.self)
181181
try container.encodeIfPresent(parameters.status, forKey: .status)
182182
try container.encodeIfPresent(parameters.date, forKey: .date)
183183
try container.encodeIfPresent(parameters.authorID, forKey: .authorID)
@@ -196,7 +196,7 @@ struct RemotePostCreateParametersWordPressComEncoder: Encodable {
196196
// Posts
197197
try container.encodeIfPresent(parameters.format, forKey: .format)
198198
if !parameters.tags.isEmpty {
199-
try container.encode([RemotePostCodingKeys.postTags: parameters.tags], forKey: .terms)
199+
try container.encode([RemotePostWordPressComCodingKeys.postTags: parameters.tags], forKey: .terms)
200200
}
201201
if !parameters.categoryIDs.isEmpty {
202202
try container.encodeIfPresent(parameters.categoryIDs, forKey: .categoryIDs)
@@ -211,7 +211,7 @@ struct RemotePostUpdateParametersWordPressComEncoder: Encodable {
211211
let parameters: RemotePostUpdateParameters
212212

213213
func encode(to encoder: Encoder) throws {
214-
var container = encoder.container(keyedBy: RemotePostCodingKeys.self)
214+
var container = encoder.container(keyedBy: RemotePostWordPressComCodingKeys.self)
215215
try container.encodeIfPresent(parameters.ifNotModifiedSince, forKey: .ifNotModifiedSince)
216216

217217
try container.encodeIfPresent(parameters.status, forKey: .status)
@@ -232,7 +232,111 @@ struct RemotePostUpdateParametersWordPressComEncoder: Encodable {
232232
// Posts
233233
try container.encodeIfPresent(parameters.format, forKey: .format)
234234
if let tags = parameters.tags {
235-
try container.encode([RemotePostCodingKeys.postTags: tags], forKey: .terms)
235+
try container.encode([RemotePostWordPressComCodingKeys.postTags: tags], forKey: .terms)
236+
}
237+
try container.encodeIfPresent(parameters.categoryIDs, forKey: .categoryIDs)
238+
try container.encodeIfPresent(parameters.isSticky, forKey: .isSticky)
239+
}
240+
}
241+
242+
// MARK: - Encoding (XML-RPC)
243+
244+
private enum RemotePostXMLRPCCodingKeys: String, CodingKey {
245+
case ifNotModifiedSince = "if_not_modified_since"
246+
case postStatus = "post_status"
247+
case pageStatus = "page_status"
248+
case date = "date_created_gmt"
249+
case authorID = "wp_author_id"
250+
case title
251+
case content = "description"
252+
case password = "wp_password"
253+
case excerpt = "mt_excerpt"
254+
case slug = "wp_slug"
255+
case featuredImageID = "wp_post_thumbnail"
256+
case parentPageID = "wp_page_parent_id"
257+
case tags = "mt_keywords"
258+
case format = "wp_post_format"
259+
case isSticky = "sticky"
260+
case categoryIDs = "categories"
261+
262+
static let postTags = "post_tag"
263+
}
264+
265+
enum RemotePostEncodingPostType {
266+
case post, page
267+
}
268+
269+
struct RemotePostCreateParametersXMLRPCEncoder: Encodable {
270+
let parameters: RemotePostCreateParameters
271+
let type: RemotePostEncodingPostType
272+
273+
func encode(to encoder: Encoder) throws {
274+
var container = encoder.container(keyedBy: RemotePostXMLRPCCodingKeys.self)
275+
switch type {
276+
case .post:
277+
try container.encodeIfPresent(parameters.status, forKey: .postStatus)
278+
case .page:
279+
try container.encodeIfPresent(parameters.status, forKey: .pageStatus)
280+
}
281+
try container.encodeIfPresent(parameters.date, forKey: .date)
282+
try container.encodeIfPresent(parameters.authorID, forKey: .authorID)
283+
try container.encodeIfPresent(parameters.title, forKey: .title)
284+
try container.encodeIfPresent(parameters.content, forKey: .content)
285+
try container.encodeIfPresent(parameters.password, forKey: .password)
286+
try container.encodeIfPresent(parameters.excerpt, forKey: .excerpt)
287+
try container.encodeIfPresent(parameters.slug, forKey: .slug)
288+
try container.encodeIfPresent(parameters.featuredImageID, forKey: .featuredImageID)
289+
290+
// Pages
291+
if let parentPageID = parameters.parentPageID {
292+
try container.encodeIfPresent(parentPageID, forKey: .parentPageID)
293+
}
294+
295+
// Posts
296+
try container.encodeIfPresent(parameters.format, forKey: .format)
297+
if !parameters.tags.isEmpty {
298+
try container.encode(parameters.tags, forKey: .tags)
299+
}
300+
if !parameters.categoryIDs.isEmpty {
301+
try container.encodeIfPresent(parameters.categoryIDs, forKey: .categoryIDs)
302+
}
303+
if parameters.isSticky {
304+
try container.encode(parameters.isSticky, forKey: .isSticky)
305+
}
306+
}
307+
}
308+
309+
struct RemotePostUpdateParametersXMLRPCEncoder: Encodable {
310+
let parameters: RemotePostUpdateParameters
311+
let type: RemotePostEncodingPostType
312+
313+
func encode(to encoder: Encoder) throws {
314+
var container = encoder.container(keyedBy: RemotePostXMLRPCCodingKeys.self)
315+
try container.encodeIfPresent(parameters.ifNotModifiedSince, forKey: .ifNotModifiedSince)
316+
switch type {
317+
case .post:
318+
try container.encodeIfPresent(parameters.status, forKey: .postStatus)
319+
case .page:
320+
try container.encodeIfPresent(parameters.status, forKey: .pageStatus)
321+
}
322+
try container.encodeIfPresent(parameters.date, forKey: .date)
323+
try container.encodeIfPresent(parameters.authorID, forKey: .authorID)
324+
try container.encodeIfPresent(parameters.title, forKey: .title)
325+
try container.encodeIfPresent(parameters.content, forKey: .content)
326+
try container.encodeIfPresent(parameters.password, forKey: .password)
327+
try container.encodeIfPresent(parameters.excerpt, forKey: .excerpt)
328+
try container.encodeIfPresent(parameters.slug, forKey: .slug)
329+
try container.encodeIfPresent(parameters.featuredImageID, forKey: .featuredImageID)
330+
331+
// Pages
332+
if let parentPageID = parameters.parentPageID {
333+
try container.encodeIfPresent(parentPageID, forKey: .parentPageID)
334+
}
335+
336+
// Posts
337+
try container.encodeIfPresent(parameters.format, forKey: .format)
338+
if let tags = parameters.tags {
339+
try container.encode(tags, forKey: .tags)
236340
}
237341
try container.encodeIfPresent(parameters.categoryIDs, forKey: .categoryIDs)
238342
try container.encodeIfPresent(parameters.isSticky, forKey: .isSticky)

WordPressKit/StringCodingKey.swift

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

0 commit comments

Comments
 (0)