deep dive into oss written in swift

34
Deep dive into OSS written in Swift yukiasai

Upload: yuki-asai

Post on 07-Jan-2017

2.562 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Deep dive into oss written in swift

Deep dive into OSS written in Swift

yukiasai

Page 2: Deep dive into oss written in swift

・浅井勇樹 28歳

・ Github : yukiasai

・出身:福井県福井高専

・所属 : 株式会社マネーフォワード

・マネーフォワード - 自動家計簿アプリ

・経歴 : 株式会社ナチュラルスタイル

・ ZOZOTOWN - ファッション通販アプリ

・WEAR - ファッションコーディネートアプリ

・得意 : iOS

・趣味 : コードリーディング

自己紹介

Page 3: Deep dive into oss written in swift

日常のコードリーディング

・通勤時間中はだいたい Github

・おやすみ前もだいたい Github

・ Trending repositories - Github

・最近人気のリポジトリランキング

・ Search Github - Github

・使い方がわからないクラスがあったらとりあえず検索

・みんながどんな使い方をしているかわかる

Page 4: Deep dive into oss written in swift

社内でコードリーディング

・週1で Swiftコードリーディング会を開催

・全社の iOSエンジニアに知見を共有するのが目的

・有名どころから自作まで様々

・ Alamofire

・ Bond

・ ObjectMapper

・ SwiftTask

・などなど、、、

Page 5: Deep dive into oss written in swift

紹介する OSS

・ Shoyu - yukiasai/Shoyu

・ UITableViewをもっと簡単に

・ Kaiseki - yukiasai/Kaiseki

・ JSONのパースを自動で

Page 6: Deep dive into oss written in swift

Shoyu

UITableViewをもっと簡単に

Page 7: Deep dive into oss written in swift

UITableViewを普通に使うとこうなるfunc numberOfSectionsInTableView(tableView: UITableView) -> Int { return 3}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: return 5 case 1: return 3 default: fatalError() }}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { switch (indexPath.section, indexPath.row) { case (0, _): let cell = tableView.dequeueReusableCellWithIdentifier("MemberCell") as! MemberTableViewCell return cell case (1, _): let cell = tableView.dequeueReusableCellWithIdentifier("GroupCell") as! GroupTableViewCell return cell default: fatalError() }}

Page 8: Deep dive into oss written in swift

UITableViewあるある

・デリゲートメソッドが switch文( if文)地獄になる

・ビューの見た目とコードの見た目が乖離している

・どのセクションにどのローが表示されているか追うのが大変

・ switch文を一箇所いじるとほぼ全箇所に影響する

・バグの温床となる可能性が極めて高い

・カスタムセルを使う場合デリゲートメソッド内でいちいちキャストしてやる必要がある

Page 9: Deep dive into oss written in swift

Shoyuだとこうなる

tableView.source = Source() .createSection { section in section.createRows(5) { (_, row: Row<MemberTableViewCell>) in row.height = 52 row.configureCell = { cell, _ in } row.didSelect = { _ in } } } .createSection { section in section.createRows(3) { (_, row: Row<GroupTableViewCell>) in row.height = 52 row.configureCell = { cell, _ in } row.didSelect = { _ in } } }tableView.reloadData()

Page 10: Deep dive into oss written in swift

Shoyuの利点

・ switch文( if文)地獄から開放される

・ビューの見た目とコードの見た目が近い

・セクションの追加、ローの追加がとっても楽ちん

Page 11: Deep dive into oss written in swift

Shoyuで使われているテクニック

・初期化クロージャ付き init

・ジェネリクス対応の Sectionと Row

Page 12: Deep dive into oss written in swift

初期化クロージャ付き init

Page 13: Deep dive into oss written in swift

例えば Rowの init

init(@noescape closure: (Row<T> -> Void)) { closure(self)}

使うときlet row = Row<XxxxCell>() { $0.height = 52}

利点

・インデントが一つ下がるのでコードが見やすくなる

・スコープをより小さく保つことができる

Page 14: Deep dive into oss written in swift

OSSでこんなのもありました

Then - devxoul/Then

let view = UIView().then { $0.backgroundColor = UIColor.redColor()}

Page 15: Deep dive into oss written in swift

ジェネリクス対応の Sectionと Row

Page 16: Deep dive into oss written in swift

ジェネリック対応の Sectionと Row

