SlideShare a Scribd company logo
Developer Day
続・ゲンバのSwift
1
D-1
安達 勇一
クラスメソッド株式会社
Ⓒ Classmethod, Inc.
2015年03月29日
#cmdevio2015
2
安達 勇一
• 2013/12 入社

• TwitterID:   
@UsrNameu1

• Github:   
https://github.com/UsrNameu1

• Blog:   
http://dev.classmethod.jp/
author/yad

•        で連載やって
ます
Topics for today
• Enum
• Collection API
• BrightFutures
3Ⓒ Classmethod, Inc.
Topics for today
• Enum
• Collection API
• BrightFutures
4Ⓒ Classmethod, Inc.
Enum
• 離散値とEnum
• リソースとEnum
• エラーとEnum
5Ⓒ Classmethod, Inc.
離散値とEnum
• 決まった数値のみ許容するAPIのEndpoint :
6Ⓒ Classmethod, Inc.
POST api/v1/user
[
{ "name" : "Tarou", "gender" : 1 },
{ "name" : "Yoko", "gender" : 2 }
]
離散値とEnum
• 決まった数値のみ許容するAPIのEndpoint :
7Ⓒ Classmethod, Inc.
POST api/v1/user
[
{ "name" : "Tarou", "gender" : 1 },
{ "name" : "Yoko", "gender" : 2 }
]
決まった数値以外は
400 Bad Request
離散値とEnum
• 対応するモデル
8Ⓒ Classmethod, Inc.
struct User {
let name: String
let gender: Int
}
離散値とEnum
• 対応するモデル
9Ⓒ Classmethod, Inc.
struct User {
let name: String
let gender: Int
}
違う、そうじゃない!
struct User {
let name: String
let gender: Gender
}
enum Gender: Int {
case Male = 1
case Female = 2
}
離散値とEnum
• 限られた値しかとらない数値はEnumにすることで…
→APIエラーを防げる
10Ⓒ Classmethod, Inc.
POST api/v1/user
[
{ "name" : "Tarou", "gender" : 1 },
{ "name" : "Yoko", "gender" : 2 }
]
決まった数値以外は
400 Bad Request
離散値とEnum
• 限られた値しかとらない数値はEnumにすることで…
11Ⓒ Classmethod, Inc.
switch user.gender {
case 1 : println(“male”)
case 2 : println(“female”)
default : assert(false, “error”)
}
離散値とEnum
• 限られた値しかとらない数値はEnumにすることで…
→網羅性チェックによりassertionを書く手間を省ける
12Ⓒ Classmethod, Inc.
switch user.gender {
case .Male : println(“male”)
case .Female : println(“female”)
}
13Ⓒ Classmethod, Inc.
離散値はEnumにして
IOを型として束縛
Enum
• 離散値とEnum
• リソースとEnum
• エラーとEnum
14Ⓒ Classmethod, Inc.
リソースとEnum
• Enumで宣言された値に対する
• NSLocalizedString
• UIImage
→Objective-Cではコンバータやカテゴリ拡張を
 別に実装するなどで対処。
