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

Commit 46c2bc0

Browse files
Merge pull request #63 from wordpress-mobile/issue/10711-site-creation-service-changes
Exposed new WPCOM service method for enhanced site creation
2 parents e0bf233 + b0e035e commit 46c2bc0

File tree

8 files changed

+758
-3
lines changed

8 files changed

+758
-3
lines changed

Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ PODS:
2727
- OHHTTPStubs/Swift (6.1.0):
2828
- OHHTTPStubs/Default
2929
- UIDeviceIdentifier (0.5.0)
30-
- WordPressKit (1.6.0-beta.1):
30+
- WordPressKit (1.7.0-beta.1):
3131
- Alamofire (~> 4.7.3)
3232
- CocoaLumberjack (= 3.4.2)
3333
- NSObject-SafeExpectations (= 0.0.3)
@@ -70,7 +70,7 @@ SPEC CHECKSUMS:
7070
OCMock: 43565190abc78977ad44a61c0d20d7f0784d35ab
7171
OHHTTPStubs: 1e21c7d2c084b8153fc53d48400d8919d2d432d0
7272
UIDeviceIdentifier: a959a6d4f51036b4180dd31fb26483a820f1cc46
73-
WordPressKit: 0a36f22916424c030cca58416e66560b7fc2bbbd
73+
WordPressKit: 4c6b7320a5bc604cb3a87d8f80298f2fa0e14ab6
7474
WordPressShared: f55be10963c8f6dbbc8e896450805ba1dd5353f7
7575
wpxmlrpc: bfc572f62ce7ee897f6f38b098d2ba08732ecef4
7676

WordPressKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "WordPressKit"
3-
s.version = "1.6.0-beta.1"
3+
s.version = "1.7.0-beta.1"
44
s.summary = "WordPressKit offers a clean and simple WordPress.com and WordPress.org API."
55

66
s.description = <<-DESC

