From 3933ff929b31ee975c6600b12f1f51c05e1c587c Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 25 Mar 2024 16:47:02 +1100 Subject: [PATCH 1/6] Convert `FilePart` to Objective-C to be used in API abstraction layer --- WordPressKit.xcodeproj/project.pbxproj | 26 ++++++++++++------- WordPressKit/FilePart.h | 16 ++++++++++++ WordPressKit/FilePart.m | 18 +++++++++++++ WordPressKit/MediaServiceRemoteREST.m | 4 +-- WordPressKit/PostServiceRemoteREST.m | 2 +- WordPressKit/WordPressComRestApi.swift | 19 +------------- WordPressKit/WordPressKit.h | 2 ++ .../WordPressComRestApiTests.swift | 4 +-- 8 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 WordPressKit/FilePart.h create mode 100644 WordPressKit/FilePart.m diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index d84fd093..85e43c0c 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -71,6 +71,8 @@ 3F758FD324F6C68200BBA2FC /* AnnouncementServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */; }; 3F8308A729EE683500354497 /* ActivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8308A629EE683500354497 /* ActivityTests.swift */; }; 3FB8642C2888089F003A86BE /* BuildkiteTestCollector in Frameworks */ = {isa = PBXBuildFile; productRef = 3FB8642B2888089F003A86BE /* BuildkiteTestCollector */; }; + 3FE2E9492BB14322002CA2E1 /* FilePart.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FE2E9482BB14322002CA2E1 /* FilePart.m */; }; + 3FE2E94B2BB14342002CA2E1 /* FilePart.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FE2E94A2BB1433B002CA2E1 /* FilePart.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3FFCC0412BA995290051D229 /* Date+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */; }; 3FFCC0472BAA6EF40051D229 /* NSDate+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */; }; 3FFCC0492BAB98130051D229 /* DateFormatter+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */; }; @@ -803,6 +805,8 @@ 3F758FD224F6C68200BBA2FC /* AnnouncementServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementServiceRemote.swift; sourceTree = ""; }; 3F8308A629EE683500354497 /* ActivityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTests.swift; sourceTree = ""; }; 3FB8642D288813E9003A86BE /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; + 3FE2E9482BB14322002CA2E1 /* FilePart.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FilePart.m; sourceTree = ""; }; + 3FE2E94A2BB1433B002CA2E1 /* FilePart.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilePart.h; sourceTree = ""; }; 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+WordPressComTests.swift"; sourceTree = ""; }; 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+WordPressCom.swift"; sourceTree = ""; }; 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressCom.swift"; sourceTree = ""; }; @@ -2528,22 +2532,24 @@ 93BD27731EE7388E002BB00B /* WordPressAPI */ = { isa = PBXGroup; children = ( - 3FFCC0552BABC78B0051D229 /* WordPressComRESTAPIInterfacing.h */, - 4A05E7952B2FCB6400C25E3B /* NonceRetrieval.swift */, - 4A05E7992B2FDC3200C25E3B /* WordPressOrgRestApi.swift */, - 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */, - 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */, 3FFCC04E2BABA6E60051D229 /* Date+WordPressCom.swift */, 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */, + 3FE2E94A2BB1433B002CA2E1 /* FilePart.h */, + 3FE2E9482BB14322002CA2E1 /* FilePart.m */, + 93BD27741EE73944002BB00B /* HTTPAuthenticationAlertController.swift */, + 4A11239D2B1926D1004690CF /* HTTPClient.swift */, + 4A11239B2B1926B7004690CF /* HTTPRequestBuilder.swift */, + 4A05E7952B2FCB6400C25E3B /* NonceRetrieval.swift */, + 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */, + 4A1123992B19269A004690CF /* WordPressAPIError.swift */, + 4A57A6822B54A326008D0660 /* WordPressAPIError+NSErrorBrdige.swift */, 93BD27771EE73944002BB00B /* WordPressComOAuthClient.swift */, 93BD27781EE73944002BB00B /* WordPressComRestApi.swift */, + 3FFCC0552BABC78B0051D229 /* WordPressComRESTAPIInterfacing.h */, + 4A05E7992B2FDC3200C25E3B /* WordPressOrgRestApi.swift */, 93BD27791EE73944002BB00B /* WordPressOrgXMLRPCApi.swift */, 93BD277A1EE73944002BB00B /* WordPressOrgXMLRPCValidator.swift */, 93BD277B1EE73944002BB00B /* WordPressRSDParser.swift */, - 4A11239D2B1926D1004690CF /* HTTPClient.swift */, - 4A11239B2B1926B7004690CF /* HTTPRequestBuilder.swift */, - 4A1123992B19269A004690CF /* WordPressAPIError.swift */, - 4A57A6822B54A326008D0660 /* WordPressAPIError+NSErrorBrdige.swift */, ); name = WordPressAPI; sourceTree = ""; @@ -2810,6 +2816,7 @@ 9368C78C1EC5EF1B0092CE8E /* WordPressKit.h in Headers */, 93C674F11EE8351E00BFAF05 /* NSMutableDictionary+Helpers.h in Headers */, 93BD273C1EE73282002BB00B /* AccountServiceRemoteREST.h in Headers */, + 3FE2E94B2BB14342002CA2E1 /* FilePart.h in Headers */, 93BD27711EE737A8002BB00B /* ServiceRemoteWordPressXMLRPC.h in Headers */, 93BD276F1EE737A8002BB00B /* ServiceRemoteWordPressComREST.h in Headers */, 93BD273B1EE73282002BB00B /* AccountServiceRemote.h in Headers */, @@ -3382,6 +3389,7 @@ 4A68E3D529406AA0004AC3DC /* RemoteMenuLocation.swift in Sources */, 8B16CE8E25250039007BE5A9 /* RemoteReaderPost.swift in Sources */, 74E2294E1F1E73FE0085F7F2 /* RemotePublicizeService.swift in Sources */, + 3FE2E9492BB14322002CA2E1 /* FilePart.m in Sources */, 8B749DED25AF3E4600023F03 /* JetpackCapabilitiesServiceRemote.swift in Sources */, 9F3E0BA22087345F009CB5BA /* ServiceRequest.swift in Sources */, E1A6605F1FD694ED00BAC339 /* PluginDirectoryEntry.swift in Sources */, diff --git a/WordPressKit/FilePart.h b/WordPressKit/FilePart.h new file mode 100644 index 00000000..5590a5fc --- /dev/null +++ b/WordPressKit/FilePart.h @@ -0,0 +1,16 @@ +#import + +/// Represents the infomartion needed to encode a file on a multipart form request. +@interface FilePart: NSObject + +@property (strong, nonatomic) NSString * _Nonnull parameterName; +@property (strong, nonatomic) NSURL * _Nonnull url; +@property (strong, nonatomic) NSString * _Nonnull fileName; +@property (strong, nonatomic) NSString * _Nonnull mimeType; + +- (instancetype _Nonnull)initWithParameterName:(NSString * _Nonnull)parameterName + url:(NSURL * _Nonnull)url + fileName:(NSString * _Nonnull)fileName + mimeType:(NSString * _Nonnull)mimeType; + +@end diff --git a/WordPressKit/FilePart.m b/WordPressKit/FilePart.m new file mode 100644 index 00000000..5648f04d --- /dev/null +++ b/WordPressKit/FilePart.m @@ -0,0 +1,18 @@ +#import "FilePart.h" + +@implementation FilePart + +- (instancetype)initWithParameterName:(NSString *)parameterName + url:(NSURL *)url + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType +{ + self = [super init]; + self.parameterName = parameterName; + self.url = url; + self.fileName = fileName; + self.mimeType = mimeType; + return self; +} + +@end diff --git a/WordPressKit/MediaServiceRemoteREST.m b/WordPressKit/MediaServiceRemoteREST.m index 4652c811..8586e0e9 100644 --- a/WordPressKit/MediaServiceRemoteREST.m +++ b/WordPressKit/MediaServiceRemoteREST.m @@ -139,7 +139,7 @@ - (void)uploadMedia:(NSArray *)mediaItems if (remoteMedia.postID != nil && [remoteMedia.postID compare:@(0)] == NSOrderedDescending) { parameters[@"attrs[0][parent_id]"] = remoteMedia.postID; } - FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:remoteMedia.localURL filename:filename mimeType:type]; + FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:remoteMedia.localURL fileName:filename mimeType:type]; [fileParts addObject:filePart]; } @@ -203,7 +203,7 @@ - (void)uploadMedia:(RemoteMedia *)media } return; } - FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:media.localURL filename:filename mimeType:type]; + FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:media.localURL fileName:filename mimeType:type]; __block NSProgress *localProgress = [self.wordPressComRestApi multipartPOST:requestUrl parameters:parameters fileParts:@[filePart] diff --git a/WordPressKit/PostServiceRemoteREST.m b/WordPressKit/PostServiceRemoteREST.m index c072091f..6924d461 100644 --- a/WordPressKit/PostServiceRemoteREST.m +++ b/WordPressKit/PostServiceRemoteREST.m @@ -164,7 +164,7 @@ - (void)createPost:(RemotePost *)post parameters[@"content"] = post.content; parameters[@"title"] = post.title; parameters[@"status"] = post.status; - FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:media.localURL filename:filename mimeType:type]; + FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:media.localURL fileName:filename mimeType:type]; [self.wordPressComRestApi multipartPOST:requestUrl parameters:parameters fileParts:@[filePart] diff --git a/WordPressKit/WordPressComRestApi.swift b/WordPressKit/WordPressComRestApi.swift index 3d919297..123c81a2 100644 --- a/WordPressKit/WordPressComRestApi.swift +++ b/WordPressKit/WordPressComRestApi.swift @@ -452,7 +452,7 @@ open class WordPressComRestApi: NSObject { let builder: HTTPRequestBuilder do { let form = try fileParts.map { - try MultipartFormField(fileAtPath: $0.url.path, name: $0.parameterName, filename: $0.filename, mimeType: $0.mimeType) + try MultipartFormField(fileAtPath: $0.url.path, name: $0.parameterName, filename: $0.fileName, mimeType: $0.mimeType) } builder = try requestBuilder(URLString: URLString) .method(.post) @@ -476,23 +476,6 @@ open class WordPressComRestApi: NSObject { } -// MARK: - FilePart - -/// FilePart represents the infomartion needed to encode a file on a multipart form request -public final class FilePart: NSObject { - @objc let parameterName: String - @objc let url: URL - @objc let filename: String - @objc let mimeType: String - - @objc public init(parameterName: String, url: URL, filename: String, mimeType: String) { - self.parameterName = parameterName - self.url = url - self.filename = filename - self.mimeType = mimeType - } -} - // MARK: - Error processing extension WordPressComRestApi { diff --git a/WordPressKit/WordPressKit.h b/WordPressKit/WordPressKit.h index 657e44fc..9552d17f 100644 --- a/WordPressKit/WordPressKit.h +++ b/WordPressKit/WordPressKit.h @@ -6,7 +6,9 @@ FOUNDATION_EXPORT double WordPressKitVersionNumber; //! Project version string for WordPressKit. FOUNDATION_EXPORT const unsigned char WordPressKitVersionString[]; +#import #import + #import #import #import diff --git a/WordPressKitTests/WordPressComRestApiTests.swift b/WordPressKitTests/WordPressComRestApiTests.swift index 877096a5..611b4298 100644 --- a/WordPressKitTests/WordPressComRestApiTests.swift +++ b/WordPressKitTests/WordPressComRestApiTests.swift @@ -295,7 +295,7 @@ class WordPressComRestApiTests: XCTestCase { let expect = self.expectation(description: "One callback should be invoked") let api = WordPressComRestApi(oAuthToken: "fakeToken") - let filePart = FilePart(parameterName: "file", url: URL(fileURLWithPath: "/a.txt") as URL, filename: "a.txt", mimeType: "image/jpeg") + let filePart = FilePart(parameterName: "file", url: URL(fileURLWithPath: "/a.txt") as URL, fileName: "a.txt", mimeType: "image/jpeg") api.multipartPOST(wordPressMediaNewEndpointPath, parameters: nil, fileParts: [filePart], success: { (_: AnyObject, _: HTTPURLResponse?) in expect.fulfill() XCTFail("This call should fail") @@ -319,7 +319,7 @@ class WordPressComRestApiTests: XCTestCase { let mediaURL = URL(fileURLWithPath: mediaPath) let expect = self.expectation(description: "One callback should be invoked") let api = WordPressComRestApi(oAuthToken: "fakeToken") - let filePart = FilePart(parameterName: "media[]", url: mediaURL as URL, filename: "test-image.jpg", mimeType: "image/jpeg") + let filePart = FilePart(parameterName: "media[]", url: mediaURL as URL, fileName: "test-image.jpg", mimeType: "image/jpeg") let progress1 = api.multipartPOST(wordPressMediaNewEndpointPath, parameters: nil, fileParts: [filePart], success: { (_: AnyObject, _: HTTPURLResponse?) in XCTFail("This call should fail") }, failure: { (error, _) in From 9097eb9a7b2cd039c7b280e725661d0b9294d8f1 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 25 Mar 2024 16:48:11 +1100 Subject: [PATCH 2/6] Add `CHANGELOG` entry for `FilePart` breaking change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 543863c2..5b764f22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ _None._ ### Breaking Changes - Reworked the `NSDate` RFC3339 / WordPress.com JSON conversions API [#759] +- Changed `FilePart` `filename` property to `fileName` [???] ### New Features From 35850def40dc841a108b27725ac975c3edf34c6e Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 25 Mar 2024 16:48:53 +1100 Subject: [PATCH 3/6] Define multi-part form POST method in `WordPressComRESTAPIInterfacing` --- WordPressKit/WordPressComRESTAPIInterfacing.h | 9 ++++++++ WordPressKit/WordPressComRestApi.swift | 23 ++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/WordPressKit/WordPressComRESTAPIInterfacing.h b/WordPressKit/WordPressComRESTAPIInterfacing.h index 119f52d0..7bb1350d 100644 --- a/WordPressKit/WordPressComRESTAPIInterfacing.h +++ b/WordPressKit/WordPressComRESTAPIInterfacing.h @@ -1,5 +1,7 @@ @import Foundation; +@class FilePart; + @protocol WordPressComRESTAPIInterfacing @property (strong, nonatomic, readonly) NSURL * _Nonnull baseURL; @@ -14,4 +16,11 @@ success:(void (^ _Nonnull)(id _Nonnull, NSHTTPURLResponse * _Nullable))success failure:(void (^ _Nonnull)(NSError * _Nonnull, NSHTTPURLResponse * _Nullable))failure; +- (NSProgress * _Nullable)multipartPOST:(NSString * _Nonnull)URLString + parameters:(NSDictionary * _Nullable)parameters + fileParts:(NSArray * _Nonnull)fileParts + requestEnqueued:(void (^ _Nonnull)(NSNumber * _Nonnull))requestEnqueue + success:(void (^ _Nonnull)(id _Nonnull, NSHTTPURLResponse * _Nullable))success + failure:(void (^ _Nonnull)(NSError * _Nonnull, NSHTTPURLResponse * _Nullable))failure; + @end diff --git a/WordPressKit/WordPressComRestApi.swift b/WordPressKit/WordPressComRestApi.swift index 123c81a2..afadae2c 100644 --- a/WordPressKit/WordPressComRestApi.swift +++ b/WordPressKit/WordPressComRestApi.swift @@ -268,11 +268,11 @@ open class WordPressComRestApi: NSObject { - parameter success: callback to be called on successful request - parameter failure: callback to be called on failed request - - returns: a NSProgress object that can be used to track the progress of the upload and to cancel the upload. If the method + - returns: a `Progerss` object that can be used to track the progress of the upload and to cancel the upload. If the method returns nil it's because something happened on the request serialization and the network request was not started, but the failure callback will be invoked with the error specificing the serialization issues. */ - @objc @discardableResult open func multipartPOST(_ URLString: String, + @discardableResult open func multipartPOST(_ URLString: String, parameters: [String: AnyObject]?, fileParts: [FilePart], requestEnqueued: RequestEnqueuedBlock? = nil, @@ -605,7 +605,6 @@ extension WordPressAPIError { } extension WordPressComRestApi: WordPressComRESTAPIInterfacing { - // A note on the naming: Even if defined as `GET` in Objective-C, then method gets converted to Swift as `get`. // // Also, there is no Objective-C direct equivalent of `AnyObject`, which here is used in `parameters: [String: AnyObject]?`. @@ -629,4 +628,22 @@ extension WordPressComRestApi: WordPressComRESTAPIInterfacing { ) -> Progress? { POST(URLString, parameters: parameters, success: success, failure: failure) } + + public func multipartPOST( + _ URLString: String, + parameters: [String: NSObject]?, + fileParts: [FilePart], + requestEnqueued: @escaping (NSNumber) -> Void, + success: @escaping (Any, HTTPURLResponse?) -> Void, + failure: @escaping (any Error, HTTPURLResponse?) -> Void + ) -> Progress? { + multipartPOST( + URLString, + parameters: parameters, + fileParts: fileParts, + requestEnqueued: requestEnqueued, + success: success as SuccessResponseBlock, + failure: failure as FailureReponseBlock + ) + } } From 93b0a9ef238a410e7a131ad4e47c3232ed9d18e9 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 25 Mar 2024 19:34:22 +1100 Subject: [PATCH 4/6] Use `WordPressComRESTAPIInterfacing` to run ObjC multipart POST --- WordPressKit/MediaServiceRemoteREST.m | 4 ++-- WordPressKit/PostServiceRemoteREST.m | 2 +- WordPressKit/WordPressComRESTAPIInterfacing.h | 2 +- WordPressKit/WordPressComRestApi.swift | 8 +++++++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/WordPressKit/MediaServiceRemoteREST.m b/WordPressKit/MediaServiceRemoteREST.m index 8586e0e9..9d684c3b 100644 --- a/WordPressKit/MediaServiceRemoteREST.m +++ b/WordPressKit/MediaServiceRemoteREST.m @@ -143,7 +143,7 @@ - (void)uploadMedia:(NSArray *)mediaItems [fileParts addObject:filePart]; } - [self.wordPressComRestApi multipartPOST:requestUrl + [self.wordPressComRESTAPI multipartPOST:requestUrl parameters:parameters fileParts:fileParts requestEnqueued:^(NSNumber *taskID) { @@ -204,7 +204,7 @@ - (void)uploadMedia:(RemoteMedia *)media return; } FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:media.localURL fileName:filename mimeType:type]; - __block NSProgress *localProgress = [self.wordPressComRestApi multipartPOST:requestUrl + __block NSProgress *localProgress = [self.wordPressComRESTAPI multipartPOST:requestUrl parameters:parameters fileParts:@[filePart] requestEnqueued:nil diff --git a/WordPressKit/PostServiceRemoteREST.m b/WordPressKit/PostServiceRemoteREST.m index 6924d461..50401de1 100644 --- a/WordPressKit/PostServiceRemoteREST.m +++ b/WordPressKit/PostServiceRemoteREST.m @@ -165,7 +165,7 @@ - (void)createPost:(RemotePost *)post parameters[@"title"] = post.title; parameters[@"status"] = post.status; FilePart *filePart = [[FilePart alloc] initWithParameterName:@"media[]" url:media.localURL fileName:filename mimeType:type]; - [self.wordPressComRestApi multipartPOST:requestUrl + [self.wordPressComRESTAPI multipartPOST:requestUrl parameters:parameters fileParts:@[filePart] requestEnqueued:^(NSNumber *taskID) { diff --git a/WordPressKit/WordPressComRESTAPIInterfacing.h b/WordPressKit/WordPressComRESTAPIInterfacing.h index 7bb1350d..e1a669e8 100644 --- a/WordPressKit/WordPressComRESTAPIInterfacing.h +++ b/WordPressKit/WordPressComRESTAPIInterfacing.h @@ -19,7 +19,7 @@ - (NSProgress * _Nullable)multipartPOST:(NSString * _Nonnull)URLString parameters:(NSDictionary * _Nullable)parameters fileParts:(NSArray * _Nonnull)fileParts - requestEnqueued:(void (^ _Nonnull)(NSNumber * _Nonnull))requestEnqueue + requestEnqueued:(void (^ _Nullable)(NSNumber * _Nonnull))requestEnqueue success:(void (^ _Nonnull)(id _Nonnull, NSHTTPURLResponse * _Nullable))success failure:(void (^ _Nonnull)(NSError * _Nonnull, NSHTTPURLResponse * _Nullable))failure; diff --git a/WordPressKit/WordPressComRestApi.swift b/WordPressKit/WordPressComRestApi.swift index afadae2c..868fbead 100644 --- a/WordPressKit/WordPressComRestApi.swift +++ b/WordPressKit/WordPressComRestApi.swift @@ -633,7 +633,13 @@ extension WordPressComRestApi: WordPressComRESTAPIInterfacing { _ URLString: String, parameters: [String: NSObject]?, fileParts: [FilePart], - requestEnqueued: @escaping (NSNumber) -> Void, + // Notice this does not require @escaping because it is Optional. + // + // Annotate with @escaping, and the compiler will fail with: + // > Closure is already escaping in optional type argument + // + // It is necessary to explicitly set this as Optional because of the _Nullable parameter requirement in the Objective-C protocol. + requestEnqueued: ((NSNumber) -> Void)?, success: @escaping (Any, HTTPURLResponse?) -> Void, failure: @escaping (any Error, HTTPURLResponse?) -> Void ) -> Progress? { From 52a1a5c0e93686ff45589512d556a4e9360e96db Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 25 Mar 2024 19:40:19 +1100 Subject: [PATCH 5/6] Add PR number to `CHANGELOG` entry for `FilePart` breakage --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b764f22..f53db4f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ _None._ ### Breaking Changes - Reworked the `NSDate` RFC3339 / WordPress.com JSON conversions API [#759] -- Changed `FilePart` `filename` property to `fileName` [???] +- Changed `FilePart` `filename` property to `fileName` [#765] ### New Features From d7d0bf74194bc7953eaa4dc36a87eab85f06a276 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Tue, 26 Mar 2024 16:55:49 +1100 Subject: [PATCH 6/6] Mark the Swift native multipart POST implementation as `@nonobjc` Co-authored-by: Tony Li --- .../WordPressAPI/WordPressComRestApi.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift b/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift index 868fbead..3764903c 100644 --- a/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift +++ b/Sources/WordPressKit/WordPressAPI/WordPressComRestApi.swift @@ -272,12 +272,14 @@ open class WordPressComRestApi: NSObject { returns nil it's because something happened on the request serialization and the network request was not started, but the failure callback will be invoked with the error specificing the serialization issues. */ - @discardableResult open func multipartPOST(_ URLString: String, - parameters: [String: AnyObject]?, - fileParts: [FilePart], - requestEnqueued: RequestEnqueuedBlock? = nil, - success: @escaping SuccessResponseBlock, - failure: @escaping FailureReponseBlock) -> Progress? { + @nonobjc @discardableResult open func multipartPOST( + _ URLString: String, + parameters: [String: AnyObject]?, + fileParts: [FilePart], + requestEnqueued: RequestEnqueuedBlock? = nil, + success: @escaping SuccessResponseBlock, + failure: @escaping FailureReponseBlock + ) -> Progress? { let progress = Progress.discreteProgress(totalUnitCount: 100) Task { @MainActor in