OSSから学ぶ
Swift実践テクニック
Takahashi Yosuke
@yousan
自己紹介
• Takahashi Yosuke
• 株式会社ネクスト

HOME Sアプリ
• モバイルアプリエンジニア
• 趣味 テニス

錦織に似ていると言われる
OSSを使う前に
コードリーディングしてみると
いろいろ勉強になる
Alamofire
https://github.com/Alamofire/Alamofire
ProtocolとExtensionで
組み込み型を抽象化
request
Alamofire.request(.GET, "https://httpbin.org/get")
README.md
requestの定義
public func request(
method: Method,
_ URLString: URLStringConvertible,
parameters: [String: AnyObject]? = nil,
encoding: ParameterEncoding = .URL,
headers: [String: String]? = nil)
-> Request
{
return Manager.sharedInstance.request(
method,
URLString,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
Stringじゃない!
Alamofire.swift
URLStringConvertible
public protocol URLStringConvertible {
∼中略∼
var URLString: String { get }
}
extension String: URLStringConvertible {
public var URLString: String {
return self
}
}
extension NSURL: URLStringConvertible {
public var URLString: String {
return absoluteString
}
}
extension NSURLComponents: URLStringConvertible {
public var URLString: String {
return URL!.URLString
}
}
extension NSURLRequest: URLStringConvertible {
public var URLString: String {
return URL!.URLString
}
}
String
NSURL
NSURLComponents
NSURLRequest
全てをrequestの引数に使える
Alamofire.swift
Protocolごとに
Extensionを分ける
public struct Response<Value, Error: ErrorType> {
public let request: NSURLRequest?
public let response: NSHTTPURLResponse?
public let data: NSData?
public let result: Result<Value, Error>
public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result<Value, Erro
self.request = request
self.response = response
self.data = data
self.result = result
}
}
extension Response: CustomStringConvertible {
public var description: String {
return result.debugDescription
}
}
extension Response: CustomDebugStringConvertible {
public var debugDescription: String {
var output: [String] = []
output.append(request != nil ? "[Request]: (request!)" : "[Request]: nil")
output.append(response != nil ? "[Response]: (response!)" : "[Response]: nil")
output.append("[Data]: (data?.length ?? 0) bytes")
output.append("[Result]: (result.debugDescription)")
return output.joinWithSeparator("n")
}
}
Response.swift(コメント省略)
普通に書くとこんな感じ
public struct Response<Value, Error: ErrorType>: CustomStringConvertible, CustomDebugStringConvertible {
public let request: NSURLRequest?
public let response: NSHTTPURLResponse?
public let data: NSData?
public let result: Result<Value, Error>
public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result<Value, Erro
self.request = request
self.response = response
self.data = data
self.result = result
}
// MARK: - CustomStringConvertible
public var description: String {
return result.debugDescription
}
// MARK: - CustomDebugStringConvertible
public var debugDescription: String {
var output: [String] = []
output.append(request != nil ? "[Request]: (request!)" : "[Request]: nil")
output.append(response != nil ? "[Response]: (response!)" : "[Response]: nil")
output.append("[Data]: (data?.length ?? 0) bytes")
output.append("[Result]: (result.debugDescription)")
return output.joinWithSeparator("n")
}
}
extension使うと見やすい
各Protocolとメソッドの対応が、MARKで区切るより
見やすい。
Protocolとメソッドが近い、括弧によるグルーピング
各Protocolごとに関連するメソッドやサブタイプなどを
まとめておけば管理も簡単。
Protocolが不要になったらextensionの範囲丸ごと消せばいい
※ただし、例えばCustomStringConvertibleのextension内に
descriptionがある保証はないし、関係ないメソッドが書かれる可能
性もあるので注意。
メソッドチェーンで
requestを使いやすく
public func authenticate(usingCredential credential: NSURLCredential) -> Self {
delegate.credential = credential
return self
}
public func progress(closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self {
if let uploadDelegate = delegate as? UploadTaskDelegate {
uploadDelegate.uploadProgress = closure
} else if let dataDelegate = delegate as? DataTaskDelegate {
dataDelegate.dataProgress = closure
} else if let downloadDelegate = delegate as? DownloadTaskDelegate {
downloadDelegate.downloadProgress = closure
}
return self
}
public func stream(closure: (NSData -> Void)? = nil) -> Self {
if let dataDelegate = delegate as? DataTaskDelegate {
dataDelegate.dataStream = closure
}
return self
}
Request.swift
オプションを追加したついでに
最後にselfを返す
大量オプションでも見やすい
Alamofire.request(.GET, “https://httpbin.org/")
.authenticate(usingCredential: credential)
.progress(closure)
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.response { response in
print(response)
}
・余分な変数が不要
・書きやすいし読みやすい
まとめ
コードを書くのも大切だけど
他人のコードを読むのも重要

OSSから学ぶSwift実践テクニック