15Ⓒ Classmethod, Inc.
リソースとEnum
• Enumで宣言された値に対する
• NSLocalizedString
• UIImage
→SwiftではEnumに直接書けるようになりました!
16Ⓒ Classmethod, Inc.
リソースとEnum
• NSLocalizedStringを性別に対して取得
17Ⓒ Classmethod, Inc.
enum Gender: Int {
case Male = 1
case Female = 2
}
extension Gender {
var localizedString: String {
switch self {
case .Male:
return
NSLocalizedString(“male”, comment: “”)
case .Female:
return
NSLocalizedString(“female”, comment: “”)
}
}
}
リソースとEnum
• UIImageを性別に対して取得
18Ⓒ Classmethod, Inc.
enum Gender: Int {
case Male = 1
case Female = 2
}
extension Gender {
var thumbImage: UIImage {
switch self {
case .Male:
return UIImage(named: “male”)
case .Female:
return UIImage(named: “female”)
}
}
}
リソースとEnum
• Before
• After
19Ⓒ Classmethod, Inc.
cell.thumbImage =
[UIImage thumbImageWithGender: gender];
cell.title =
[NSString localizedStringWithGender: gender];
cell.thumbImage = gender.thumbImage
cell.title = gender.localizedString
20Ⓒ Classmethod, Inc.
Enumに紐付くリソースは
自身で管理・記述を簡潔化
Enum
• 離散値とEnum
• リソースとEnum
• エラーとEnum
21Ⓒ Classmethod, Inc.
エラーとEnum
• Objective-Cでの典型的なエラーハンドリング
(ライトバック渡し)
22Ⓒ Classmethod, Inc.
NSError *error = nil;
NSData *aData =
[NSData dataWithContentsOfURL:pathURL
options:NSDataReadingUncached
error:&error];
if (!error) {
// エラーがない時の処理
} else {
// エラーがある時の処理
}
エラーとEnum
• エラーハンドリングについてはほぼ定形の処理
• エラーが無い時に続けて簡単に処理を書きたい…
• Optionalでは何のエラーかわからない…
23Ⓒ Classmethod, Inc.
エラーとEnum
24Ⓒ Classmethod, Inc.
• エラーハンドリングについてはほぼ定形の処理
• エラーが無い時に続けて簡単に処理を書きたい…
• Optionalでは何のエラーかわからない…
そのお悩み
Result<T>が
解決します!!!!
エラーとEnum
• Result<T>
25Ⓒ Classmethod, Inc.
public enum Result<T> {
case Success(Box<T>)
case Failure(NSError)
public init(_ value: T) {
self = .Success(Box(value))
}
}
public final class Box<T> {
public let value: T
public init(_ value: T) {
self.value = value
}
}
エラーの可能性
を持った値
コンパイラによるエラー
回避の為のBoxクラス
エラーとEnum
• Before
• After
26Ⓒ Classmethod, Inc.
- (NSData *)dataWithURL:(NSURL *)filePathURL
error:(NSError *)error
{
// エラーがあるときにはerrorにポインタを渡し
// エラーがない時にnilでないNSData*を返す。
}
func dataWithURL(url: NSURL) -> Result<NSData>
{
// エラーがあるときもない時もResult型を返す。
}
エラーとEnum
• map in Optional<T>
27Ⓒ Classmethod, Inc.
// Noneの可能性をもった値を
let someInt = “127”.toInt()
// Noneの可能性を保ったまま中身だけ変更
someInt.map { val in val + 1 }
エラーとEnum
• map in Optional<T>
28Ⓒ Classmethod, Inc.
Some
None
Optional<T> Optional<U>
map(f: T -> U)
T
None
U
None
f
エラーとEnum
• map in Result<T>
29Ⓒ Classmethod, Inc.
extension Result {
func map<U>(f: T -> U) -> Result<U> {
switch self {
case .Success(let box) :
return .Success(Box(f(box.value)))
case .Failure(let err) :
return .Failure(err)
}
}
}
エラーの可能性を保って中の値をマッピングする
エラーとEnum
• map in Result<T>
30Ⓒ Classmethod, Inc.
// NSErrorの可能性をもった値を
let someInt = Result(127)
// NSErrorの可能性を保ったまま中身だけ変更
someInt.map { val in val + 1 }
エラーの可能性を保って中の値をマッピングする
エラーとEnum
31Ⓒ Classmethod, Inc.
Success
Failure
Result<T> Result<U>
map(f: T -> U)
T
NSError
U
NSError
f
• map in Result<T>
エラーとEnum
• flatMap in Optional<T> (Swift 1.2)
32Ⓒ Classmethod, Inc.
// Noneの可能性をもった値を
let someStr: String? = “127”
// 中身があったら新しい
// Noneの可能性をもった値に変更
let someInt = someStr?.toInt()
Optional Chaining
エラーとEnum
• flatMap in Optional<T> (Swift 1.2)
33Ⓒ Classmethod, Inc.
// Noneの可能性をもった値を
let someInt = “127”.toInt()
// 中身があったら新しい
// Noneの可能性をもった値に変更する
someInt.flatMap { val as? UInt8 }
// Noneの可能性をもった値を
let someStr: String? = “127”
// 中身があったら新しい
// Noneの可能性をもった値に変更
let someInt = someStr.flatMap { str in
str.toInt()
}
エラーとEnum
• flatMap in Optional<T> (Swift 1.2)
34Ⓒ Classmethod, Inc.
// Noneの可能性をもった値を
let someInt = “127”.toInt()
// 中身があったら新しい
// Noneの可能性をもった値に変更する
someInt.flatMap { val as? UInt8 }
// Noneの可能性をもった値を
let someStr: String? = “127”
// 中身があったら新しい
// Noneの可能性をもった値に変更
let someInt =
someStr.flatMap(String.toInt)
無引数インスタンスメソッドを関数として扱う
エラーとEnum
35Ⓒ Classmethod, Inc.
Some
None
Optional<T>
T
None
f
flatMap(f: T -> Optional<U>)
Optional<U>
U
None
• flatMap in Optional<T> (Swift 1.2)
エラーとEnum
36Ⓒ Classmethod, Inc.
extension Result {
func flatMap<U>(f: T -> Result<U>) -> Result<U> {
switch self {
case .Success(let box) :
return f(box.value)
case .Failure(let err) :
return .Failure(err)
}
}
}
新しいエラー可能性を持つ値にマッピングしてマージ
• flatMap in Result<T>
エラーとEnum
• flatMap in Result<T>
37Ⓒ Classmethod, Inc.
// NSErrorの可能性をもった値を
let someInt = Result(127)
// 中身があったら新しい
// 新しいNSErrorの可能性をもった値に変更
someInt.flatMap{ val in Result(val + 1)}
新しいエラー可能性を持つ値にマッピングしてマージ
エラーとEnum
38Ⓒ Classmethod, Inc.
Success
Failure
Result<T>
T
NSError
f
flatMap(f: T -> Result<U>)
Result<U>
U
NSError
• flatMap in Result<T>
39Ⓒ Classmethod, Inc.
Enumでエラーの
可能性を持つ
値を作る
40Ⓒ Classmethod, Inc.
Topics for today
• Enum
• Collection API
• BrightFutures
41Ⓒ Classmethod, Inc.
Collection API
• 実践 CollectionAPI
• map, filter, reduce
• flatMap(Swift 1.2)
42Ⓒ Classmethod, Inc.
let names = users.map { user in
user.name
}
println(names)
実践 CollectionAPI
• Swiftのコードでfor文は一回も使わなかった
43Ⓒ Classmethod, Inc.
var names = [String]()
for user in users {
names.append(user.name)
}
println(names)
マッピングのたびに
コード領域に変数が
一つ加わる
変数という状態を
考えずに済む
let totalAge =
users.reduce(0) { total, user in
total + user.age
}
println(totalAge)
実践 CollectionAPI
• Swiftのコードでfor文は一回も使わなかった
44Ⓒ Classmethod, Inc.
var totalAge = 0
for user in users {
totalAge += user.age
}
println(totalAge)
集計の際に変数1つが
コード領域に加わる
集計の際に変数は
考えなくていい
実践 CollectionAPI
• コード量を大幅に削減できた
45Ⓒ Classmethod, Inc.
- (NSArray *)namesForUsers:(NSArray *)users
{
NSMutableArray *names = [NSMutableArray new];
for(User *user in users) {
[names addObject:user.name];
}
return [NSArray arrayWithArray:names];
}
のようなマッピング用のメソッド、処理がmap一つで書ける
実践 CollectionAPI
• メモリ管理のためにunownedやweakを使う
46Ⓒ Classmethod, Inc.
users.map { [weak self] user in
self?.names.append(user.name)
return Void()
}
• map等のクロージャでselfを使う場合は
 循環参照の可能性からweak, unownedを考慮する
