備忘録です。
ライブラリを使えば大体対応されているものですが、 自前で書いたことがなかったのでやってみました。
力技な感じも否めませんが、問題なくできたのでとりあえず大丈夫かと。
来月当たりに自分がみたときにもっと改良できることを祈っています。
ソースコード
とりあえずソースコード載せます。
NSURLRequest
を作成する部分だけに絞りますので、リクエストの送り方などは他でお願いします。
RequestData.swift
/*
送りたいバイナリデータを持つクラス
MIME-TYPEとfile nameとNSDataを保持します
*/
class RequestData {
enum MimeType: String {
case JPEG = "image/jpeg"
case MP4 = "video/mp4"
}
var data: NSData
var mimeType: MimeType
var filename: String
init(data: NSData, mimeType: MimeType, filename: String) {
self.data = data
self.mimeType = mimeType
self.filename = filename
}
}
API.swift
/*
NSURLRequestを作成するクラス
ここにAPIにRequestを送る処理も書いていました
*/
class API {
enum Method: String {
case GET = "GET"
case POST = "POST"
}
class func baseURL() -> NSURL {
return NSURL(string: "リクエストを送るURL")
}
class func URLRequest(#method: Method, path: String, parameters: [String:AnyObject]) -> NSURLRequest? {
if let components = NSURLComponents(URL: baseURL(), resolvingAgainstBaseURL: true) {
let request = NSMutableURLRequest()
request.HTTPMethod = method.rawValue
if method == .GET {
components.query = API.Helper.stringFromObject(parameters, encoding: NSUTF8StringEncoding)
} else if method == .POST {
var contentType: String?
var paramsData: NSData?
if API.Helper.isMultiParams(parameters) {
let boundary = "POST-boundary-\(arc4random())-\(arc4random())"
paramsData = API.Helper.multiDataFromObject(parameters, boundary: boundary)
contentType = "multipart/form-data; boundary=\(boundary)"
} else {
paramsData = API.Helper.dataFromObject(parameters, encoding: NSUTF8StringEncoding)
contentType = "application/x-www-form-urlencoded"
}
if let params = paramsData {
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.setValue("\(params.length)", forHTTPHeaderField: "Content-Length")
request.HTTPBody = params
}
}
components.path = (components.path ?? "").stringByAppendingPathComponent(path).stringByAppendingString("/")
request.URL = components.URL
// responseがjsonの場合
request.setValue("application/json", forHTTPHeaderField: "Accept")
return request
}
return nil
}
class Helper {
class func isMultiParams(params: [String:AnyObject]) -> Bool {
var isMultiParams = false
for (_, value) in params {
if value is RequestData {
isMultiParams = true
break
}
}
return isMultiParams
}
class func dataFromObject(object: AnyObject, encoding: NSStringEncoding) -> NSData? {
let string = stringFromObject(object, encoding: encoding)
return string.dataUsingEncoding(encoding, allowLossyConversion: false)
}
class func stringFromObject(object: AnyObject, encoding: NSStringEncoding) -> String {
var pairs = [String]()
if let dictionary = object as? [String: AnyObject] {
for (key, value) in dictionary {
let string = (value as? String) ?? "\(value)"
let pair = "\(key)=\(string.escape())"
pairs.append(pair)
}
}
return join("&", pairs)
}
class func multiDataFromObject(object: [String:AnyObject], boundary: String) -> NSData? {
var data = NSMutableData()
let prefixString = "--\(boundary)\r\n"
let prefixData = prefixString.dataUsingEncoding(NSUTF8StringEncoding)!
let seperatorString = "\r\n"
let seperatorData = seperatorString.dataUsingEncoding(NSUTF8StringEncoding)!
for (key, value) in object {
var valueData: NSData?
var valueType: String?
var filenameClause = ""
if value is RequestData {
let requestData = value as! RequestData
if let data = requestData.getData() {
valueData = data
valueType = requestData.mimeType.rawValue
filenameClause = " filename=\"\(requestData.filename)\""
}
} else {
let stringValue = "\(value)"
valueData = stringValue.dataUsingEncoding(NSUTF8StringEncoding)!
}
if valueData == nil {
continue
}
data.appendData(prefixData)
let contentDispositionString = "Content-Disposition: form-data; name=\"\(key)\";\(filenameClause)\r\n"
let contentDispositionData = contentDispositionString.dataUsingEncoding(NSUTF8StringEncoding)
data.appendData(contentDispositionData!)
if let type = valueType {
let contentTypeString = "Content-Type: \(type)\r\n"
let contentTypeData = contentTypeString.dataUsingEncoding(NSUTF8StringEncoding)
data.appendData(contentTypeData!)
}
data.appendData(seperatorData)
data.appendData(valueData!)
data.appendData(seperatorData)
}
let endingString = "--\(boundary)--\r\n"
let endingData = endingString.dataUsingEncoding(NSUTF8StringEncoding)!
data.appendData(endingData)
return data
}
}
}
とまあこんな感じでできました。
使い方
Playgoround.swift
// UIImageをjpegにしてリクエストする
let image = UIImage(named: "hoge")
let jpegData = UIImageJPEGRepresentation(image, 1)
let requestData = RequestData(data: jpegData, mimeType: JPEG, "hoge.jpeg")
let parameters = ["title":"hogehoge", "text": "hoge", "post_image": requestData]
if let request = API.URLRequest(method: .POST, path: "/hogehoge", parameters: parameters) {
// requestを送る処理
}
みたいになります。
やっていること
至極簡単です。
multipart/form-dataのことを調べてもらえればわかるかと思います。
for-in
でkey
とvalue
に分けて、クラス判別をして作業を分けています。
いろいろなライブラリのソースコードを参考にしましたが大体同じことをしていました。
最後に
クラス分け、enum
の使い所など、参考になる点が多々あるのでライブラリを読むのって大事ですね。
以上です。