diff --git a/Sources/APIInterface/include/WordPressComRESTAPIInterfacing.h b/Sources/APIInterface/include/WordPressComRESTAPIInterfacing.h index 1f652fd6..5590ba14 100644 --- a/Sources/APIInterface/include/WordPressComRESTAPIInterfacing.h +++ b/Sources/APIInterface/include/WordPressComRESTAPIInterfacing.h @@ -6,6 +6,22 @@ @property (strong, nonatomic, readonly) NSURL * _Nonnull baseURL; +/// Whether the user's preferred language locale should be appended to the request. +/// Should default to `true`. +/// +/// - SeeAlso: `localeKey` and `localeValue` to configure the locale appendend to the request. +@property (nonatomic, readonly) BOOL appendsPreferredLanguageLocale; + +/// The key with which to specify locale in the parameters of a request. +@property (strong, nonatomic, readonly) NSString * _Nonnull localeKey; + +/// The value with which to specify locale in the parameters of a request. +@property (strong, nonatomic, readonly) NSString * _Nonnull localeValue; + +@property (strong, nonatomic, readonly) NSURLSession * _Nonnull urlSession; + +@property (strong, nonatomic, readonly) void (^ _Nullable invalidTokenHandler)(void); + /// - Note: `parameters` has `id` instead of the more common `NSObject *` as its value type so it will convert to `AnyObject` in Swift. /// In Swift, it's simpler to work with `AnyObject` than with `NSObject`. For example `"abc" as AnyObject` over `"abc" as NSObject`. - (NSProgress * _Nullable)get:(NSString * _Nonnull)URLString diff --git a/Sources/WordPressKit/Models/RemotePostParameters.swift b/Sources/WordPressKit/Models/RemotePostParameters.swift index 81849b09..ea5f6685 100644 --- a/Sources/WordPressKit/Models/RemotePostParameters.swift +++ b/Sources/WordPressKit/Models/RemotePostParameters.swift @@ -177,10 +177,14 @@ private enum RemotePostWordPressComCodingKeys: String, CodingKey { static let postTags = "post_tag" } -struct RemotePostCreateParametersWordPressComEncoder: Encodable { +public struct RemotePostCreateParametersWordPressComEncoder: Encodable { let parameters: RemotePostCreateParameters - func encode(to encoder: Encoder) throws { + public init(parameters: RemotePostCreateParameters) { + self.parameters = parameters + } + + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: RemotePostWordPressComCodingKeys.self) try container.encodeIfPresent(parameters.type, forKey: .type) try container.encodeIfPresent(parameters.status, forKey: .status) @@ -212,10 +216,14 @@ struct RemotePostCreateParametersWordPressComEncoder: Encodable { } } -struct RemotePostUpdateParametersWordPressComEncoder: Encodable { +public struct RemotePostUpdateParametersWordPressComEncoder: Encodable { let parameters: RemotePostUpdateParameters - func encode(to encoder: Encoder) throws { + public init(parameters: RemotePostUpdateParameters) { + self.parameters = parameters + } + + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: RemotePostWordPressComCodingKeys.self) try container.encodeIfPresent(parameters.ifNotModifiedSince, forKey: .ifNotModifiedSince) @@ -228,7 +236,7 @@ struct RemotePostUpdateParametersWordPressComEncoder: Encodable { try container.encodeIfPresent(parameters.excerpt, forKey: .excerpt) try container.encodeIfPresent(parameters.slug, forKey: .slug) if let value = parameters.featuredImageID { - if let featuredImageID = value { + if value != nil { try container.encode(parameters.featuredImageID, forKey: .featuredImageID) } else { // Passing `null` doesn't work. @@ -274,10 +282,14 @@ private enum RemotePostXMLRPCCodingKeys: String, CodingKey { static let postTags = "post_tag" } -struct RemotePostCreateParametersXMLRPCEncoder: Encodable { +public struct RemotePostCreateParametersXMLRPCEncoder: Encodable { let parameters: RemotePostCreateParameters - func encode(to encoder: Encoder) throws { + public init(parameters: RemotePostCreateParameters) { + self.parameters = parameters + } + + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: RemotePostXMLRPCCodingKeys.self) try container.encode(parameters.type, forKey: .type) try container.encodeIfPresent(parameters.status, forKey: .postStatus) @@ -309,10 +321,14 @@ struct RemotePostCreateParametersXMLRPCEncoder: Encodable { } } -struct RemotePostUpdateParametersXMLRPCEncoder: Encodable { +public struct RemotePostUpdateParametersXMLRPCEncoder: Encodable { let parameters: RemotePostUpdateParameters - func encode(to encoder: Encoder) throws { + public init(parameters: RemotePostUpdateParameters) { + self.parameters = parameters + } + + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: RemotePostXMLRPCCodingKeys.self) try container.encodeIfPresent(parameters.ifNotModifiedSince, forKey: .ifNotModifiedSince) try container.encodeIfPresent(parameters.status, forKey: .postStatus) @@ -324,7 +340,7 @@ struct RemotePostUpdateParametersXMLRPCEncoder: Encodable { try container.encodeIfPresent(parameters.excerpt, forKey: .excerpt) try container.encodeIfPresent(parameters.slug, forKey: .slug) if let value = parameters.featuredImageID { - if let featuredImageID = value { + if value != nil { try container.encode(parameters.featuredImageID, forKey: .featuredImageID) } else { // Passing `null` doesn't work. diff --git a/Sources/WordPressKit/Services/PostServiceRemoteExtended.swift b/Sources/WordPressKit/Services/PostServiceRemoteExtended.swift deleted file mode 100644 index fb7ed17e..00000000 --- a/Sources/WordPressKit/Services/PostServiceRemoteExtended.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation - -public protocol PostServiceRemoteExtended: PostServiceRemote { - /// Creates a new post with the given parameters. - func createPost(with parameters: RemotePostCreateParameters) async throws -> RemotePost - - /// Performs a partial update to the existing post. - /// - /// - throws: ``PostServiceRemoteUpdatePostError`` or oher underlying errors - /// (see ``WordPressAPIError``) - func patchPost(withID postID: Int, parameters: RemotePostUpdateParameters) async throws -> RemotePost - - /// Permanently deletes a post with the given ID. - /// - /// - throws: ``PostServiceRemoteUpdatePostError`` or oher underlying errors - /// (see ``WordPressAPIError``) - func deletePost(withID postID: Int) async throws -} - -public enum PostServiceRemoteUpdatePostError: Error { - /// 409 (Conflict) - case conflict - /// 404 (Not Found) - case notFound -} diff --git a/Sources/WordPressKit/Services/PostServiceRemoteREST+Extended.swift b/Sources/WordPressKit/Services/PostServiceRemoteREST+Extended.swift deleted file mode 100644 index 6637a785..00000000 --- a/Sources/WordPressKit/Services/PostServiceRemoteREST+Extended.swift +++ /dev/null @@ -1,67 +0,0 @@ -import Foundation - -extension PostServiceRemoteREST: PostServiceRemoteExtended { - public func createPost(with parameters: RemotePostCreateParameters) async throws -> RemotePost { - let path = self.path(forEndpoint: "sites/\(siteID)/posts/new?context=edit", withVersion: ._1_2) - let parameters = try makeParameters(from: RemotePostCreateParametersWordPressComEncoder(parameters: parameters)) - - let response = try await wordPressComRestApi.perform(.post, URLString: path, parameters: parameters).get() - return try await decodePost(from: response.body) - } - - public func patchPost(withID postID: Int, parameters: RemotePostUpdateParameters) async throws -> RemotePost { - let path = self.path(forEndpoint: "sites/\(siteID)/posts/\(postID)?context=edit", withVersion: ._1_2) - let parameters = try makeParameters(from: RemotePostUpdateParametersWordPressComEncoder(parameters: parameters)) - - let result = await wordPressComRestApi.perform(.post, URLString: path, parameters: parameters) - switch result { - case .success(let response): - return try await decodePost(from: response.body) - case .failure(let error): - guard case .endpointError(let error) = error else { - throw error - } - switch error.apiErrorCode ?? "" { - case "unknown_post": throw PostServiceRemoteUpdatePostError.notFound - case "old-revision": throw PostServiceRemoteUpdatePostError.conflict - default: throw error - } - } - } - - public func deletePost(withID postID: Int) async throws { - let path = self.path(forEndpoint: "sites/\(siteID)/posts/\(postID)/delete", withVersion: ._1_1) - let result = await wordPressComRestApi.perform(.post, URLString: path) - switch result { - case .success: - return - case .failure(let error): - guard case .endpointError(let error) = error else { - throw error - } - switch error.apiErrorCode ?? "" { - case "unknown_post": throw PostServiceRemoteUpdatePostError.notFound - default: throw error - } - } - } -} - -// Decodes the post in the background. -private func decodePost(from object: AnyObject) async throws -> RemotePost { - guard let dictionary = object as? [AnyHashable: Any] else { - throw WordPressAPIError.unparsableResponse(response: nil, body: nil) - } - return PostServiceRemoteREST.remotePost(fromJSONDictionary: dictionary) -} - -private func makeParameters(from value: T) throws -> [String: AnyObject] { - let encoder = JSONEncoder() - 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 { - throw URLError(.unknown) // This should never happen - } - return dictionary -} diff --git a/Sources/WordPressKit/Services/PostServiceRemoteXMLRPC+Extended.swift b/Sources/WordPressKit/Services/PostServiceRemoteXMLRPC+Extended.swift deleted file mode 100644 index b1bccf1a..00000000 --- a/Sources/WordPressKit/Services/PostServiceRemoteXMLRPC+Extended.swift +++ /dev/null @@ -1,77 +0,0 @@ -import Foundation -import wpxmlrpc - -extension PostServiceRemoteXMLRPC: PostServiceRemoteExtended { - public func createPost(with parameters: RemotePostCreateParameters) async throws -> RemotePost { - let dictionary = try makeParameters(from: RemotePostCreateParametersXMLRPCEncoder(parameters: parameters)) - let parameters = xmlrpcArguments(withExtra: dictionary) as [AnyObject] - let response = try await api.call(method: "metaWeblog.newPost", parameters: parameters).get() - guard let postID = (response.body as? NSObject)?.numericValue() else { - throw URLError(.unknown) // Should never happen - } - return try await getPost(withID: postID) - } - - public func patchPost(withID postID: Int, parameters: RemotePostUpdateParameters) async throws -> RemotePost { - let dictionary = try makeParameters(from: RemotePostUpdateParametersXMLRPCEncoder(parameters: parameters)) - var parameters = xmlrpcArguments(withExtra: dictionary) as [AnyObject] - if parameters.count > 0 { - parameters[0] = postID as NSNumber - } - let result = await api.call(method: "metaWeblog.editPost", parameters: parameters) - switch result { - case .success: - return try await getPost(withID: postID as NSNumber) - case .failure(let error): - guard case .endpointError(let error) = error else { - throw error - } - switch error.code ?? 0 { - case 404: throw PostServiceRemoteUpdatePostError.notFound - case 409: throw PostServiceRemoteUpdatePostError.conflict - default: throw error - } - } - } - - public func deletePost(withID postID: Int) async throws { - let parameters = xmlrpcArguments(withExtra: postID) as [AnyObject] - let result = await api.call(method: "wp.deletePost", parameters: parameters) - switch result { - case .success: - return - case .failure(let error): - guard case .endpointError(let error) = error else { - throw error - } - switch error.code ?? 0 { - case 404: throw PostServiceRemoteUpdatePostError.notFound - default: throw error - } - } - } - - private func getPost(withID postID: NSNumber) async throws -> RemotePost { - try await withUnsafeThrowingContinuation { continuation in - getPostWithID(postID) { post in - guard let post else { - return continuation.resume(throwing: URLError(.unknown)) // Should never happen - } - continuation.resume(returning: post) - } failure: { error in - continuation.resume(throwing: error ?? URLError(.unknown)) - } - } - } -} - -private func makeParameters(from value: T) throws -> [String: AnyObject] { - let encoder = PropertyListEncoder() - encoder.outputFormat = .xml - let data = try encoder.encode(value) - let object = try PropertyListSerialization.propertyList(from: data, format: nil) - guard let dictionary = object as? [String: AnyObject] else { - throw URLError(.unknown) // This should never happen - } - return dictionary -} diff --git a/Sources/WordPressKit/WordPressAPI/DateFormatter+WordPressCom.swift b/Sources/WordPressKit/WordPressAPI/DateFormatter+WordPressCom.swift index 1bb03baf..a20a1775 100644 --- a/Sources/WordPressKit/WordPressAPI/DateFormatter+WordPressCom.swift +++ b/Sources/WordPressKit/WordPressAPI/DateFormatter+WordPressCom.swift @@ -3,7 +3,7 @@ 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 = { + public static let wordPressCom: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ" formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone diff --git a/Sources/WordPressKit/WordPressAPI/HTTPRequestBuilder.swift b/Sources/WordPressKit/WordPressAPI/HTTPRequestBuilder.swift index c53cf9ca..bb26a907 100644 --- a/Sources/WordPressKit/WordPressAPI/HTTPRequestBuilder.swift +++ b/Sources/WordPressKit/WordPressAPI/HTTPRequestBuilder.swift @@ -5,8 +5,8 @@ import wpxmlrpc /// /// Calling this class's url related functions (the ones that changes path, query, etc) does not modify the /// original URL string. The URL will be perserved in the final result that's returned by the `build` function. -final class HTTPRequestBuilder { - enum Method: String, CaseIterable { +public final class HTTPRequestBuilder { + public enum Method: String, CaseIterable { case get = "GET" case post = "POST" case put = "PUT" diff --git a/Sources/WordPressKit/WordPressAPI/WordPressAPIError.swift b/Sources/WordPressKit/WordPressAPI/WordPressAPIError.swift index a4680b20..508710f1 100644 --- a/Sources/WordPressKit/WordPressAPI/WordPressAPIError.swift +++ b/Sources/WordPressKit/WordPressAPI/WordPressAPIError.swift @@ -18,14 +18,10 @@ public enum WordPressAPIError: Error where EndpointError: Localiz /// The API call returned an status code that's unacceptable to the endpoint. case unacceptableStatusCode(response: HTTPURLResponse, body: Data) /// The API call returned an HTTP response that WordPressKit can't parse. Receiving this error could be an indicator that there is an error response that's not handled properly by WordPressKit. - case unparsableResponse(response: HTTPURLResponse?, body: Data?, underlyingError: Error) + case unparsableResponse(response: HTTPURLResponse?, body: Data?, underlyingError: Error = URLError(.cannotParseResponse)) /// Other error occured. case unknown(underlyingError: Error) - static func unparsableResponse(response: HTTPURLResponse?, body: Data?) -> Self { - return WordPressAPIError.unparsableResponse(response: response, body: body, underlyingError: URLError(.cannotParseResponse)) - } - var response: HTTPURLResponse? { switch self { case .requestEncodingFailure, .connection, .unknown: diff --git a/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift b/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift index a0b9d605..c75407ef 100644 --- a/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift +++ b/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift @@ -95,11 +95,15 @@ open class WordPressComRestApi: NSObject { private let backgroundUploads: Bool - private let localeKey: String + public let localeKey: String + + public var localeValue: String { + WordPressComLanguageDatabase().deviceLanguage.slug + } @objc public let baseURL: URL - private var invalidTokenHandler: (() -> Void)? + public var invalidTokenHandler: (() -> Void)? /** Configure whether or not the user's preferred language locale should be appended. Defaults to true. @@ -173,9 +177,6 @@ open class WordPressComRestApi: NSObject { } } - @objc func setInvalidTokenHandler(_ handler: @escaping () -> Void) { - invalidTokenHandler = handler - } // MARK: Network requests @@ -306,21 +307,6 @@ open class WordPressComRestApi: NSObject { return "\(String(describing: oAuthToken)),\(String(describing: userAgent))".hashValue } - private func requestBuilder(URLString: String) throws -> HTTPRequestBuilder { - guard let url = URL(string: URLString, relativeTo: baseURL) else { - throw URLError(.badURL) - } - - var builder = HTTPRequestBuilder(url: url) - - if appendsPreferredLanguageLocale { - let preferredLanguageIdentifier = WordPressComLanguageDatabase().deviceLanguage.slug - builder = builder.query(defaults: [URLQueryItem(name: localeKey, value: preferredLanguageIdentifier)]) - } - - return builder - } - @objc public func temporaryFileURL(withExtension fileExtension: String) -> URL { assert(!fileExtension.isEmpty, "file Extension cannot be empty") let fileName = "\(ProcessInfo.processInfo.globallyUniqueString)_file.\(fileExtension)" @@ -330,7 +316,7 @@ open class WordPressComRestApi: NSObject { // MARK: - Async - private lazy var urlSession: URLSession = { + public lazy var urlSession: URLSession = { URLSession(configuration: sessionConfiguration(background: false)) }() @@ -360,15 +346,37 @@ open class WordPressComRestApi: NSObject { return configuration } - func perform( - _ method: HTTPRequestBuilder.Method, + public func upload( URLString: String, - parameters: [String: AnyObject]? = nil, + parameters: [String: AnyObject]?, + fileParts: [FilePart], + requestEnqueued: RequestEnqueuedBlock? = nil, fulfilling progress: Progress? = nil ) async -> APIResult { - await perform(method, URLString: URLString, parameters: parameters, fulfilling: progress) { - try (JSONSerialization.jsonObject(with: $0) as AnyObject) + let builder: HTTPRequestBuilder + do { + let form = try fileParts.map { + try MultipartFormField(fileAtPath: $0.url.path, name: $0.parameterName, filename: $0.fileName, mimeType: $0.mimeType) + } + builder = try requestBuilder(URLString: URLString) + .method(.post) + .body(form: form) + } catch { + return .failure(.requestEncodingFailure(underlyingError: error)) } + + return await perform( + request: builder.query(parameters ?? [:]), + fulfilling: progress, + decoder: { try JSONSerialization.jsonObject(with: $0) as AnyObject }, + session: uploadURLSession, + invalidTokenHandler: invalidTokenHandler, + taskCreated: { taskID in + DispatchQueue.main.async { + requestEnqueued?(NSNumber(value: taskID)) + } + } + ) } func perform( @@ -384,8 +392,25 @@ open class WordPressComRestApi: NSObject { return try decoder.decode(type, from: $0) } } +} + +extension WordPressComRESTAPIInterfacing { + + public typealias APIResult = WordPressAPIResult, WordPressComRestApiEndpointError> - private func perform( + public func perform( + _ method: HTTPRequestBuilder.Method, + URLString: String, + parameters: [String: AnyObject]? = nil, + fulfilling progress: Progress? = nil + ) async -> APIResult { + await perform(method, URLString: URLString, parameters: parameters, fulfilling: progress) { + try (JSONSerialization.jsonObject(with: $0) as AnyObject) + } + } + + // FIXME: This was private. It became public during the extraction. Consider whether to make it privated once done. + public func perform( _ method: HTTPRequestBuilder.Method, URLString: String, parameters: [String: AnyObject]?, @@ -394,8 +419,7 @@ open class WordPressComRestApi: NSObject { ) async -> APIResult { var builder: HTTPRequestBuilder do { - builder = try requestBuilder(URLString: URLString) - .method(method) + builder = try requestBuilder(URLString: URLString).method(method) } catch { return .failure(.requestEncodingFailure(underlyingError: error)) } @@ -408,17 +432,25 @@ open class WordPressComRestApi: NSObject { } } - return await perform(request: builder, fulfilling: progress, decoder: decoder) + return await perform( + request: builder, + fulfilling: progress, + decoder: decoder, + session: urlSession, + invalidTokenHandler: invalidTokenHandler + ) } - private func perform( + // FIXME: This was private. It became public during the extraction. Consider whether to make it privated once done. + public func perform( request: HTTPRequestBuilder, fulfilling progress: Progress?, decoder: @escaping (Data) throws -> T, - taskCreated: ((Int) -> Void)? = nil, - session: URLSession? = nil + session: URLSession, + invalidTokenHandler: (() -> Void)?, + taskCreated: ((Int) -> Void)? = nil ) async -> APIResult { - await (session ?? self.urlSession) + await session .perform(request: request, taskCreated: taskCreated, fulfilling: progress, errorType: WordPressComRestApiEndpointError.self) .mapSuccess { response -> HTTPAPIResponse in let object = try decoder(response.body) @@ -426,7 +458,7 @@ open class WordPressComRestApi: NSObject { return HTTPAPIResponse(response: response.response, body: object) } .mapUnacceptableStatusCodeError { response, body in - if let error = self.processError(response: response, body: body, additionalUserInfo: nil) { + if let error = self.processError(response: response, body: body, additionalUserInfo: nil, invalidTokenHandler: invalidTokenHandler) { return error } @@ -444,45 +476,28 @@ open class WordPressComRestApi: NSObject { } } - public func upload( - URLString: String, - parameters: [String: AnyObject]?, - fileParts: [FilePart], - requestEnqueued: RequestEnqueuedBlock? = nil, - fulfilling progress: Progress? = nil - ) async -> APIResult { - let builder: HTTPRequestBuilder - do { - let form = try fileParts.map { - try MultipartFormField(fileAtPath: $0.url.path, name: $0.parameterName, filename: $0.fileName, mimeType: $0.mimeType) - } - builder = try requestBuilder(URLString: URLString) - .method(.post) - .body(form: form) - } catch { - return .failure(.requestEncodingFailure(underlyingError: error)) + func requestBuilder(URLString: String) throws -> HTTPRequestBuilder { + let locale: (String, String)? + if appendsPreferredLanguageLocale { + locale = (localeKey, localeValue) + } else { + locale = nil } - return await perform( - request: builder.query(parameters ?? [:]), - fulfilling: progress, - decoder: { try JSONSerialization.jsonObject(with: $0) as AnyObject }, - taskCreated: { taskID in - DispatchQueue.main.async { - requestEnqueued?(NSNumber(value: taskID)) - } - }, - session: uploadURLSession - ) + return try HTTPRequestBuilder.with(URLString: URLString, relativeTo: baseURL, appendingLocale: locale) } - } // MARK: - Error processing -extension WordPressComRestApi { +extension WordPressComRESTAPIInterfacing { - func processError(response httpResponse: HTTPURLResponse, body data: Data, additionalUserInfo: [String: Any]?) -> WordPressComRestApiEndpointError? { + func processError( + response httpResponse: HTTPURLResponse, + body data: Data, + additionalUserInfo: [String: Any]?, + invalidTokenHandler: (() -> Void)? + ) -> WordPressComRestApiEndpointError? { // Not sure if it's intentional to include 500 status code, but the code seems to be there from the very beginning. // https://github.com/wordpress-mobile/WordPressKit-iOS/blob/1.0.1/WordPressKit/WordPressComRestApi.swift#L374 guard (400...500).contains(httpResponse.statusCode) else { @@ -528,7 +543,7 @@ extension WordPressComRestApi { if mappedError == .invalidToken { // Call `invalidTokenHandler in the main thread since it's typically used by the apps to present an authentication UI. DispatchQueue.main.async { - self.invalidTokenHandler?() + invalidTokenHandler?() } } @@ -564,6 +579,28 @@ extension WordPressComRestApi { ) } } + +extension HTTPRequestBuilder { + + static func with( + URLString: String, + relativeTo baseURL: URL, + appendingLocale locale: (key: String, value: String)? + ) throws -> HTTPRequestBuilder { + guard let url = URL(string: URLString, relativeTo: baseURL) else { + throw URLError(.badURL) + } + + let builder = Self.init(url: url) + + guard let locale else { + return builder + } + + return builder.query(defaults: [URLQueryItem(name: locale.key, value: locale.value)]) + } + +} // MARK: - Anonymous API support extension WordPressComRestApi { diff --git a/Sources/WordPressKit/WordPressAPI/WordPressOrgXMLRPCApi.swift b/Sources/WordPressKit/WordPressAPI/WordPressOrgXMLRPCApi.swift index 2698c89f..4bb825eb 100644 --- a/Sources/WordPressKit/WordPressAPI/WordPressOrgXMLRPCApi.swift +++ b/Sources/WordPressKit/WordPressAPI/WordPressOrgXMLRPCApi.swift @@ -180,7 +180,7 @@ open class WordPressOrgXMLRPCApi: NSObject { /// - Parameters: /// - streaming: set to `true` if there are large data (i.e. uploading files) in given `parameters`. `false` by default. /// - Returns: A `Result` type that contains the XMLRPC success or failure result. - func call(method: String, parameters: [AnyObject]?, fulfilling progress: Progress? = nil, streaming: Bool = false) async -> WordPressAPIResult, WordPressOrgXMLRPCApiFault> { + public func call(method: String, parameters: [AnyObject]?, fulfilling progress: Progress? = nil, streaming: Bool = false) async -> WordPressAPIResult, WordPressOrgXMLRPCApiFault> { let session = streaming ? uploadURLSession : urlSession let builder = HTTPRequestBuilder(url: endpoint) .method(.post) diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index 393460ce..7a663384 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -13,9 +13,6 @@ 0152100C28EDA9E400DD6783 /* StatsAnnualAndMostPopularTimeInsightDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0152100B28EDA9E400DD6783 /* StatsAnnualAndMostPopularTimeInsightDecodingTests.swift */; }; 0847B92C2A4442730044D32F /* IPLocationRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0847B92B2A4442730044D32F /* IPLocationRemote.swift */; }; 08C7493E2A45EA11000DA0E2 /* IPLocationRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08C7493D2A45EA11000DA0E2 /* IPLocationRemoteTests.swift */; }; - 0C1C08412B9CD79900E52F8C /* PostServiceRemoteExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */; }; - 0C1C08432B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */; }; - 0C1C08452B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */; }; 0C9CD7992B9A107E0045BE03 /* RemotePostParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9CD7982B9A107E0045BE03 /* RemotePostParameters.swift */; }; 0CB1905E2A2A5E83004D3E80 /* BlazeCampaign.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB1905D2A2A5E83004D3E80 /* BlazeCampaign.swift */; }; 0CB190612A2A6A13004D3E80 /* blaze-campaigns-search.json in Resources */ = {isa = PBXBuildFile; fileRef = 0CB1905F2A2A6943004D3E80 /* blaze-campaigns-search.json */; }; @@ -757,9 +754,6 @@ 0152100B28EDA9E400DD6783 /* StatsAnnualAndMostPopularTimeInsightDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsAnnualAndMostPopularTimeInsightDecodingTests.swift; sourceTree = ""; }; 0847B92B2A4442730044D32F /* IPLocationRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPLocationRemote.swift; sourceTree = ""; }; 08C7493D2A45EA11000DA0E2 /* IPLocationRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPLocationRemoteTests.swift; sourceTree = ""; }; - 0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostServiceRemoteExtended.swift; sourceTree = ""; }; - 0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteREST+Extended.swift"; sourceTree = ""; }; - 0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteXMLRPC+Extended.swift"; sourceTree = ""; }; 0C3A2A412A2E7BA500FD91D6 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 0C9CD7982B9A107E0045BE03 /* RemotePostParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemotePostParameters.swift; sourceTree = ""; }; 0CB1905D2A2A5E83004D3E80 /* BlazeCampaign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlazeCampaign.swift; sourceTree = ""; }; @@ -1779,15 +1773,12 @@ E1BD95141FD5A2B800CD5CE3 /* PluginDirectoryServiceRemote.swift */, E13EE1461F33258E00C15787 /* PluginServiceRemote.swift */, 740B23B21F17EC7300067A2A /* PostServiceRemote.h */, - 0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */, 740B23BC1F17ECB500067A2A /* PostServiceRemoteOptions.h */, 740B23B31F17EC7300067A2A /* PostServiceRemoteREST.h */, 740B23B41F17EC7300067A2A /* PostServiceRemoteREST.m */, - 0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */, 9AF4F2FB218331DC00570E4B /* PostServiceRemoteREST+Revisions.swift */, 740B23B51F17EC7300067A2A /* PostServiceRemoteXMLRPC.h */, 740B23B61F17EC7300067A2A /* PostServiceRemoteXMLRPC.m */, - 0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */, F181EA0127184D3C00F26141 /* ProductServiceRemote.swift */, 74A44DCA1F13C533006CD8F4 /* PushAuthenticationServiceRemote.swift */, C7A09A4F284104DB003096ED /* QR Login */, @@ -3342,18 +3333,15 @@ 436D56352118D85800CEAA33 /* WPCountry.swift in Sources */, 74A44DCB1F13C533006CD8F4 /* NotificationSettingsServiceRemote.swift in Sources */, FAD1344525908F5F00A8FEB1 /* JetpackBackupServiceRemote.swift in Sources */, - 0C1C08452B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift in Sources */, F1BB7806240FB90B0030ADDC /* AtomicAuthenticationServiceRemote.swift in Sources */, 404057CE221C38130060250C /* StatsTopVideosTimeIntervalData.swift in Sources */, 7E0D64FF22D855700092AD10 /* EditorServiceRemote.swift in Sources */, - 0C1C08412B9CD79900E52F8C /* PostServiceRemoteExtended.swift in Sources */, 9AF4F2FF2183346B00570E4B /* RemoteRevision.swift in Sources */, FE6C673A2BB739950083ECAB /* Decodable+Dictionary.swift in Sources */, 17D936252475D8AB008B2205 /* RemoteHomepageType.swift in Sources */, 74BA04F41F06DC0A00ED5CD8 /* CommentServiceRemoteREST.m in Sources */, 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 */, E1D6B558200E473A00325669 /* TimeZoneServiceRemote.swift in Sources */,