• 設計を考え直す
47Ⓒ Classmethod, Inc.
関数フローを用い
コード領域から
できるだけ状態を排する
Collection API
• 実践 CollectionAPI
• map, filter, reduce
• flatMap(Swift 1.2)
48Ⓒ Classmethod, Inc.
map, filter, reduce
• map
49Ⓒ Classmethod, Inc.
Array<T> Array<U>
map(transform: T -> U)
T U
T
T
U
U
transform
map, filter, reduce
• filter
50Ⓒ Classmethod, Inc.
Array<T> Array<T>
filter(includeElement: T -> Bool)
T
T
T
true/false
T
T
includeElement
map, filter, reduce
• reduce
51Ⓒ Classmethod, Inc.
Array<T>
reduce(initial: U,
combine: (U, T) -> U)
T
combine
T
T
U
U
initial
map, filter, reduce
• 文字列の配列のうち、二文字以上のものを選び、
 文字数のトータルを集計する
52Ⓒ Classmethod, Inc.
Before
NSArray *strings = [@“aaaa”, @“bbb”, @“c”, @“dd”];
NSUInteger totalLengthForStrings = 0;
for(NSString *string in strings) {
NSUInteger lengthOfString = string.length
if (lengthOfString >= 2) {
totalLengthForStrings += lengthOfString
}
}
NSLog(@“%ld”, totalLengthForStrings) // 9
map, filter, reduce
• 文字列の配列のうち、二文字以上のものを選び、
 文字数のトータルを集計する
