狠狠撸

狠狠撸Share a Scribd company logo
Deep dive into OSS written in Swift
yukiasai
?浅井勇樹 28歳
?Github : yukiasai
?出身:福井県 福井高専
?所属 : 株式会社マネーフォワード
?マネーフォワード - 自動家計簿アプリ
?経歴 : 株式会社ナチュラルスタイル
?ZOZOTOWN - ファッション通販アプリ
?WEAR - ファッションコーディネートアプリ
?得意 : iOS
?趣味 : コードリーディング
自己紹介
日常のコードリーディング
?通勤時間中はだいたいGithub
?おやすみ前もだいたいGithub
?Trending repositories - Github
?最近人気のリポジトリランキング
?Search Github - Github
?使い方がわからないクラスがあったらとりあえず検
索
?みんながどんな使い方をしているかわかる
社内でコードリーディング
?週1でSwiftコードリーディング会を開催
?全社のiOSエンジニアに知見を共有するのが目的
?有名どころから自作まで様々
?Alamofire
?Bond
?ObjectMapper
?SwiftTask
?などなど、、、
紹介するOSS
?Shoyu - yukiasai/Shoyu
?UITableViewをもっと簡単に
?Kaiseki - yukiasai/Kaiseki
?JSONのパースを自動で
Shoyu
UITableViewをもっと簡単に
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()
}
}
UITableViewあるある
?デリゲートメソッドがswitch文(if文)地獄になる
?ビューの見た目とコードの見た目が乖離している
?どのセクションにどのローが表示されているか追うのが
大変
?switch文を一箇所いじるとほぼ全箇所に影響する
?バグの温床となる可能性が極めて高い
?カスタムセルを使う場合デリゲートメソッド内でいちいちキャ
ストしてやる必要がある
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()
Shoyuの利点
?switch文(if文)地獄から開放される
?ビューの見た目とコードの見た目が近い
?セクションの追加、ローの追加がとっても楽ちん
Shoyuで使われているテクニック
?初期化クロージャ付き颈苍颈迟
?ジェネリクス対応の厂别肠迟颈辞苍と搁辞飞
初期化クロージャ付き颈苍颈迟
例えばRowのinit
init(@noescape closure: (Row<T> -> Void)) {
closure(self)
}
使うとき
let row = Row<XxxxCell>() {
$0.height = 52
}
利点
?インデントが一つ下がるのでコードが見やすくなる
?スコープをより小さく保つことができる
OSSでこんなのもありました
Then - devxoul/Then
let view = UIView().then {
$0.backgroundColor = UIColor.redColor()
}
ジェネリクス対応の厂别肠迟颈辞苍と搁辞飞
ジェネリック対応のSectionとRow
public class Section<HeaderType: UIView, FooterType: UIView>: SectionType {
// ごにょごにょ
}
public class Row<T: UITableViewCell>: RowType {
// ごにょごにょ
}
利点
?ビューのタイプが確定するのでタイプセーフが貫ける
?デリゲートでいちいちキャストするみたいなのをしなくて良い
今一度コードを見てみる
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()
今後対応したいこと
?UICollectionView対応
?遅延評価対応
Kaiseki
JSONのパースを自動で
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 // -> 1
obj.string.value // -> aaa
Kaisekiで使われているテクニック
?惭颈谤谤辞谤を用いた自动マッピング
?プロトコルで構造体(Int,Optional,Array)を拡張
惭颈谤谤辞谤を用いた自动マッピング
惭颈谤谤辞谤を用いた自动マッピング
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のキー名とプロパティ名を付け合わせてパースする!
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)
}
}
}
}
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
}
}
}
ValueType
?Property<ValueType>に指定できるValueType一覧
?Bool
?Int
?Float
?Double
?String
?Optinal
?Array
今一度サンプルコードを見てみる
こんなオブジェクトを宣言
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 // -> 1
obj.string.value // -> aaa
プロトコルで構造体(Int,Optional,Array)を拡張
Optionalの例を紹介
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
}
}
}
どういうこと?
static func fromJSON
let 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を呼び出している
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
}
}
今後対応したいこと
?Property.valueへのアクセスをもっと簡単にしたい
?Property<Bool>をBoolと同様に扱えるようにしたい
?
紹介したOSS
?Shoyu - yukiasai/Shoyu
?UITableViewをもっと簡単に
?Kaiseki - yukiasai/Kaiseki
?JSONのパースを自動で
Thank you!
yukiasai

More Related Content

Deep dive into oss written in swift