Skip to content

Commit

Permalink
Make sure to upload files to Misskey server
Browse files Browse the repository at this point in the history
  • Loading branch information
YuigaWada committed Nov 28, 2019
1 parent cbd590c commit 813e777
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 70 deletions.
4 changes: 2 additions & 2 deletions MisskeyKit/APIs/Wrapper/HTTP/Drive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,15 @@ extension MisskeyKit {


//MARK: Control Files
public func createFile(name: String, isSensitive: Bool = false, force: Bool = false, folderId: String = "", result callback: @escaping (DriveFileModel?, MisskeyKitError?)->()) {
public func createFile(fileData: Data, fileType: String, name: String, isSensitive: Bool = false, force: Bool = false, folderId: String = "", result callback: @escaping (DriveFileModel?, MisskeyKitError?)->()) {

var params = ["name":name,
"isSensitive":isSensitive,
"folderId":folderId,
"force":force] as [String : Any]

params = params.removeRedundant()
MisskeyKit.handleAPI(needApiKey: true, api: "drive/files/create", params: params, type: DriveFileModel.self) { info, error in
MisskeyKit.handleAPI(needApiKey: true, api: "drive/files/create", params: params, data: fileData, fileType: fileType, type: DriveFileModel.self) { info, error in

if let error = error { callback(nil, error); return }
guard let info = info else { callback(nil, error); return }
Expand Down
159 changes: 91 additions & 68 deletions MisskeyKit/APIs/Wrapper/HTTP/MisskeyKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,77 +28,47 @@ open class MisskeyKit {
Api.instance = instance
}

//MARK:- Internal Methods
internal static func handleAPI<T>(needApiKey: Bool = false, api: String, params: [String: Any], type: T.Type, missingCount: Int? = nil, callback: @escaping (T?, MisskeyKitError?)->Void) where T : Decodable {
if let missingCount = missingCount
{
guard missingCount < 4 else {
return callback(nil, .FailedToCommunicateWithServer)
}
}

var params = params
if needApiKey {
params["i"] = auth.getAPIKey()
}

guard let rawJson = params.toRawJson() else {
callback(nil, .FailedToDecodeJson)
return
}


Requestor.post(url: Api.fullUrl(api), rawJson: rawJson) { (response: HTTPURLResponse?, resultRawJson: String?, error: MisskeyKitError?) in
guard let resultRawJson = resultRawJson else {
if let missingCount = missingCount {
self.handleAPI(needApiKey: needApiKey,
api: api,
params: params,
type: type,
missingCount: missingCount + 1,
callback: callback)
}

// If being initial error...
self.handleAPI(needApiKey: needApiKey,
api: api,
params: params,
type: type,
missingCount: 1,
callback: callback)

return
}

let resultJson = arrayReactions(rawJson: resultRawJson) // Changes a form of reactions to array.

if let response = response, response.statusCode == 200, resultJson.count == 0 {
callback(nil, nil)
return
}

guard let json = resultJson.decodeJSON(type) else {
if resultJson.count == 0 {
guard String(reflecting: T.self) == "Swift.Bool" else {
callback(nil, .ResponseIsNull)
return
}

callback(nil, nil)
return
}

//guard上のifでnilチェックできているので強制アンラップでOK
let error = ApiError.checkNative(rawJson: resultJson, response!.statusCode)
callback(nil, error)
return
}

callback(json, nil)
//MARK:- Internal Methods
internal static func handleAPI<T>(needApiKey: Bool = false, api: String, params: [String: Any], data: Data? = nil, fileType: String? = nil, type: T.Type, missingCount: Int? = nil, callback: @escaping (T?, MisskeyKitError?)->Void) where T : Decodable {
let hasAttachment = data != nil

if hasAttachment && fileType == nil { return } // If fileType being nil ...

if let missingCount = missingCount, missingCount >= 4 { return callback(nil, .FailedToCommunicateWithServer) }

var params = params
if needApiKey {
params["i"] = auth.getAPIKey()
}

let completion = { (response: HTTPURLResponse?, resultRawJson: String?, error: MisskeyKitError?) in
self.handleResponse(response: response,
resultRawJson: resultRawJson,
error: error,
needApiKey: needApiKey,
api: api,
params: params,
data: data,
fileType: fileType,
type: T.self,
missingCount: missingCount,
callback: callback)
}




if !hasAttachment {
guard let rawJson = params.toRawJson() else { callback(nil, .FailedToDecodeJson); return }
Requestor.post(url: Api.fullUrl(api), rawJson: rawJson, completion: completion)
}
else {
Requestor.post(url: Api.fullUrl(api), paramsDic: params, data: data, fileType: fileType, completion: completion)
}
}



// ** 参考 **
//reactionsのkeyは無数に存在するため、codableでのパースは難しい。
//そこで、生のjsonを直接弄り、reactionsを配列型に変更する。
Expand All @@ -119,7 +89,7 @@ open class MisskeyKit {

replaceList.append(shapedReactions)
}

var replacedRawJson = rawJson
for i in 0...reactionsList.count-1 {
replacedRawJson = replacedRawJson.replacingOccurrences(of: reactionsList[i][0], with: replaceList[i])
Expand All @@ -128,5 +98,58 @@ open class MisskeyKit {
return replacedRawJson
}




//MARK: Private Methods
private static func handleResponse<T>(response: HTTPURLResponse?, resultRawJson: String?, error: MisskeyKitError?, needApiKey: Bool = false, api: String, params: [String: Any], data: Data? = nil, fileType: String? = nil, type: T.Type, missingCount: Int? = nil, callback: @escaping (T?, MisskeyKitError?)->Void) where T : Decodable {
guard let resultRawJson = resultRawJson else {
if let missingCount = missingCount {
self.handleAPI(needApiKey: needApiKey,
api: api,
params: params,
type: type,
missingCount: missingCount + 1,
callback: callback)
}

// If being initial error...
self.handleAPI(needApiKey: needApiKey,
api: api,
params: params,
type: type,
missingCount: 1,
callback: callback)

return
}

let resultJson = arrayReactions(rawJson: resultRawJson) // Changes a form of reactions to array.

if let response = response, response.statusCode == 200, resultJson.count == 0 {
callback(nil, nil)
return
}

guard let json = resultJson.decodeJSON(type) else {
if resultJson.count == 0 {
guard String(reflecting: T.self) == "Swift.Bool" else {
callback(nil, .ResponseIsNull)
return
}

callback(nil, nil)
return
}

//guard上のifでnilチェックできているので強制アンラップでOK
let error = ApiError.checkNative(rawJson: resultJson, response!.statusCode)
callback(nil, error)
return
}

callback(json, nil)
}

}

65 changes: 65 additions & 0 deletions MisskeyKit/Utilities/Requestor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import Foundation
internal typealias ResponseCallBack = (HTTPURLResponse?, String?, MisskeyKitError?) -> Void
internal class Requestor {

private static let boundary = UUID().uuidString

//MARK: GET
static func get(url: String, completion: @escaping ResponseCallBack) {
self.get(url: URL(string: url)!, completion: completion)
}
Expand All @@ -36,9 +39,71 @@ internal class Requestor {
}


//MARK: POST

//With Attachment
static func post(url: String, paramsDic: [String: Any], data: Data?, fileType: String?, completion: @escaping ResponseCallBack) {
self.post(url: URL(string: url)!,
paramsDic: paramsDic,
data: data,
fileType: fileType,
completion: completion)
}


// Generate a "multipart/form-data" request
static func post(url: URL, paramsDic: [String: Any], data: Data?, fileType: String?, completion: @escaping ResponseCallBack) {
guard let data = data, let fileType = fileType else { completion(nil,nil,.FailedToCommunicateWithServer); return }

var request = URLRequest(url: url)
request.httpMethod = "POST"

let session = URLSession(configuration: URLSessionConfiguration.default)
let uuid = UUID().uuidString
var bodyData: Data = Data()


request.setValue("multipart/form-data; boundary=\(self.boundary)", forHTTPHeaderField: "Content-Type")


paramsDic.forEach{ params in
let value = String(describing: params.value)

bodyData.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
bodyData.append("Content-Disposition: form-data; name=\"\(params.key)\"\r\n\r\n".data(using: .utf8)!)
bodyData.append(value.data(using: .utf8)!)
}

bodyData.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
bodyData.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(uuid)\"\r\n".data(using: .utf8)!)
bodyData.append("Content-Type: \(fileType)\r\n\r\n".data(using: .utf8)!)
bodyData.append(data)

bodyData.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

request.httpBody = bodyData

let task = session.dataTask(with: request) { (data, response, error) in
if let _ = error {
completion(nil, nil, .FailedToCommunicateWithServer)
return
}
guard let response = response as? HTTPURLResponse else {
completion(nil, nil, nil)
return
}


if let data = data, let dataString = String(data: data, encoding: .utf8) {
completion(response, dataString, nil)
}

}
task.resume()
}


//Without attachment
static func post(url: String, rawJson: String, completion: @escaping ResponseCallBack) {
self.post(url: URL(string: url)!,
rawJson: rawJson,
Expand Down

1 comment on commit 813e777

@YuigaWada
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive�のApiを叩いてデータをアップロードするためには、application/jsonではなくmultipart/form-dataなPOSTを生成しなきゃいけないので、比較的大きめの変更を加えた。

Please sign in to comment.