53Ⓒ Classmethod, Inc.
After
let strings = [“aaaa”, “bbb”, “c”, “dd”]
let totalCount =
strings.map(countElements)
.filter { count in count >= 2 }
.reduce(0, +)
println(totalCount) // 9
54Ⓒ Classmethod, Inc.
コレクションを
関数フローで
整形・選択・集計する
Collection API
• 実践 CollectionAPI
• map, filter, reduce,
sorted, first, last
• flatMap(Swift 1.2)
55Ⓒ Classmethod, Inc.
56Ⓒ Classmethod, Inc.
flatMap(Swift 1.2)
let strings = [“aaaa”, “bbb”]
let bothCaseStrings =
strings.flatMap { str in
[str.uppercaseString, str.lowercaseString]
}
println(bothCaseStrings)
// [“aaaa”, “AAAA”, “bbb”, “BBB”]
57Ⓒ Classmethod, Inc.
flatMap(Swift 1.2)
Array<T> Array<U>
flatMap(transform: T -> [U])
T U
transform
T
T
U
U
U
U
U
58Ⓒ Classmethod, Inc.
Topics for today
• Enum
• Collection API
• BrightFutures
59Ⓒ Classmethod, Inc.
BrightFuture
• Futures in Swift
• Future
• Promise
• メソッド概観
• Pluggable Activities
60Ⓒ Classmethod, Inc.
• Swiftz
https://github.com/typelift/swiftz
• BrightFutures
https://github.com/Thomvis/BrightFutures
61Ⓒ Classmethod, Inc.
Futures in Swift
• Swiftz
関数型プログラミングインターフェイスをSwiftに追加
62Ⓒ Classmethod, Inc.
Futures in Swift
• Swiftz
多くの演算子はHaskellやF#から由来している
中には • (Option+8) などの特殊文字もある
63Ⓒ Classmethod, Inc.
Futures in Swift
• Swiftz
非同期を扱う為のクラスFutureも
抽象度の高い基底クラスを有するため採用見送り
64Ⓒ Classmethod, Inc.
Futures in Swift
• Swiftz
非同期を扱う為のクラスFutureも
抽象度の高い基底クラスを有するため採用見送り
65Ⓒ Classmethod, Inc.
Futures in Swift
66Ⓒ Classmethod, Inc.
BrightFutures
BrightFuture
• Futures in Swift
• Future
• Promise
• メソッド概観
• Pluggable Activities
67Ⓒ Classmethod, Inc.
68
コールバックハンドラでの
非同期処理
69Ⓒ Classmethod, Inc.
Future
• Objective-Cでの典型的な非同期ハンドリング
[[SomeTaskManager sharedInstance]
taskWithCompletion:^(NSData *data, NSError *err) {
if (err) {
// エラーハンドリング
} else {
// エラーがない時の処理
}
}];
70Ⓒ Classmethod, Inc.
Future
• エラーハンドリングについてはほぼ定形の処理
• エラーが無い時に続けて簡単に処理を書きたい…
• 非同期処理がまたがる度にネストが深くなっていく
71Ⓒ Classmethod, Inc.
Future
• エラーハンドリングについてはほぼ定形の処理
• エラーが無い時に続けて簡単に処理を書きたい…
• 非同期処理がまたがる度にネストが深くなっていく
そのお悩み
Future<T>が
解決します!!!!
72Ⓒ Classmethod, Inc.
public class Future<T> {
var result: Result<T>? = nil
...
}
非同期処理エラーの可能性
を持った値
Future
public enum Result<T> {
case Success(Box<T>)
case Failure(NSError)
}
エラーの可能性
を持った値
Result<T>は実装に入っている
73Ⓒ Classmethod, Inc.
Future
Success
Failure
T
NSError
Pending None
Some(Result<T>)
• Futureの取りうる状態は3つ
非同期処理中
非同期処理完了
74Ⓒ Classmethod, Inc.
Future
• Futureをつくる
public func future<T>(
context c: ExecutionContext = Queue.global,
task: () -> T
) -> Future<T>
let futureValue = future {
// 何か重い処理
return 返り値
}
必要に応じてキューを指定する
(Queueはdispatch_queue_tのラッパー)
75
Futureは非同期エラーの
可能性をもった値
BrightFuture
• Futures in Swift
• Future
• Promise
• メソッド概観
• Pluggable Activities
76Ⓒ Classmethod, Inc.
77Ⓒ Classmethod, Inc.
Promise
• Future<T>の生成に利用
func someTask() -> Future<NSData> {
let promise = Promise<NSData>()
SomeTaskManager.sharedInstance
.taskWithCompletion { data, err in
if err != nil { // エラーハンドリング
promise.failure(err)
} else { // エラーがない時の処理
promise.success(data)
}
}
return promise.future
}
78Ⓒ Classmethod, Inc.
Promise
• 「約束」「契約」を表すオブジェクト
func someTask() -> Future<NSData> {
let promise = Promise<NSData>()
SomeTaskManager.sharedInstance
.taskWithCompletion { data, err in
if err != nil { // エラーハンドリング
promise.failure(err)
} else { // エラーがない時の処理
promise.success(data)
}
}
return promise.future
}
コールバックが一回きり呼ばれる
契約を表す
Futureをエラーで
完了させる
Futureを成功で
完了させる
79Ⓒ Classmethod, Inc.
Promise
• 非同期処理が呼ばれた証として
 Future<T>オブジェクトを発行
