Your SlideShare is downloading. ×

ゲンバのSwift
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

ゲンバのSwift

412
views

Published on

Developers Summit 2015 19-C-3 発表資料

Developers Summit 2015 19-C-3 発表資料

Published in: Software

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
412
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
6
Comments
0
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. ゲンバのSwift Feb 19, 2015 クラスメソッド株式会社 安達 勇一
  • 2. 安達 勇一 • 2013/12 入社 • TwitterID:    @UsrNameu1 • Github:    https://github.com/UsrNameu1 • Blog:    http://dev.classmethod.jp/ author/yad •        で連載やって ます
  • 3. Topics for today • UIKitのSelector • TestでのMock • iOS7でのSwift OSS • Moduleの穴 • 後方互換性
  • 4. UIKitのSelector • UIKitのイベントハンドリング • SwiftでのSelector • Objective-Cとの比較 • BlocksKit
  • 5. UIKitでのイベントハンドリング • UIControl, UIBarButtonItem の target, action navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("cancel", comment: ""), style: .Bordered, target: self, action: Selector(“cancelButtonDidTapped:”));
  • 6. UIKitでのイベントハンドリング • UIControl, UIBarButtonItem の target, action navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("cancel", comment: ""), style: .Bordered, target: self, action: Selector(“cancelButtonDidTapped:”));
  • 7. UIKitでのイベントハンドリング • UIControl, UIBarButtonItem の target, action navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("cancel", comment: ""), style: .Bordered, target: self, action: Selector(“cancelButtonDidTapped:”)); UIKitのAPIのイベントハンドリングでは Selectorが要を握る
  • 8. SwiftでのSelector • Selectorの定義 struct Selector : StringLiteralConvertible, NilLiteralConvertible { ...
  • 9. SwiftでのSelector • Selectorの定義 struct Selector : StringLiteralConvertible, NilLiteralConvertible { ... "buttonDidTouchUpInside:"リテラルで宣言可能→
  • 10. SwiftでのSelector • Selectorの定義 struct Selector : StringLiteralConvertible, NilLiteralConvertible { ... "buttonDidTouchUpInside:" Selectorを指定しない時はnilを入れられる→ nil リテラルで宣言可能→
  • 11. • リテラルで宣言可能 navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("cancel", comment: ""), style: .Bordered, target: self, action: “cancelButtonDidTapped:"); SwiftでのSelector
  • 12. • Selectorを指定しない時はnilを入れられる navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("cancel", comment: ""), style: .Bordered, target: self, action: nil); SwiftでのSelector
  • 13. Objective-Cとの比較 • Objective-C • Swift [removeButton addTarget:self action:@selector(removeButtonDidTouchUpInside:) forControlEvents:UIControlEventTouchUpInside]; removeButton.addTarget(self, action: Selector("removeButtonDidTouchUpInside:"), forControlEvents: .TouchUpInside)
  • 14. Objective-Cとの比較 • Objective-C • Swift [removeButton addTarget:self action:@selector(removeButtonDidTouchUpInside:) forControlEvents:UIControlEventTouchUpInside]; removeButton.addTarget(self, action: Selector("removeButtonDidTouchUpInside:"), forControlEvents: .TouchUpInside) ⇧Selector名が間違っていたら コンパイラが警告を出してくれる
  • 15. Objective-Cとの比較 • Objective-C • Swift [removeButton addTarget:self action:@selector(removeButtonDidTouchUpInside:) forControlEvents:UIControlEventTouchUpInside]; removeButton.addTarget(self, action: Selector("removeButtonDidTouchUpInside:"), forControlEvents: .TouchUpInside) ⇧Selector名が間違っていたら コンパイラが警告を出してくれる ⇧Selector名が間違っていても コンパイラは警告を出してくれない
  • 16. Objective-Cとの比較 • Objective-C • Swift [removeButton addTarget:self action:@selector(removeButtonDidTouchUpInside:) forControlEvents:UIControlEventTouchUpInside]; removeButton.addTarget(self, action: Selector("removeButtonDidTouchUpInside:"), forControlEvents: .TouchUpInside) ⇧Selector名が間違っていたら コンパイラが警告を出してくれる ⇧Selector名が間違っていても コンパイラは警告を出してくれない
  • 17. BlocksKit • Podfile • Brigdeing Header pod 'BlocksKit/UIKit' #import <BlocksKit/BlocksKit+UIKit.h>
  • 18. BlocksKit • Before removeButton.addTarget(self, action: Selector("removeButtonDidTouchUpInside:"), forControlEvents: .TouchUpInside) ⇧Selector名が間違っていても コンパイラは警告を出してくれない
  • 19. BlocksKit • Before • After removeButton.addTarget(self, action: Selector("removeButtonDidTouchUpInside:"), forControlEvents: .TouchUpInside) ⇧Selector名が間違っていても コンパイラは警告を出してくれない removeButton.bk_addEventHandler({[weak self] sender in self?.removeButtonDidTouchUpInside(sender) return Void() }, forControlEvents: .TouchUpInside) ⇧ハンドラの中でメソッドを直に呼び出すため、 メソッド名が間違っていたらコンパイラはエラーになる
  • 20. • 循環参照によるリーク BlocksKit
  • 21. • weak, unownedで解決 BlocksKit removeButton.bk_addEventHandler({[weak self] sender in self?.removeButtonDidTouchUpInside(sender) return Void() }, forControlEvents: .TouchUpInside)
  • 22. • weak, unownedで解決 BlocksKit removeButton.bk_addEventHandler({[weak self] sender in self?.removeButtonDidTouchUpInside(sender) return Void() }, forControlEvents: .TouchUpInside) ・ weak : selfがなくなった後もアクセスされる場合 ・ unowned : selfがなくなった後にアクセスされない場合
  • 23. TestでのMock • iOSでのテスト実施 • 2つのFramework : Quick, Nimble • SwiftでのMock実現方法
  • 24. iOSでのテスト実施 • 設計、実装の手助けの為 • Model層に対して実施 • 挙動が明らかなもの(イニシャライザ等)については 省略した
  • 25. 2つのFramework : Quick, Nimble • QuickはテストのためのBDD流DSLを用意 • Nimbleは実際の値と期待する値を比較するマッチャー • テストはプロダクトコードに含まれない為、     iOS 8 向けにインストール
  • 26. 2つのFramework : Quick, Nimble • 病院にかかわる一連のモデル create confirm use public struct Diagnosis { public let name: String public init(name: String) { self.name = name } } public protocol Examinable { func examine() -> Diagnosis } public class Doctor: Examinable { public init() {} public func examine() -> Diagnosis { return Diagnosis(name: "cold") } } public class Hospital { private let examinable: Examinable public init(examinable: Examinable) { self.examinable = examinable } public func serve() -> Diagnosis { return examinable.examine() } }
  • 27. https://github.com/UsrNameu1/QuickStudy
  • 28. 2つのFramework : Quick, Nimble • 診断データ型 public struct Diagnosis { public let name: String public init(name: String) { self.name = name } }
  • 29. 2つのFramework : Quick, Nimble • 診断者プロトコル public protocol Examinable { func examine() -> Diagnosis }
  • 30. 2つのFramework : Quick, Nimble • 医者クラス public class Doctor: Examinable { public init() {} public func examine() -> Diagnosis { return Diagnosis(name: "cold") } }
  • 31. 2つのFramework : Quick, Nimble • 病院クラス public class Hospital { private let examinable: Examinable public init(examinable: Examinable) { self.examinable = examinable } public func serve() -> Diagnosis { return examinable.examine() } }
  • 32. 2つのFramework : Quick, Nimble • BDD Example class HospitalSpecs: QuickSpec { override func spec() { describe("病院の診断について") { context("診察できる人が診断を行うとき") { let doctor = Doctor() let hospital = Hospital(examinable: doctor) it("診察できる人の診断を返すこと。") { expect(hospital.serve().name) == "cold" } } } } }
  • 33. SwiftでのMock実装例 class HospitalSpecs: QuickSpec { // protocol準拠でMockを使う override func spec() { describe("病院の診断について") { context("診察できる人が診断を行うとき") { class MockExaminable: Examinable { override func examine() -> Diagnosis { return Diagnosis(name: "cold") } } let mock = MockExaminable() let hospital = Hospital(examinable: mock) it("診察できる人の診断を返すこと。") { expect(hospital.serve().name) == "cold" } } } } }
  • 34. SwiftでのMock実装例 class HospitalSpecs: QuickSpec { // protocol準拠でMockを使う override func spec() { describe("病院の診断について") { context("診察できる人が診断を行うとき") { class MockExaminable: Examinable { override func examine() -> Diagnosis { return Diagnosis(name: "cold") } } let mock = MockExaminable() let hospital = Hospital(examinable: mock) it("診察できる人の診断を返すこと。") { expect(hospital.serve().name) == "cold" } } } } }
  • 35. SwiftでのMock実装例 class HospitalSpecs: QuickSpec { // 継承でMockを使う override func spec() { describe("病院の診断について") { context("診察できる人が診断を行うとき") { class MockDoctor: Doctor { override func examine() -> Diagnosis { return Diagnosis(name: "headache") } } // HospitalがDoctorを直接用いている時も使える let mock = MockDoctor() let hospital = Hospital(examinable: mock) it("診察できる人の診断を返すこと。") { expect(hospital.serve().name) == "headache" } } } } }
  • 36. SwiftでのMock実装例 class HospitalSpecs: QuickSpec { // 継承でMockを使う override func spec() { describe("病院の診断について") { context("診察できる人が診断を行うとき") { class MockDoctor: Doctor { override func examine() -> Diagnosis { return Diagnosis(name: "headache") } } // HospitalがDoctorを直接用いている時も使える let mock = MockDoctor() let hospital = Hospital(examinable: mock) it("診察できる人の診断を返すこと。") { expect(hospital.serve().name) == "headache" } } } } }
  • 37. SwiftでのMock実装例 • protocol, subclassは一長一短 • protocol準拠のMockの場合、@objc とoptionalをつけな い限り必須実装をモックにもれなく実装する必要があ る • subclassのMockの場合、ネストの深い箇所では Segmentation fault 11が起こるケースがあった
  • 38. iOS7でのSwift OSS • OSSのインストール • iOS7でOSSを使う • 使用中のOSS
  • 39. OSSのインストール • git submodule • Cocoapods (0.36以上) • Carthage
  • 40. OSSのインストール • git submodule • Cocoapods (0.36以上) • Carthage ←プロジェクトではこれを利用 ←iOS8以降 ←iOS8以降
  • 41. OSSのインストール • git submodule でのOSS導入例(Alamofire) $ cd (Project dir) $ git submodule add https://github.com/ Alamofire/Alamofire.git
  • 42. 詳しくはWebで!  http://dev.classmethod.jp/references/swift-oss- alamofire-1/
  • 43. iOS7でOSSを使う https://github.com/CocoaPods/swift/issues/9
  • 44. iOS7でOSSを使う
  • 45. iOS7でOSSを使う https://github.com/CocoaPods/swift/issues/9
  • 46. iOS7でOSSを使う • iOS8 では Dynamic Library の形式では使えないので直 にソースファイルをプロジェクトに入れる必要がある • 極力 git submodule の更新のみでファイル追従を行うた めにCopyせずにソースファイルへの参照をリンク
  • 47. iOS7でOSSを使う
  • 48. iOS7でOSSを使う チェックを外して参照のみ保持
  • 49. 使用中のOSS • Alamofire : HTTP通信のためのOSS • SwiftyJSON : JSONハンドリングのためのOSS • BrightFutures : 非同期処理のためのOSS • Quick : テストのDSLを提供するOSS • Nimble : テストのマッチャ−を提供するOSS
  • 50. • Alamofire • SwiftyJson • BrightFutures • Quick • Nimble テストターゲットのみに含まれる為、 テストをiOS8向けとして Dynamic frameworkでプロジェクトに入れた 使用中のOSS
  • 51. • Alamofire • SwiftyJson • BrightFutures • Quick • Nimble テストターゲットのみに含まれる為、 テストをiOS8向けとして Dynamic frameworkでプロジェクトに入れた アプリターゲットの対象に iOS7が含まれるため frameworkとしてではなく、 ソースの参照を保持するようにした 使用中のOSS
  • 52. 使用中のOSS : Alamofire https://github.com/Alamofire/Alamofire
  • 53. 使用中のOSS : Alamofire https://github.com/Alamofire/Alamofire
  • 54. 使用中のOSS : SwiftyJSON https://github.com/SwiftyJSON/SwiftyJSON
  • 55. 使用中のOSS : SwiftyJSON https://github.com/SwiftyJSON/SwiftyJSON
  • 56. 使用中のOSS : BrightFutures https://github.com/Thomvis/BrightFutures
  • 57. 使用中のOSS : BrightFutures https://github.com/Thomvis/BrightFutures • Before User.logIn(username, password) { user, error in if !error { Posts.fetchPosts(user, success: { posts in // do something with the user's posts }, failure: handleError) } else { // handeError is a custom function to handle errors handleError(error) } }
  • 58. 使用中のOSS : BrightFutures https://github.com/Thomvis/BrightFutures • After User.logIn(username,password).flatMap { user in Posts.fetchPosts(user) }.onSuccess { posts in // do something with the user's posts }.onFailure { error in // either logging in or fetching posts failed }
  • 59. Moduleの穴 • クラス名だけを文字列で取 得する • NSManagedObject in Test • Storyboard & InterfaceBuiler
  • 60. クラス名だけを文字列で取得する • SwiftでNSObjectのクラス名を取得する際にはモジュー ル名も含まれる
  • 61. クラス名だけを文字列で取得する // モジュール名.Sample が得られる let nameWithModule = NSStringFromClass(Sample.self) // Sampleのみを取得したい let nameWithoutModule = Sample.nameForClass
  • 62. クラス名だけを文字列で取得する • SwiftでNSObjectのクラス名を取得する際にはモジュー ル名も含まれる • 従来のクラス名取得機能についてはエクステンション で実装
  • 63. クラス名だけを文字列で取得する extension NSObject { /// クラス名をモジュール名を取り除いて取得します。 public class var nameForClass: String { return NSStringFromClass(self) .componentsSeparatedByString(".").last! } }
  • 64. NSManagedObject in Test • NSManagedObjectサブクラスを用いたクラスのテスト で実行時に落ちる
  • 65. NSManagedObject in Test • NSManagedObjectサブクラスを用いたクラスのテスト で実行時に落ちる *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'executeFetchRequest:error: A fetch request must have an entity.' *** First throw call stack: ( 0 CoreFoundation 0x000000010387ef35 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x0000000103517bb7 objc_exception_throw + 45 2 CoreData 0x00000001025e137d -[NSManagedObjectContext executeFetchRequest:error:] + 4541
  • 66. NSManagedObject in Test • NSManagedObjectサブクラスを用いたクラスのテスト で実行時に落ちる • Objective-Cから見た時のNSObjectサブクラス名もSwift のクラスに対してはModule名.クラス名 • @objc()キーワードを使ってObjective-Cから見た時の NSObjectサブクラス名を変更する
  • 67. NSManagedObject in Test • Before class Product: NSManagedObject { @NSManaged var name: String }
  • 68. NSManagedObject in Test • Before • After class Product: NSManagedObject { @NSManaged var name: String } @objc(Product) class Product: NSManagedObject { @NSManaged var name: String }
  • 69. NSManagedObject in Test • Before • After class Product: NSManagedObject { @NSManaged var name: String } @objc(Product) class Product: NSManagedObject { @NSManaged var name: String } @objc()キーワードを使ってObjective-Cから見た 時のNSObjectサブクラス名を変更
  • 70. Storyboard & InterfaceBuilder • Storyboardで定義したInitialViewControllerに接続した UINavigationControllerのrootViewControllerがうまく初期 化されない
  • 71. Storyboard & InterfaceBuilder • Xcode 6 より追加されたModule入力欄にターゲットモ ジュールを入力 • またはViewController宣言の前に             @objcでObjective-Cから見た             クラス名を記述 http://stackoverflow.com/questions/ 24924966/xcode-6-strange-bug- unknown-class-in-interface-builder-file 
  • 72. 後方互換性 • CIとマイナーアップデート • Swift 1.2 • Chris Lattnerの哲学
  • 73. CIとマイナーアップデート • CIサービスはTravisを利用 • Xcodeのマイナーアップデート6.1.0 →6.1.1によるトラ ブル (Travis側がデフォルトで6.1.0だった)
  • 74. CIとマイナーアップデート • Travisがすぐに対応してくれた • Twitterのアカウントで最新情報にキャッチアップ https://twitter.com/travisci osx_image: xcode611
  • 75. CIとマイナーアップデート • 0.0.1単位のマイナーアップデートでも       XcodeでOptionalの変更がよくある
  • 76. CIとマイナーアップデート • 0.0.1単位のマイナーアップデートでも       XcodeでOptionalの変更がよくある 例 var textLabel: UILabel? { get } var textLabel: UILabel! { get } Xcode 6.1.0 Xcode 6.1.1
  • 77. Swift 1.2 • Incremental Buildが実現! • より便利になった if let ! • 集合データ構造Set<T>! 
  • 78. Swift 1.2 • Incremental Buildが実現! • より便利になった if let ! • 集合データ構造Set<T>!  • Objective-Cの諸クラスからの暗黙的型変換禁止!
  • 79. Swift 1.2 • Objective-Cの諸クラスからの暗黙的型変換禁止! func mangleString(input: String) { // do something with input } let someString: NSString = "hello" mangleString(someString) // compile error!
  • 80. Swift 1.2 • Objective-Cの諸クラスからの暗黙的型変換禁止! func mangleString(input: String) { // do something with input } let someString: NSString = "hello" mangleString(someString as! String)
  • 81. Chris Lattnerの哲学 • Chris Lattner:Swiftつくった人 • コンパイラ基盤LLVMの作者でもある
  • 82. Chris Lattnerの哲学 • The Architecture of Open Source Applications : Chapter 11 LLVM • 邦語訳へのリンク http://m-takagi.github.io/aosa-ja/
  • 83. Chris Lattnerの哲学 もうひとつ、LLVMを軽量なままに保ち続けている大きな特徴がある (ライブラリを使うクライアント側から見ると賛否両論がある)。 それは、過去の決断も積極的に見直して、過去との互換性を気にせずにAPIを 大きく変更していくということだ。 たとえばLLVM IR自体に大幅な変更を加えるには、すべての最適化パスの変更 が必要になる。 そしてそれは、C++のAPIにも大きな影響を及ぼす。 LLVMでは過去に何度かそういうことがあった。 クライアント側にとっては辛かっただろうが、今後の開発を順調に進めていく ためにはそうすべきだった。
  • 84. Chris Lattnerの哲学 もうひとつ、LLVMを軽量なままに保ち続けている大きな特徴がある (ライブラリを使うクライアント側から見ると賛否両論がある)。 それは、過去の決断も積極的に見直して、過去との互換性を気にせずにAPIを 大きく変更していくということだ。 たとえばLLVM IR自体に大幅な変更を加えるには、すべての最適化パスの変更 が必要になる。 そしてそれは、C++のAPIにも大きな影響を及ぼす。 LLVMでは過去に何度かそういうことがあった。 クライアント側にとっては辛かっただろうが、今後の開発を順調に進めていく ためにはそうすべきだった。
  • 85. 過去の決断も積極的に見直して 過去との互換性を気にせずに APIを大きく変更していく
  • 86. Swift発表時
  • 87. Swift発表時 Swiftプロジェクト 開始直後
  • 88. Swift発表時 Swiftプロジェクト 開始直後 現在!
  • 89. Chris Lattnerの哲学 ここまではなんとかうまくやってきたが、まだやり残したことは多い。 さらに、今後LLVMが年を重ねるにつれて、軽快さが失われて硬直化してしま うというリスクもある。 この問題には魔法のような解決策があるわけではない。 でも、新しい問題領域を公開し続けていることや 過去の決断を躊躇せず再考していること、 さらに再設計で過去のコードを捨てられるようにしていることなどが、 すこしでもその対策になって欲しいものだ。 結局のところ、パーフェクトな存在を目指すのではなく、常に向上し続ける ことが大切なのだ。
  • 90. Chris Lattnerの哲学 ここまではなんとかうまくやってきたが、まだやり残したことは多い。 さらに、今後LLVMが年を重ねるにつれて、軽快さが失われて硬直化してしま うというリスクもある。 この問題には魔法のような解決策があるわけではない。 でも、新しい問題領域を公開し続けていることや 過去の決断を躊躇せず再考していること、 さらに再設計で過去のコードを捨てられるようにしていることなどが、 すこしでもその対策になって欲しいものだ。 結局のところ、パーフェクトな存在を目指すのではなく、常に向上し続ける ことが大切なのだ。
  • 91. 過去の決断を躊躇せず再考
  • 92. パーフェクトな存在を目指すの ではなく、常に向上し続けるこ とが大切