Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

ゲンバのSwift

7.514 visualizaciones

Publicado el

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

Publicado en: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí

ゲンバのSwift

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

×