func someTask() -> Future<NSData> {
let promise = Promise<NSData>()
SomeTaskManager.sharedInstance
.taskWithCompletion { data, err in
if err != nil { // エラーハンドリング
promise.failure(err)
} else { // エラーがない時の処理
promise.success(data)
}
}
return promise.future
}
Futureを発行する
80Ⓒ Classmethod, Inc.
BoltsFramework
Promise
81Ⓒ Classmethod, Inc.
• BrightFutures と BoltsFrameworkの比較
- (BFTask *)someTask {
BFTaskCompletionSource *task =
[BFTaskCompletionSource taskCompletionSource];
[[SomeTaskManager sharedInstance]
taskWithCompletion:^(NSData *data, NSError *err) {
if (err) {
[task setError:err];
} else {
[task setResult:data];
}
}];
return task.task;
}
Promise
Sourseの生成
完了処理
BFTaskをSourceから生成
82Ⓒ Classmethod, Inc.
• BrightFutures と BoltsFrameworkの比較
func someTask() -> Future<NSData> {
let promise = Promise<NSData>()
SomeTaskManager.sharedInstance()
.taskWithCompletion { data, err in
if err != nil {
promise.failure(err)
} else {
promise.success(data)
}
}
return promise.future
}
Promise
Promiseの生成
完了処理
FutureをPromiseから生成
83Ⓒ Classmethod, Inc.
Promise
• Promiseは発行したら必ず成功かエラーを一回だけ
発行するように実装
• Promiseに複数の成功を送るとランタイムエラーを
起こす
• 複数回呼ばれるようなDelegateとは相性が悪い
84
コールバックハンドラには
Promiseを用いてFutureを生成
BrightFuture
• Futures in Swift
• Future
• Promise
• メソッド概観
• Pluggable Activities
85Ⓒ Classmethod, Inc.
86Ⓒ Classmethod, Inc.
メソッド概観
• onComplete in Future<T>
onComplete(
callback: Result<T> -> Void
) -> Future<T>
T
NSError
None
Result<T>
callback
Future<T>
87Ⓒ Classmethod, Inc.
メソッド概観
futureTask.onComplete { result in
switch result {
case .Success(let val): ()
case .Failure(let err): ()
}
}
非同期計算の結果にかかわらす
Result<T> を返却
• onComplete in Future<T>
88Ⓒ Classmethod, Inc.
メソッド概観
• onSuccess in Future<T>
onSuccess(
callback: T -> Void
) -> Future<T>
T
NSError
None
Result<T>
callback
Future<T>
89Ⓒ Classmethod, Inc.
メソッド概観
• onSuccess in Future<T>
futureTask.onSuccess { value in
// T型のvalueに対する処理
}
非同期計算が成功した場合
Tを返却
90Ⓒ Classmethod, Inc.
メソッド概観
• onFailure in Future<T>
onFailure(
callback: NSError -> Void
) -> Future<T>
T
NSError
None
Result<T>
Future<T>
callback
91Ⓒ Classmethod, Inc.
メソッド概観
futureTask.onFailure { error in
// NSError型のerrorに対する処理
}
非同期計算が成功した場合
NSError を返却
• onFailure in Future<T>
92Ⓒ Classmethod, Inc.
メソッド概観
• map in Future<T>
Future<T> Future<U>
map(f: T -> U)
T
NSError
U
NSError
f
None
93Ⓒ Classmethod, Inc.
// 非同期エラーの可能性をもった値を
let futureData =
someTask() // Future<NSData>型の返り値
// 非同期エラーの可能性を保ったまま中身だけ変更
futureData.map { data in
NSString(
data: data,
encoding: NSUTF8StringEncoding
)
} // Future<NSString>型の返り値
メソッド概観
• map in Future<T>
94Ⓒ Classmethod, Inc.
メソッド概観
• flatMap in Future<T>
Future<T> Future<U>
flatMap(f: T -> Future<U>)
T
NSError
U
NSError
f
None
95Ⓒ Classmethod, Inc.
// 非同期エラーの可能性をもった値を
let someURLString =
someURLTask()// Future<String>型の返り値
// 中身がエラーでなければ続きの
// 非同期エラー可能性をもった値を評価
someURLString.flatMap(fetchData)
// fetchData: Stringを引数にもち
// Future<NSData>を返り値に持つ関数
メソッド概観
• flatMap in Future<T>
96
非同期処理を定形的な
ハンドリングで記述
BrightFuture
• Futures in Swift
• Future
• Promise
• メソッド概観
• Pluggable Activities
97Ⓒ Classmethod, Inc.
• 非同期処理のまとまり毎にFutureを生成
98Ⓒ Classmethod, Inc.
Pluggable Activities
→非同期処理同士を抜き差し、順序入替しやすくなった
Web API Task App DB Task
Web API TaskApp DB Task
• 非同期処理のまとまり毎にFutureを生成
99Ⓒ Classmethod, Inc.
Pluggable Activities
Web API App DB HeavyTaskInfrastructure
Use case Authentication UserLogic
→ロジックが
  非同期を要する処理かどうかがわかりやすくなった