public class Section<HeaderType: UIView, FooterType: UIView>: SectionType { // ごにょごにょ}

public class Row<T: UITableViewCell>: RowType { // ごにょごにょ}

利点・ビューのタイプが確定するのでタイプセーフが貫ける

・デリゲートでいちいちキャストするみたいなのをしなくて良い

Page 17: Deep dive into oss written in swift

今一度コードを見てみる

tableView.source = Source() .createSection { section in section.createRows(5) { (_, row: Row<MemberTableViewCell>) in row.height = 52 row.configureCell = { cell, _ in } // <- この cellのタイプはMemberTableViewCellになる row.didSelect = { _ in } } } .createSection { section in section.createRows(3) { (_, row: Row<GroupTableViewCell>) in row.height = 52 row.configureCell = { cell, _ in } // <- この cellのタイプは GroupTableViewCellになる row.didSelect = { _ in } } }tableView.reloadData()

Page 18: Deep dive into oss written in swift

今後対応したいこと

・ UICollectionView対応・遅延評価対応

Page 19: Deep dive into oss written in swift

Kaiseki

JSONのパースを自動で

Page 20: Deep dive into oss written in swift

Kaisekiを使うとこうなる

こんなオブジェクトを宣言

class Object: Entity { // Basic let int = Property<Int>() let string = Property<String>() // Array let array = Property<[Bool]>() // Optional let optional = Property<Int?>() // Entity let object = Property<Object?>()}

使い方let json: [String: AnyObject] = [“int”: 1, “string”: “aaa”, “array”: [true, false], “optional”: null, ....]let obj = Object.fromJSON(json: jsonData)obj.int.value // -> 1obj.string.value // -> aaa

Page 21: Deep dive into oss written in swift

Kaisekiで使われているテクニック

・Mirrorを用いた自動マッピング・プロトコルで構造体( Int,Optional,Array)を拡張

Page 22: Deep dive into oss written in swift

Mirrorを用いた自動マッピング

Page 23: Deep dive into oss written in swift

Mirrorを用いた自動マッピング

Mirror : Swiftのリフレクション API

class Object { var id: Int = 0 var name: String = ""}

let obj = Object()Mirror(reflecting: obj).children.forEach { child in print(child)}

出力

(Optional("id"), 0)(Optional("name"), "")

・プロパティ名と値がとれるので、、・ JSONのキー名とプロパティ名を付け合わせてパースする!

Page 24: Deep dive into oss written in swift

Entityクラス

public class Entity: ValueType { typealias ReflectedProperty = (label: String, property: PropertyType) private lazy var reflectedProperties: [ReflectedProperty] = { return Mirror(reflecting: self).children.filter { $1 is PropertyType }.flatMap {($0!, $1 as! PropertyType)} }() public static func fromJSON(json: AnyObject?) -> Self? { // … reflectedProperties.forEach { let key = $1.keyWith($0) if let value = dic[key] { $1.fromJSON(value) } } }}

Page 25: Deep dive into oss written in swift

Propertyクラス

・なぜ Properyクラスでラップしているか?・Mirrorで取得できる Childが構造体(タプル)・値を取得はできるものの変更することができない・ Child.valueがクラスの場合は参照が渡ってくる・ Property.valueを変更することで間接的に値を変更する

public class Property<Value: ValueType>: PropertyType { public var value public func fromJSON(json: AnyObject) { if json is NSNull { if let value = Value.fromJSON(nil) where Value.isOptional { self.value = value } return } if let value = Value.fromJSON(json) { self.value = value } }}

Page 26: Deep dive into oss written in swift

ValueType

・ Property<ValueType>に指定できる ValueType一覧・ Bool・ Int・ Float・ Double・ String・ Optinal・ Array

Page 27: Deep dive into oss written in swift

今一度サンプルコードを見てみる

こんなオブジェクトを宣言

class Object: Entity { // Basic let int = Property<Int>() let string = Property<String>() // Array let array = Property<[Bool]>() // Optional let optional = Property<Int?>() // Entity let object = Property<Object?>()}

使い方let json: [String: AnyObject] = [“int”: 1, “string”: “aaa”, “array”: [true, false], “optional”: null, ....]let obj = Object.fromJSON(json: jsonData)obj.int.value // -> 1obj.string.value // -> aaa

Page 28: Deep dive into oss written in swift

プロトコルで構造体( Int,Optional,Array)を拡張

Optionalの例を紹介

Page 29: Deep dive into oss written in swift

Optional<T>を ValueTypeで拡張public protocol ValueType { static func fromJSON(json: AnyObject?) -> Self? func toJSON() -> AnyObject?}

extension Optional: ValueType { public static func fromJSON(json: AnyObject?) -> Wrapped?? { guard let valueType = Wrapped.self as? ValueType.Type, let value = valueType.fromJSON(json), let wrapped = value as? Wrapped else { return .Some(.None) } return .Some(.Some(wrapped)) } public func toJSON() -> AnyObject? { switch self { case .Some(let wrapped): guard let value = wrapped as? ValueType else { return nil } return value.toJSON() case .None: return nil } }}

Page 30: Deep dive into oss written in swift

どういうこと?

static func fromJSONlet optionalInt = Int?.fromJSON(json: 1)print(optionalInt) // -> Optional<Optional(1)>if let unwrappedOptinalInt = optionalInt { print(unwrappedOptionalInt) // -> Optional(1)}

・上記のコードは Optional<Int>へのスタティックメソッド呼び出し・ fromJSONは Self?を返すので、 optionalIntの型は Optional<Optional<Int>>になる

func toJSON( instance func)let json = optionalInt.toJSON()

・ Optional<Int>に追加された toJSONを呼び出している

Page 31: Deep dive into oss written in swift

Propertyクラスで行われていること

public class Property<Value: ValueType>: PropertyType { public var value: Value public func fromJSON(json: AnyObject) { // ...NSNullの処理 if let value = Value.fromJSON(json) { // <- ここ self.value = value } } public func toJSON() -> AnyObject? { if let json = value.toJSON() { return json } // ...NSNullの処理 return nil }}

Page 32: Deep dive into oss written in swift

今後対応したいこと

・ Property.valueへのアクセスをもっと簡単にしたい・ Property<Bool>を Boolと同様に扱えるようにしたい

Page 33: Deep dive into oss written in swift

紹介した OSS

・ Shoyu - yukiasai/Shoyu

・ UITableViewをもっと簡単に

・ Kaiseki - yukiasai/Kaiseki

・ JSONのパースを自動で

Page 34: Deep dive into oss written in swift

Thank you!

yukiasai