WordPressKit.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
439A44DA2107C93000795ED7 /* RemotePlan_ApiVersion1_3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439A44D92107C93000795ED7 /* RemotePlan_ApiVersion1_3.swift */; };
4141
439A44DC2107CE3C00795ED7 /* site-plans-v3-empty-failure.json in Resources */ = {isa = PBXBuildFile; fileRef = 439A44DB2107CE3C00795ED7 /* site-plans-v3-empty-failure.json */; };
4242
439A44DE2107CF6F00795ED7 /* site-plans-v3-bad-json-failure.json in Resources */ = {isa = PBXBuildFile; fileRef = 439A44DD2107CF6F00795ED7 /* site-plans-v3-bad-json-failure.json */; };
43+
731BA83621DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 731BA83521DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift */; };
44+
731BA83821DECD97000FDFCD /* SiteCreationResponseDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 731BA83721DECD97000FDFCD /* SiteCreationResponseDecodingTests.swift */; };
45+
731BA83A21DED358000FDFCD /* site-creation-success.json in Resources */ = {isa = PBXBuildFile; fileRef = 731BA83921DECE93000FDFCD /* site-creation-success.json */; };
46+
7328420421CD786C00126755 /* WordPressComServiceRemote+SiteCreation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7328420321CD786C00126755 /* WordPressComServiceRemote+SiteCreation.swift */; };
47+
7328420621CD798A00126755 /* WordPressComServiceRemoteTests+SiteCreation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7328420521CD798A00126755 /* WordPressComServiceRemoteTests+SiteCreation.swift */; };
4348
7403A2E41EF06ED500DED7DC /* AccountSettingsRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7403A2E31EF06ED500DED7DC /* AccountSettingsRemote.swift */; };
4449
7403A2E61EF06F7000DED7DC /* AccountSettingsRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7403A2E51EF06F7000DED7DC /* AccountSettingsRemoteTests.swift */; };
4550
7403A2F41EF06FEB00DED7DC /* me-settings-auth-failure.json in Resources */ = {isa = PBXBuildFile; fileRef = 7403A2E71EF06FEB00DED7DC /* me-settings-auth-failure.json */; };
@@ -479,6 +484,11 @@
479484
439A44DD2107CF6F00795ED7 /* site-plans-v3-bad-json-failure.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-plans-v3-bad-json-failure.json"; sourceTree = "<group>"; };
480485
6C2A33D76FD1052D6F30466D /* Pods-WordPressKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKit/Pods-WordPressKit.debug.xcconfig"; sourceTree = "<group>"; };
481486
6F2E0CC4FA01B5475A378DA2 /* Pods-WordPressKitTests.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.release-alpha.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.release-alpha.xcconfig"; sourceTree = "<group>"; };
487+
731BA83521DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteCreationRequestEncodingTests.swift; sourceTree = "<group>"; };
488+
731BA83721DECD97000FDFCD /* SiteCreationResponseDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteCreationResponseDecodingTests.swift; sourceTree = "<group>"; };
489+
731BA83921DECE93000FDFCD /* site-creation-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "site-creation-success.json"; sourceTree = "<group>"; };
490+
7328420321CD786C00126755 /* WordPressComServiceRemote+SiteCreation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WordPressComServiceRemote+SiteCreation.swift"; sourceTree = "<group>"; };
491+
7328420521CD798A00126755 /* WordPressComServiceRemoteTests+SiteCreation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WordPressComServiceRemoteTests+SiteCreation.swift"; sourceTree = "<group>"; };
482492
7403A2E31EF06ED500DED7DC /* AccountSettingsRemote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountSettingsRemote.swift; sourceTree = "<group>"; };
483493
7403A2E51EF06F7000DED7DC /* AccountSettingsRemoteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountSettingsRemoteTests.swift; sourceTree = "<group>"; };
484494
7403A2E71EF06FEB00DED7DC /* me-settings-auth-failure.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "me-settings-auth-failure.json"; path = "WordPressKitTests/me-settings-auth-failure.json"; sourceTree = SOURCE_ROOT; };
@@ -986,8 +996,11 @@
986996
74C473AD1EF2F7BF009918F2 /* Site */ = {
987997
isa = PBXGroup;
988998
children = (
999+
731BA83521DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift */,
1000+
731BA83721DECD97000FDFCD /* SiteCreationResponseDecodingTests.swift */,
9891001
74C473AE1EF2F7D1009918F2 /* SiteManagementServiceRemoteTests.swift */,
9901002
E1787DB1200E5690004CB3AF /* TimeZoneServiceRemoteTests.swift */,
1003+
7328420521CD798A00126755 /* WordPressComServiceRemoteTests+SiteCreation.swift */,
9911004
);
9921005
name = Site;
9931006
sourceTree = "<group>";
@@ -1255,6 +1268,7 @@
12551268
93F50A3E1F227C8900B5BEBA /* UsersServiceRemoteXMLRPC.swift */,
12561269
93F50A351F226B9300B5BEBA /* WordPressComServiceRemote.h */,
12571270
93F50A361F226B9300B5BEBA /* WordPressComServiceRemote.m */,
1271+
7328420321CD786C00126755 /* WordPressComServiceRemote+SiteCreation.swift */,
12581272
9368C7A11EC62F800092CE8E /* WPStatsServiceRemote.h */,
12591273
9368C7A21EC62F800092CE8E /* WPStatsServiceRemote.m */,
12601274
E182BF691FD961810001D850 /* Endpoint.swift */,
@@ -1470,6 +1484,7 @@
14701484
74C473C81EF335B8009918F2 /* site-active-purchases-empty-response.json */,
14711485
74C473C61EF334D4009918F2 /* site-active-purchases-none-active-success.json */,
14721486
74C473C41EF33242009918F2 /* site-active-purchases-two-active-success.json */,
1487+
731BA83921DECE93000FDFCD /* site-creation-success.json */,
14731488
74C473B21EF3204B009918F2 /* site-delete-auth-failure.json */,
14741489
74C473B41EF320CC009918F2 /* site-delete-bad-json-failure.json */,
14751490
74C473B81EF325F6009918F2 /* site-delete-missing-status-failure.json */,
@@ -1900,6 +1915,7 @@
19001915
E1787DB0200E564B004CB3AF /* timezones.json in Resources */,
19011916
93BD275E1EE73442002BB00B /* me-bad-json-failure.json in Resources */,
19021917
FFE247AF20C891E6002DF3A2 /* WordPressComOAuthWrongPasswordFail.json in Resources */,
1918+
731BA83A21DED358000FDFCD /* site-creation-success.json in Resources */,
19031919
829BA4301FACF187003ADEEA /* activity-rewind-status-restore-failure.json in Resources */,
19041920
74D67F391F15C3740010C5ED /* site-viewers-delete-bad-json.json in Resources */,
19051921
93BD27631EE73442002BB00B /* me-sites-visibility-bad-json-failure.json in Resources */,
@@ -2137,6 +2153,7 @@
21372153
7403A2E41EF06ED500DED7DC /* AccountSettingsRemote.swift in Sources */,
21382154
93C674E81EE8345300BFAF05 /* RemoteBlog.m in Sources */,
21392155
93BD27831EE73944002BB00B /* WordPressRSDParser.swift in Sources */,
2156+
7328420421CD786C00126755 /* WordPressComServiceRemote+SiteCreation.swift in Sources */,
21402157
826016F31F9FA17B00533B6C /* Activity.swift in Sources */,
21412158
7E3E7A4A20E443890075D159 /* Scanner+extensions.swift in Sources */,
21422159
742362E31F1025B400BD0A7F /* RemoteMenuLocation.m in Sources */,
@@ -2217,6 +2234,7 @@
22172234
74155E251EF87DDF00A06AEA /* ServiceRemoteRESTTests.m in Sources */,
22182235
74D67F0A1F15C24C0010C5ED /* PeopleServiceRemoteTests.swift in Sources */,
22192236
9F3E0BAE20873836009CB5BA /* ReaderTopicServiceRemoteTest+Subscriptions.swift in Sources */,
2237+
7328420621CD798A00126755 /* WordPressComServiceRemoteTests+SiteCreation.swift in Sources */,
22202238
74585B901F0D51F900E7E667 /* DomainsServiceRemoteRESTTests.swift in Sources */,
22212239
9AB6D64A218727D60008F274 /* PostServiceRemoteRESTRevisionsTest.swift in Sources */,
22222240
7430C9BD1F192C0F0051B8E6 /* ReaderPostServiceRemoteTests.m in Sources */,
@@ -2232,6 +2250,7 @@
22322250
740B23D31F17F6BB00067A2A /* PostServiceRemoteXMLRPCTests.swift in Sources */,
22332251
93188D221F2264E60028ED4D /* TaxonomyServiceRemoteRESTTests.m in Sources */,
22342252
74FC6F3B1F191BB400112505 /* NotificationSyncServiceRemoteTests.swift in Sources */,
2253+
731BA83821DECD97000FDFCD /* SiteCreationResponseDecodingTests.swift in Sources */,
22352254
74FA25F71F1FDA200044BC54 /* MediaServiceRemoteRESTTests.swift in Sources */,
22362255
7433BC051EFC4556002D9E92 /* PlanServiceRemoteTests.swift in Sources */,
22372256
740B23D61F17F7C100067A2A /* XMLRPCTestable.swift in Sources */,
@@ -2247,6 +2266,7 @@
22472266
93AC8EE11ED32FD000900F5A /* StatsStringUtilitiesTest.m in Sources */,
22482267
74C473AF1EF2F7D1009918F2 /* SiteManagementServiceRemoteTests.swift in Sources */,
22492268
74A44DD51F13C6D8006CD8F4 /* PushAuthenticationServiceRemoteTests.swift in Sources */,
2269+
731BA83621DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift in Sources */,
22502270
931924241F1662FA0069CBCC /* JSONLoader.swift in Sources */,
22512271
93AC8EE21ED32FD000900F5A /* WPStatsServiceRemoteTests.m in Sources */,
22522272
E1787DB2200E5690004CB3AF /* TimeZoneServiceRemoteTests.swift in Sources */,
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
2+
import Foundation
3+
4+
// MARK: - SiteCreationRequest
5+
6+
/// This value type is intended to express a site creation request.
7+
///
8+
public struct SiteCreationRequest: Encodable {
9+
let segmentIdentifier: Int64
10+
let verticalIdentifier: String?
11+
let title: String
12+
let tagline: String?
13+
let siteURLString: String
14+
let isPublic: Bool
15+
let languageIdentifier: String
16+
let shouldValidate: Bool
17+
let clientIdentifier: String
18+
let clientSecret: String
19+
20+
public func encode(to encoder: Encoder) throws {
21+
var container = encoder.container(keyedBy: CodingKeys.self)
22+
23+
try container.encode(clientIdentifier, forKey: .clientIdentifier)
24+
try container.encode(clientSecret, forKey: .clientSecret)
25+
try container.encode(languageIdentifier, forKey: .languageIdentifier)
26+
try container.encode(shouldValidate, forKey: .shouldValidate)
27+
try container.encode(siteURLString, forKey: .siteURLString)
28+
try container.encode(title, forKey: .title)
29+
30+
let publicValue = isPublic ? 1 : 0
31+
try container.encode(publicValue, forKey: .isPublic)
32+
33+
let siteInfo: SiteInformation?
34+
if let tagline = tagline {
35+
siteInfo = SiteInformation(tagline: tagline)
36+
} else {
37+
siteInfo = nil
38+
}
39+
let options = SiteCreationOptions(segmentIdentifier: segmentIdentifier, verticalIdentifier: verticalIdentifier, siteInformation: siteInfo)
40+
41+
try container.encode(options, forKey: .options)
42+
}
43+
44+
private enum CodingKeys: String, CodingKey {
45+
case clientIdentifier = "client_id"
46+
case clientSecret = "client_secret"
47+
case languageIdentifier = "lang_id"
48+
case isPublic = "public"
49+
case shouldValidate = "validate"
50+
case siteURLString = "blog_name"
51+
case title = "blog_title"
52+
case options = "options"
53+
}
54+
}
55+
56+
private struct SiteCreationOptions: Encodable {
57+
let segmentIdentifier: Int64
58+
let verticalIdentifier: String?
59+
let siteInformation: SiteInformation?
60+
61+
enum CodingKeys: String, CodingKey {
62+
case segmentIdentifier = "site_segment"
63+
case verticalIdentifier = "site_vertical"
64+
case siteInformation = "site_information"
65+
}
66+
}
67+
68+
private struct SiteInformation: Encodable {
69+
let tagline: String?
70+
71+
enum CodingKeys: String, CodingKey {
72+
case tagline = "site_tagline"
73+
}
74+
}
75+
76+
// MARK: - SiteCreationResponse
77+
78+
/// This value type is intended to express a site creation response.
79+
///
80+
public struct SiteCreationResponse: Decodable {
81+
struct CreatedSite: Decodable {
82+
let identifier: String
83+
let title: String
84+
let urlString: String
85+
let xmlrpcString: String
86+
87+
enum CodingKeys: String, CodingKey {
88+
case identifier = "blogid"
89+
case title = "blogname"
90+
case urlString = "url"
91+
case xmlrpcString = "xmlrpc"
92+
}
93+
}
94+
95+
let createdSite: CreatedSite
96+
let success: Bool
97+
98+
enum CodingKeys: String, CodingKey {
99+
case createdSite = "blog_details"
100+
case success
101+
}
102+
}
103+
104+
// MARK: - WordPressComServiceRemote (Site Creation)
105+
106+
/// Describes the errors that could arise during the process of site creation.
107+
///
108+
/// - requestEncodingFailure: unable to encode the request parameters.
109+
/// - responseDecodingFailure: unable to decode the server response.
110+
/// - serviceFailure: the service returned an unexpected error.
111+
///
112+
public enum SiteCreationError: Error {
113+
case requestEncodingFailure
114+
case responseDecodingFailure
115+
case serviceFailure
116+
}
117+
118+
/// Advises the caller of results related to site creation requests.
119+
///
120+
/// - success: the site creation request succeeded with the accompanying result.
121+
/// - failure: the site creation request failed due to the accompanying error.
122+
///
123+
public enum SiteCreationResult {
124+
case success(SiteCreationResponse)
125+
case failure(SiteCreationError)
126+
}
127+
128+
public typealias SiteCreationResultHandler = ((SiteCreationResult) -> ())
129+
130+
/// Site creation services, exclusive to WordPress.com.
131+
///
132+
public extension WordPressComServiceRemote {
133+
134+
/// Initiates a request to create a new WPCOM site.
135+
///
136+
/// - Parameters:
137+
/// - request: the value object with which to compose the request.
138+
/// - completion: a closure including the result of the site creation attempt.
139+
///
140+
func createWPComSite(request: SiteCreationRequest, completion: @escaping SiteCreationResultHandler) {
141+
142+
let endpoint = "sites/new"
143+
let path = self.path(forEndpoint: endpoint, withVersion: ._1_1)
144+
145+
let requestParameters: [String : AnyObject]
146+
do {
147+
requestParameters = try encodeRequestParameters(request: request)
148+
} catch {
149+
DDLogError("Failed to encode \(SiteCreationRequest.self) : \(error)")
150+
151+
completion(.failure(SiteCreationError.requestEncodingFailure))
152+
return
153+
}
154+
155+
wordPressComRestApi.POST(
156+
path,
157+
parameters: requestParameters,
158+
success: { [weak self] responseObject, httpResponse in
159+
DDLogInfo("\(responseObject) | \(String(describing: httpResponse))")
160+
161+
guard let self = self else {
162+
return
163+
}
164+
165+
do {
166+
let response = try self.decodeResponse(responseObject: responseObject)
167+
completion(.success(response))
168+
} catch {
169+
DDLogError("Failed to decode \(SiteCreationResponse.self) : \(error.localizedDescription)")
170+
completion(.failure(SiteCreationError.responseDecodingFailure))
171+
}
172+
},
173+
failure: { error, httpResponse in
174+
DDLogError("\(error) | \(String(describing: httpResponse))")
175+
completion(.failure(SiteCreationError.serviceFailure))
176+
})
177+
}
178+
}
179+
180+
// MARK: - Serialization support
181+
182+
private extension WordPressComServiceRemote {
183+
184+
func encodeRequestParameters(request: SiteCreationRequest) throws -> [String : AnyObject] {
185+
186+
let encoder = JSONEncoder()
187+
188+
let jsonData = try encoder.encode(request)
189+
let serializedJSON = try JSONSerialization.jsonObject(with: jsonData, options: [])
190+
191+
let requestParameters: [String : AnyObject]
192+
if let jsonDictionary = serializedJSON as? [String : AnyObject] {
193+
requestParameters = jsonDictionary
194+
} else {
195+
requestParameters = [:]
196+
}
197+
198+
return requestParameters
199+
}
200+
201+
func decodeResponse(responseObject: AnyObject) throws -> SiteCreationResponse {
202+
203+
let decoder = JSONDecoder()
204+
205+
let data = try JSONSerialization.data(withJSONObject: responseObject, options: [])
206+
let response = try decoder.decode(SiteCreationResponse.self, from: data)
207+
208+
return response
209+
}
210+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"success": true,
3+
"blog_details": {
4+
"url": "https://10711c.wordpress.com/",
5+
"blogid": "156355635",
6+
"blogname": "10711c",
7+
"xmlrpc": "https://10711c.wordpress.com/xmlrpc.php"
8+
}
9+
}

0 commit comments

Comments
 (0)