→アクティビティ図とコードが対応するようになった
100Ⓒ Classmethod, Inc.
Pluggable Activities
User Event
API Call
DB Update
NSError NSError Success
• 非同期処理のまとまり毎にFutureを生成
101
Futureでアカルイ
非同期処理
102Ⓒ Classmethod, Inc.
103
おまけ
Topics for today
• Enum
• Collection API
• BrightFutures
104Ⓒ Classmethod, Inc.
Topics for today
• Enum
• Collection API
• BrightFutures
105Ⓒ Classmethod, Inc.
Optional<T>
Result<T>
Array<T>
Future<T>
106
• Enum
• Collection API
• BrightFutures
107Ⓒ Classmethod, Inc.
Optional<T>
Result<T>
Array<T>
Future<T>
map
map(f: T -> U) -> U?
map(f: T -> U) -> Result<U>
map(f: T -> U) -> Array<U>
map(f: T -> U) -> Future<U>
• Enum
• Collection API
• BrightFutures
108Ⓒ Classmethod, Inc.
flatMap
Optional<T>
Result<T>
Array<T>
Future<T>
flatMap(f: T -> U?) -> U?
flatMap(f: T -> Result<U>)
-> Result<U>
flatMap(f: T -> Array<U>)
-> Array<U>
flatMap(f: T -> Future<U>)
-> Future<U>
109Ⓒ Classmethod, Inc.
map ・ flatMapを
適用できる型を抽象化
XXX<T> map(f: T -> U) -> XXX<U>
flatMap(f: T -> XXX<U>)
-> XXX<U>
Functor・Applicative・Monadへ
110
111Ⓒ Classmethod, Inc.
Haskell・Scala等の
先達に根拠を求める
112Ⓒ Classmethod, Inc.
巨人の肩に乗る
Developer Day
スライドは後日ブログで公開します。
113
A-1
Ⓒ Classmethod, Inc.
#cmdevio2015

More Related Content

続・ゲンバのSwift