ãæµ·å¤äºæ¥åãã®iOSã¢ããªã±ã¼ã·ã§ã³éçºãæ å½ãã¦ãã西山(@yuseinishiyama)ã§ããã¯ãã¯ãããã¯ç¾å¨ãæµ·å¤è¤æ°ã«å½ã«åãã¦ãµã¼ãã¹ãå±éãã¦ãã¾ãã
ã主ã«Objective-Cã§è¨è¿°ãããã¢ããªã±ã¼ã·ã§ã³ãå ¨é¢çã«Swiftã«æ¸ãæããæ©ä¼ããã£ãã®ã§ããã®éã«å¾ãç¥è¦ãæ¸ãæããã«è³ã£ãåæ©ãå ±æãã¾ãã
æ¸ãæãã«è³ãã¾ã§ã®çµç·¯
ããã®é ã§ã¯ãæ¸ãæãã«è³ãã¾ã§ã®çµç·¯ã«ã¤ãã¦èª¬æãã¾ãã
Objective-Cæ
ãã¢ããªã±ã¼ã·ã§ã³ã®éçºã¯2014å¹´7æé ã«ã¹ã¿ã¼ããã¾ãããSwiftã®çºè¡¨ç´å¾ã§ããããææå°æ©ã¨ãããã¨ããããObjective-Cã§å®è£ ãããã¨ã«ãªãã¾ããã
Objective-CãSwiftæ··å¨æ
ã2014å¹´10æé ãããSwiftã¸ã®æ®µéçãªç§»è¡ã®ããã«ãæ°è¦ã®ã³ã¼ããSwiftã§æ¸ãããã«ãªãã¾ãããSwiftã®è¨è¿°åãããããã¨å®è£ ãè¡ãæ¥ããªãã¦è¯ããã¨ãªã©ã«ã¡ãªãããæããããã§ãããããããObjective-Cã¨Swiftãæ··å¨ããããã«ãªãã¾ãã
Swiftæ
ãObjective-Cã¨Swiftãæ··å¨ãã¦ããç¶æ ã§ã¯ãå¾è¿°ããSwiftã«ããã¡ãªãããå®å ¨ã«ã¯åãããã¨ãã§ããªãã¨èããä¸é¨ãé¤ãã¦ã»ã¼å ¨ã¦ã®ã³ã¼ããSwiftã«æ¸ãæãã¾ããã
ãªãæ¸ãæããã
ãæ¸ãæãã«ã¯ãã¨ã³ãã°ãã¹ã±ã¸ã¥ã¼ã«ã®é ããªã©ã®ãªã¹ã¯ãä¼´ãªãã¾ããã¾ããæ°è¦ã®è¨èªã«å¯¾ããç´ç²ãªèå³ãããæ¥åã§ä½¿ç¨ããè¨èªãé¸æãããã¨ãå½ç¶ãã¹ãã§ã¯ããã¾ãããããããããã§ãå°ãæ¸ãæããã»ããè¯ãã¨æãããååãªçç±ãããã¾ããããã®é ã§ã¯ããã®çç±ã«ã¤ãã¦èª¬æãã¾ãã
Objective-Cã¨Swiftã®æ··å¨ã«ãã£ã¦çããå¶ç´
ãAppleã¯Swiftã¨Objective-Cãç¸äºã«éç¨ããæ¹æ³ã«ã¤ãã¦ã詳細ãªããã¥ã¡ã³ããæä¾ãã¦ãã¾ããããããåã«ç¸äºå©ç¨ã§ããã¨ããã ãã§ãå®éã«ã¯æ§ã ãªå¶ç´ãããã¾ãã
ãObjective-CããSwiftãåç
§ããå ´åãSwiftå´ã®ä¸é¨ã®ã³ã¼ãã¯åç
§ãããã¨ãã§ãã¾ãããSwiftã§ã®enum
ãstruct
ã®ãããªæ¦å¿µã¯Objective-Cã«ã¯ç¡ããããããããObjective-Cããå©ç¨ãããã¨ã¯ã§ãã¾ãããã¾ããOptionalåã®å©ç¨ã«ãå¶éãããã¾ããä¾ãã°ãä¸è¨ã®ãããªã¯ã©ã¹ã¨ãã®ããããã£ç¾¤ããã£ãå ´åãããããã£a
ã¯Objective-Cããåç
§ã§ãã¾ãããããããã£b
ã¯ã§ãã¾ããã
class SampleClass : NSObject { var a = 0 var b: Int?
ãSwiftã®Int
åã¯Objective-Cã§ã¯NSInteger
ã¨ã¿ãªããã¾ããSwiftã®Int
ã¯struct
ã¨ãã¦å®£è¨ããã¦ããä¸æ¹ãNSInteger
ã®å®æ
ã¯ããªããã£ããªåãªã®ã§nil
å¤ãã¨ããã¨ãã§ãã¾ããããã®ãããOptionalãªInt
åãObjective-Cã§æ±ããã¨ãã§ããªãã®ã§ãããã®ãããªã±ã¼ã¹ã§nil
ã許容ããæ°å¤ã使ç¨ãããå ´åã¯ãNSNumber
ã¨ãã¦å®£è¨ããããå¾ã¾ããã
ã以ä¸ã®ãããªäºæãå«ããObjective-Cããå©ç¨ãããéããSwiftã®ä»æ§ããã«ã«çãããã¨ã¯ã§ãããã¾ããObjective-Cããã®å©ç¨ãæèããã³ã¼ããæ¸ãç¶ããå¿ è¦ãããã¨ãããã¨ãåããã¾ãã
Objective-CããSwiftãåç §ããªãããã«ããï¼
ãåé ã®åé¡ã¯Objective-CããSwiftãåç §ãããæ ã«ãèµ·ããããåé¡ã§ãã極åãObjective-CããSwiftãå¼ã³åºããªããããªããªã·ã¼ã§æ··å¨ãããã®ã¯ã©ãã§ããããï¼
ã確ãã«ãSwiftããObjective-Cã©ã¤ãã©ãªãåç §ãããSwiftããObjective-Cã§è¨è¿°ãããã¢ãã«ãåç §ããããªã©ã®SwiftããObjective-Cãåç §ããã±ã¼ã¹ã§ã¯ããããã®åé¡ãæ°ã«ããå¿ è¦ã¯ããã¾ãããå®éãå½è©²ããã¸ã§ã¯ãã§ããå½åã¯ãã¥ã¼é¢é£ã®ã³ã¼ãã ãSwiftã§è¨è¿°ãã¦ããã®ã§ãObjective-CããSwiftãåç §ããã±ã¼ã¹ã¯æ®ã©ããã¾ããã§ããã
Swiftã®ã¡ãªããã享åããã«ã¯
ãã¨ããã§ãSwiftåã«ããã¡ãªãããæ大éã«åãããã¨ãã§ããã®ã¯ãã¢ãã«ãAPIã¯ã©ã¤ã¢ã³ãã§ãããããã®ã¬ã¤ã¤ã¼ã¯ãObjective-Cã«ããã¦ã¯ããã®åçç¹æ§ã®ããã«ããã°ã®æ¸©åºã¨ãªã£ã¦ãã¾ãããJSONããªãã¸ã§ã¯ãã«ãããã³ã°ããéã«ãäºæãã¦ããåã¨éãåãå ¥ã£ã¦ããã¨ãããããªãã¨ã¯ãçããã度ã çµé¨ããã¦ããã®ã§ã¯ãªãã§ããããã
ãä¸æ¹ãSwiftã§ã¯éçåä»ããnullabilityã®ã³ã³ããã¼ã«ã«ãã£ã¦ããããã®ãã°ã解æ¶ãããã¨ãã§ãã¾ããé·æçã«éç¨ãããã§ãããã¢ããªã±ã¼ã·ã§ã³ã«ããã¦ãããããæ©è½ãæ´»ç¨ãã¦ãã®å®å®æ§ãé«ãããã¨ã«ã¯å¤å¤§ãªã¡ãªãããããã¾ãã
ãããããã¢ãã«ãAPIã¯ã©ã¤ã¢ã³ããSwiftã«ç½®ãæããã¨ãããããå¼ã³åºãã¦ããæ¢åã®Objective-Cã³ã¼ã(主ã«ViewControllerãªã©)ãSwiftãåç §ãããã¨ã«ãªãã¾ããã¢ãã«ãAPIã¯ã©ã¤ã¢ã³ããSwiftã®æ©è½ããã«ã«å©ç¨ãã¦å®è£ ããã¨ãçµå±ãã¢ããªã±ã¼ã·ã§ã³å ¨ä½ãSwiftåããå¿ è¦ãã§ã¦ããã®ã§ãã
Swiftåã®ã¡ãªãã
ãåé ã§ãéçåä»ããnullabilityã®ã³ã³ããã¼ã«ã¨ãã£ããSwiftã®ã¡ãªããã«ã¤ãã¦ç°¡åã«è§¦ãã¾ãããããã§ã¯ãããããã¡ãªããã«ã¤ãã¦ãå®éã®ã³ã¼ããåç §ããªãããå ·ä½çã«è¿°ã¹ã¾ãã
ã¸ã§ããªã¯ã¹ã®æ´»ç¨
ãã¸ã§ããªã¯ã¹ãæ´»ç¨ãããã¨ã§ãããå®å ¨ã§è¡¨ç¾åã®é«ãã³ã¼ããè¨è¿°ãããã¨ãã§ãã¾ããã¸ã§ããªã¯ã¹ãå©ç¨ãããã¨ã好ã¾ããå ¸åçãªã±ã¼ã¹ã«ã¤ãã¦èª¬æãã¾ãã
ã¢ãã«ã¨APIã¯ã©ã¤ã¢ã³ãã®è¡¨ç¾
ãã¸ã§ããªã¯ã¹ãç¨ãã¦ãã¬ã¹ãã³ã¹ã®åãéçã«æ±ºãããã¨ãã§ãã¾ããããã«ãã£ã¦ãåè¿°ãããäºæããªãåãä»£å ¥ããããã¨ã«ãã£ã¦çãããã°ãé²ããã¨ãã§ãã¾ãã
ãã¾ããåä¸ã®APIã表ãããã®ãããã³ã«ã以ä¸ã®ããã«å®£è¨ãã¾ãããã¡ãªã¿ã«ãç¾å¨ã®ãããã³ã«ã®ä»æ§ã§ã¯ããã©ã«ãã®å®è£ ãå®ç¾©ãããã¨ãã§ããªãã®ã§ããããã³ã«ã§ã¯ãªãã¯ã©ã¹ãæ¡ç¨ããã¨ããèããååã«æãå¾ã¾ãã
protocol API { typealias ResponseType typealias ResultType = Result<Response<ResponseType>, Response<CommonError>> typealias ResponseParserType: ResponseParser var method: Method { get } var pathString: String { get } var parameters: [String : AnyObject]? { get } var parameterEncoding: ParameterEncoding { get } var responseParser: ResponseParserType { get } }
ãããã¦ãæºæ ãã¦ããAPIã®å®æ ã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
extension APIs { class Recipes { class Get: API { typealias ResponseType = Recipe let id: Int init(id: Int) { self.id = id } var method: Method = .GET var pathString: String { return "/recipes/\(id)" } var parameters: [String : AnyObject]? = nil var parameterEncoding: ParameterEncoding = .Default var responseParser = DefaultResponseParser<ResponseType>() } class Post : API { typealias ResponseType = Recipe let recipe: Recipe init(recipe: Recipe) { self.recipe = recipe } (以ä¸çç¥)
ããã®ããã«è¨è¿°ãããã¨ã§ãããããã®APIã®ä»æ§ãä¸ç¼ã§åããã¾ããã¾ããã¯ã©ã¹ã®ãã¹ããå©ç¨ãã¦ãAPIã®é層æ§é ã表ç¾ãããã¨ãã§ãã¾ãã
ã次ã«ãAPIã¯ã©ã¤ã¢ã³ãã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ã§ãã
protocol APIClient { (çç¥) func sendRequest<T: API where T.ResultType == T.ResponseParserType.ResponseType>(API: T, handler: T.ResultType -> ()) }
ãã¸ã§ããªã¯ã¹ãæ´»ç¨ãããã¨ã§ãT.ResultType
ã¨ãã¦ã¬ã¹ãã³ã¹ã®åãAPIããä¸æã«æ±ºã¾ãã¾ããå®éã«ãããã使ç¨ããã³ã¼ãã¯ä¸è¨ã®ããã«ãªãã¾ãã
let api = APIs.Recipes.Get(id: 42) SharedAPIClient.sendRequest(api) { println("the title is \($0.value?.bodyObject.title)") }
ãGET /recipes/:id
ã®ã¬ã¹ãã³ã¹ã®åãRecipe
ã§ããã¨ãããã¨ãéçã«æ±ºã¾ãã¾ããããã¦ããã®ããããã£ã«title
ããããã¨ãã³ã³ãã¤ã©ã¯ç¥ããã¨ãã§ããã®ã§ãã
ãã¡ãªã¿ã«ããããã®å®è£ ã«å½ãã£ã¦ã¯ãMoyaãAPIKitã大å¤ã«åèã«ãªãã¾ããã
Eitherã«ããã¨ã©ã¼ãã³ããªã³ã°
ãObjective-Cã«ããã¦ãAPIã³ã¼ã«æã®ã³ã¼ã«ããã¯é¢æ°ã®åã¯ä¸è¨ã®ãããªãã®ãä¸è¬çã§ããã
typedef void (^CompletionBlock)(id result, NSError *error);
ãerror
ãnil
ãã©ããããã§ãã¯ããnil
ã§ãªããã°result
ãåç
§ãã¦çµæãåãåãã¨ãããã¿ã¼ã³ã§ããããããresult
ãerror
ãnil
ã«ãªãå¾ã訳ã§ãå³å¯ã«ã¯ä¸è¨4ãã¿ã¼ã³ãåå¨ãã¾ãã
result == nil | result != nil | |
---|---|---|
error == nil | ? | æå |
error != nil | 失æ | ? |
ããã®å ´åãã?ãã«ãããç®æã§ã¯ãæããã¦ãã®ãªã¯ã¨ã¹ããæåããã®ãã失æããã®ãåããã¾ããããã¡ããããããªããªãããã«å®è£ ããããã§ãããå³å¯ã«èµ·ããå¾ãªããã¨ãå¼·å¶ããè¡ã¯ããã¾ããã
ãä¸æ¹ã§ã¸ã§ããªã¯ã¹ã使ç¨ãã¦ãEitherã¨å¼ã°ãã2ã¤ã®å¯è½æ§ã表ç¾ããåãå®è£ ããã°ãããæ確ã«æåã¨å¤±æã®ã³ã³ããã¹ãã表ç¾ãããã¨ãã§ãã¾ããEitherã®å®è£ ã«é¢ãã¦ã¯ããã¡ãã®å®è£ ãåèã«ãªãã¾ãã
public enum Result<T,E> { case Success(Box<T>) case Failure(Box<E>)
(Boxã¯Swiftã®å¤åã®å¶éãåé¿ããããã«åå¨ãã¦ãã¾ãã詳細ã«ã¤ãã¦ã¯ãBoxãåç §ãã¦ãã ããã)
ããããå©ç¨ããã¨ãã¨ã©ã¼ãã³ããªã³ã°ã¯ä¸è¨ã®ããã«è¨è¿°ãããã¨ãã§ãã¾ãã
let api = APIs.Recipes.Get(id: 42) SharedAPIClient.sendRequest(api) { switch $0 { case .Success(let box): // box.unbox <- æåæã®åTãå ¥ã£ã¦ãã case .Failure(let box): // box.unbox <- 失ææã®åEãå ¥ã£ã¦ãã // ããã§ã¨ã©ã¼ããã³ããªã³ã°ãã } }
ããã®ããã«ã¸ã§ããªã¯ã¹ãæ´»ç¨ãããã¨ã§ãããå³å¯ãªã¨ã©ã¼ãã³ããªã³ã°ãè¡ããã¨ãã§ãã¾ãã
Enumã®æ´»ç¨
ãSwiftã®Enumã«ã¯è¨ç®åããããã£ãé¢æ°ãå®ç¾©ãããã¨ãã§ãã¾ãããã®ãããEnumã®å¤ã«ãã£ã¦æ¯ãèããå¤ããå ´åããã®æ¯ãèãèªä½ãEnumå´ã«å®è£ ãããã¨ãã§ãã¾ãã
enum MyTableViewSection: Int { case A = 0, B, C, D var heightForCell: CGFloat { switch self { case A: return 30 case B: return 44 case C: return 80 case D: return 44 } } } func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) ->ãCGFloat { return MyTableViewSection(rawValue: indexPath.section)!.heightForCell }
ãObjective-Cã§ã¯switchæã®ä¸ãç ©éã«ãªããã¡ã§ããããSwiftã®Enumãæ´»ç¨ãããã¨ã§ãããå¯èªæ§ã®é«ãã³ã¼ããæ¸ããã¨ãã§ãã¾ãã
Swiftã¯å®ç¨æ®µéã
ããã®é ã§ã¯ãæ°è¦ã®ããã¸ã§ã¯ããSwiftã§å§ããã¹ãããã¾ããæ¢åã®ããã¸ã§ã¯ããSwiftåããã¹ããªã®ããã¨ãããã¨ã«ã¤ãã¦ã®ç§è¦ãè¿°ã¹ã¾ãã
Swift1.2ã®ç»å ´
ãå æ¥ãXcode6.3ãæ£å¼ã«ãªãªã¼ã¹ããããã®ã¢ãããã¼ãã«ã¯Swift1.2ãå«ã¾ãã¦ãã¾ããæ£ç´ãªã¨ãããSwift1.2ãã§ãã¾ã§ã¯ãIDEã®å®å®æ§ãã³ã³ãã¤ã«æéãªã©ã«å¤§å¤ãªä¸æºããããSwiftã¸ã¨ç§»è¡ãããã¨ãå¾æãããã¨ãããã¾ãããã³ã¼ããè¨è¿°ããã°ããã»ã©ãã¸ã§ããªã¯ã¹ãå¤ç¨ããã°ããã»ã©ããããã®åé¡ãé¡èã«ãªã£ã¦ãã¾ããããããSwift1.2ããããããã®åé¡ã¯å¤§ãã解æ¶ããã¦ãã¾ããããããè¸ã¾ããã¨ãSwift1.2ããã¯ã¾ã¨ãã«ä½¿ããããã«ãªã£ããã¨è¨ã£ã¦ãããã§ãããã
Swiftã®ã¨ã³ã·ã¹ãã
ãSwiftã®ã¨ã³ã·ã¹ãã ã«ã¯ãæ¢ã«ç´ æ´ãããã©ã¤ãã©ãªãåå¨ãã¾ããä¾ãã°ãawesome-swiftã®ãªã¹ããåèã«ãªãã¾ãã
ãå½è©²ããã¸ã§ã¯ãã§ãããã§ã«ä¸è¨ã®Swift製ã©ã¤ãã©ãªã使ç¨ãã¦ãã¾ãã
- HTTPã¯ã©ã¤ã¢ã³ã...SwiftHTTP
- JSONãªãã¸ã§ã¯ããããã³ã°...ObjectMapper
- Either... Result
ããã®ããã«Swiftã«ã¯æ¢ã«å®ç¨çãªã©ã¤ãã©ãªãå¤ãåå¨ããå¹çããéçºãé²ãããã¨ãã§ãã¾ãã
ãã¡ãªã¿ã«ãSwiftã§ã¯Static Libaryãçæãããã¨ãã§ãããã¾ããDynamic Frameworkã¯iOS8以éã§ã®ã¿ã®ãµãã¼ãã¨ãªã£ã¦ãã¾ããå½è©²ããã¸ã§ã¯ãã§ã¯iOS7ããµãã¼ãããå¿ è¦ããã£ãã®ã§ãSwift製ã®ã©ã¤ãã©ãªã¯Gitã®ãµãã¢ã¸ã¥ã¼ã«ã¨ãã¦ç®¡çããããã¸ã§ã¯ããã¡ã¤ã«å ã«ã½ã¼ã¹ãç´æ¥è¿½å ãã¦ã³ã³ãã¤ã«ãã¦ãã¾ãã
Swiftã«ç§»è¡ããã¹ãã
ãSwiftã«ç§»è¡ãããã¨ã§ã以åããã表ç¾åãé«ããå ç¢ãªã³ã¼ããæ¸ããããã«ãªãã¾ãããã¢ããªã±ã¼ã·ã§ã³ãå ç¢ã§ãããã¨ã¯ãä»ã®ãã©ãããã©ã¼ã ã«æ¯ã¹ã¦ãªãªã¼ã¹ãµã¤ã¯ã«ãé·ããªã£ã¦ãã¾ããã¡ãªiOSã¢ããªã±ã¼ã·ã§ã³ã«ããã¦ãé常ã«éè¦ã§ãã
ãå®è¡æã®ã¯ã©ãã·ã¥ã¯!
ã使ç¨ãã¦ãå¼·å¶çã«ã¢ã³ã©ããããç®æããunowned
ã§ãã£ããã£ããç®æãªã©ã«éå±ããã¦ããããã以å¤ã®ã¯ã©ãã·ã¥ãæããããªã³ã¼ãã¯å®è¡åã«æ¤åºãæããªãã¾ããã
ããã®ãããªã¡ãªãããè¸ã¾ããã¨ããæ°è¦ã®ããã¸ã§ã¯ããããã¾ã è¦æ¨¡ãå°ãããé·æçã«éç¨ãããããªããã¸ã§ã¯ããã«é¢ãã¦ã¯Swiftãç©æ¥µçã«é¸æããã¹ãã§ãã
ãä¸æ¹ã§ãã大è¦æ¨¡ãªããã¸ã§ã¯ãããSwiftåããã®ã¯é£ããã¨æãã¾ããã段éç移è¡ã¨ãã£ã¦ããåè¿°ã®ããã«ãã¢ãã«ãAPIã¯ã©ã¤ã¢ã³ãã®ã¬ã¤ã¤ã¼ãæ¸ãæããã¨ãªãã¨éç¾å®çãªä½æ¥éã«æãå ¼ãã¾ãããã¾ããæ°è¦ã®ã³ã¼ãã ããSwiftãã¨ããã®ãæªãã¨ã¾ã§ã¯ããã¾ããããSwiftã®å©ç¹ããã«ã«æ´»ç¨ã§ããªãç¹ãè¨èªåãæ¿ãã®ã¹ã¤ããã³ã°ã³ã¹ããçºçããç¹ãªã©ãèæ ®ããã¨ãããã»ã©ã¡ãªãããç¡ãã®ã§ã¯ãªãã§ããããã
ãããã«
ãSwiftã¯ã¾ã ã¾ã çºå±éä¸ãªè¨èªã§ã¯ãããã®ã®ãæ¢ã«ååãªæ©è½ãåããã¾ãããã®ã¨ã³ã·ã¹ãã ãå å®ãã¦ãã¦ãã¾ããSwiftã®æ©è½ãååã«æ´»ç¨ããã°ãObjective-Cã«æ¯ã¹ã¦ãä¿å®æ§ãé«ããå®å ¨ãªã¢ããªã±ã¼ã·ã§ã³ãå®è£ ãããã¨ãã§ããã§ãããããã®è¨äºããçæ§ã®æ°è¦ããã¸ã§ã¯ãã¸ã®Swiftã®æ¡ç¨ãã¾ãObjective-CããSwiftã¸ã®ç§»è¡ã®å¾æ¼ãã«å°ãã§ããªãã°å¹¸ãã§ãã