Moya: Small refactor to MultipartFormData.

Created on 10 Jun 2020  路  2Comments  路  Source: Moya/Moya

Related: #1988 #2050 and probably few more.

Problem

So currently we have our own type for MultipartFormData, which is in fact a single part of that whole data (which is a bit different than the one from Alamofire):

public struct MultipartFormData {
    /// Method to provide the form data.
    public enum FormDataProvider {
        case data(Foundation.Data)
        case file(URL)
        case stream(InputStream, UInt64)
    }

    public init(provider: FormDataProvider, name: String, fileName: String? = nil, mimeType: String? = nil) {
        self.provider = provider
        self.name = name
        self.fileName = fileName
        self.mimeType = mimeType
    }

    /// The method being used for providing form data.
    public let provider: FormDataProvider

    /// The name.
    public let name: String

    /// The file name.
    public let fileName: String?

    /// The MIME type
    public let mimeType: String?
}

then we can pass an array of these in the Task:

enum Task {
    ...
    case uploadMultipart([MultipartFormData])
    ...
}

and then Moya will transform this array into the proper, Alamofire-provided MultipartFormData:

extension MoyaProvider {
    ...
    func sendUploadMultipart(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, multipartBody: [MultipartFormData], progress: Moya.ProgressBlock? = nil, completion: @escaping Moya.Completion) -> CancellableToken {
        let formData = RequestMultipartFormData()
        formData.applyMoyaMultipartFormData(multipartBody)
    ...
    }
}

but this way we lack the ability to provide the fileManager and boundary properties that Alamofire allows:

open class MultipartFormData {
    ...
    public init(fileManager: FileManager = .default, boundary: String? = nil) {
        self.fileManager = fileManager
        self.boundary = boundary ?? BoundaryGenerator.randomBoundary()
        bodyParts = []
    }
    ...
}

Solution

So two solutions comes to my mind. First, just use the multipart body provided by Alamofire. Though I really like the way we have it as an enum and the transition from enum to structs might be more time-consuming.

So I think we could refactor a little bit what we have. How?

  • Rename the MultipartFormData to MultipartBodyPart (or something else, just an idea)
  • Create new struct, called MultipartFormData which could look like the following:
struct MultipartFormData {
    init(fileManager: FileManager = .default, boundary: String? = nil, parts: [MultipartBodyPart]) {
        ...
    }
}

the callsite would change from:

enum MyTarget: Target {
    var task: Task {
        return .uploadMultipart([data, param])
    }
}

to:

enum MyTarget: Target {
    var task: Task {
        return .uploadMultipart(MultipartFormData(parts: [data, param]))
    }
}

Additionally, we could try and leverage ExpressibleByArrayLiteral so we could potentially not change the callsite at all for existing implementations and just add an option to provide these details.

thoughts? cc @Moya/contributors

enhancement help wanted

Most helpful comment

@Mangoman3 I don't have time to implement this right now, but I would love to have this in the next release of Moya 15. If anyone would like to take a stab at it, I would be more than happy to help in a PR.

All 2 comments

any luck with this ?

@Mangoman3 I don't have time to implement this right now, but I would love to have this in the next release of Moya 15. If anyone would like to take a stab at it, I would be more than happy to help in a PR.

Was this page helpful?
0 / 5 - 0 ratings