Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Boundaries In Practice // 実践的 “Boundaries”

Boundaries In Practice // 実践的 “Boundaries”

Talk given 3 Mar 2016 at try! Swift Tokyo

// Boundaries In Practice
One of the talks that I’ve enjoyed watching is Boundaries by Gary Bernhardt (https://www.destroyallsoftware.com/talks/boundaries). If you’ve seen a talk about functional programming in Swift, you’ve probably heard it being referenced. When I first watched the talk a few years ago, I understood the theory but wasn’t sure where exactly to apply the concepts. As I’ve been writing more and more Swift, I started to see that the concept of “Functional Core, Imperative Shell” applies not just to functional programming, but also to good engineering in general. In this talk, we’ll go over a couple of specific examples how these “boundaries” can help us write better, safer, and more future-proof Swift code. You don’t need to watch the Boundaries talk to understand this talk, but if you have time, definitely watch it because it’s a great talk.

// 実践的 “Boundaries”
Gary BernhardtさんのBoundariesの講演 (https://www.destroyallsoftware.com/talks/boundaries) 、ご存知ですか。ファンクショナルプログラミングの講演で良く参照される講演です。私は数年前に初めてこの講演を観た時、「Functional Core, Imperative Shell」の理論は理解できたのですが、実際どの様にこのコンセプトを実用化できるのか良く分かりませんでした。しかし、毎日少しずつSwiftを書いているうちに、このコンセプトはファンクショナルプログラミングだけではなく、良いエンジニアリングに一般的に適用できるのが明らかになってきました。今回はどの様に「Boundaries」を利用して、もっと良い、もっと安全なそして将来性のあるSwiftを書くことが出来るのかを幾つかの事例を紹介しながらお話しさせて頂きます。「Boundaries」をご覧になった事がなくても私の話をご理解頂けると思いますが、もしお時間があれば是非一度ご覧下さい。素晴らしい話です。

D3c5f7382824133c2718c8effb5894fb?s=128

Ayaka Nonaka

March 03, 2016
Tweet

Transcript

  1. ࣮ફత BOUNDARIES IN PRACTICE TRY! SWIFT 2016

  2. HI, I’M AYAKA. A.K.A. ࠼Ֆ @AYANONAGON

  3. None
  4. FUNCTIONAL CORE IMPERATIVE SHELL

  5. FUNCTIONAL CORE

  6. APP = MANY FUNCTIONAL CORES

  7. WITH IMPERATIVE SHELLS

  8. “ALL YOU REALLY KNOW MAYBE IS THAT YOUR TASTE IS

    IMPROVING A LOT FASTER THAN YOUR ABILITY.” ANDY MATUSCHAK, ADVANCED IOS APPLICATION ARCHITECTURE AND PATTERNS (WWDC 2014)
  9. ࣮ફత BOUNDARIES IN PRACTICE TRY! SWIFT 2016

  10. ୈҰ IMMUTABLE CORE NETWORK-Y SHELL ෆมͳਊɺωοτϫʔΫͬΆ͍ද໘

  11. None
  12. None
  13. struct Story { let ID: String let title: String let

    message: String let sender: User let recipient: User let date: NSDate // ... }
  14. class StoriesViewController: UIViewController { let stories: [Story] // ... }

  15. class StoryDetailViewController: UIViewController { init(story: Story) }

  16. class StoryDetailViewController: UIViewController { private let titleView: StoryTitleView private let

    senderView: AvatarView private let recipientView: AvatarView private let dateLabel: DateLabel init(story: Story) { titleView = StoryTitleView(story: story) senderView = AvatarView(user: story.sender) recipientView = AvatarView(user: story.recipient) dateLabel = DateLabel(date: story.date) } // ... }
  17. url_scheme://stories/12345

  18. class StoryDetailViewController: UIViewController { init(story: Story) }

  19. class StoryDetailViewController: UIViewController { init(story: Story) init(storyID: String) }

  20. class StoryDetailViewController: UIViewController { private let titleView: StoryTitleView private let

    senderView: AvatarView private let recipientView: AvatarView private let dateLabel: DateLabel init(story: Story) { /* Same as before લͱಉ͡ */ } init(storyID: String) { // Hmmmmmmm.ɹ͜ΕԿͩΖ͏ʁ } }
  21. class StoryDetailViewController: UIViewController { let storyID: String private var titleView:

    StoryTitleView? private var senderView: AvatarView? private var recipientView: AvatarView? private var dateLabel: DateLabel? init(story: Story) { /* Same as before લͱಉ͡ */ } init(storyID: String) { self.storyID = storyID titleView = nil senderView = nil recipientView = nil dataLabel = nil } // Load everything from API in viewDidLoad? // viewDidLoadͰAPI͔Βϩʔυʁ!ɹ }
  22. class StoryContainerViewController: UIViewController { let storyID: String init(storyID: String) {

    self.storyID = storyID } override func viewDidLoad() { client.showStory(ID: storyID) { result in switch result { case .Success(let story): let viewController = StoryDetailViewController(story: story) self.addChildViewController(viewController) self.view.addSubview(viewController.view) viewController.view.frame = view.bounds viewController.didMoveToParentViewController(self) case .Error(let error): // Show errorɹΤϥʔදࣔ } } } }
  23. url_scheme://stories/12345 StoryContainerViewController(storyID: "12345")

  24. url_scheme://stories/12345

  25. url_scheme://stories/12345 url_scheme://users/007

  26. url_scheme://stories/12345 url_scheme://users/007 url_scheme://messages/9876

  27. protocol RemoteContentProviding { typealias Content func fetchContent(completion: Result<Content, Error> ->

    Void) func viewControllerForContent(content: Result<Content, Error>) -> UIViewController }
  28. class RemoteContentContainerViewController<T: RemoteContentProviding>: UIViewController { let provider: T init(provider: T)

    { self.remoteContentProvider = remoteContentProvider super.init(nibName: nil, bundle: nil) } override func viewDidLoad() { super.viewDidLoad() provider.fetchContent { content in let viewController = self.provider.viewControllerForContent(content) self.addChildViewController(viewController) self.view.addSubview(viewController.view) viewController.view.frame = view.bounds viewController.didMoveToParentViewController(self) } } }
  29. struct StoryProvider: RemoteContentProviding { let ID: String func fetchContent(completion: Result<Story,

    Error> -> Void) { client.showStory(ID: ID, completion: completion) } func viewControllerForContent(content: Result<Story, Error>) -> UIViewController { switch content { case .Success(let story): return StoryDetailViewController(story: story) case .Error(_): return ErrorViewController(title: "Could not find story.") } } }
  30. url_scheme://stories/12345 let provider = StoryProvider(ID: "12345") RemoteContentContainerViewController(provider: provider)

  31. url_scheme://users/007 let provider = UserProvider(ID: "007") RemoteContentContainerViewController(provider: provider)

  32. url_scheme://messages/9876 let provider = MessageProvider(ID: "9876") RemoteContentContainerViewController(provider: provider)

  33. class StoryDetailViewController: UIViewController { let storyID: String private var titleView:

    StoryTitleView? private var senderView: AvatarView? private var recipientView: AvatarView? private var dateLabel: DateLabel? init(story: Story) { /* Same as before */ } init(storyID: String) { self.storyID = storyID titleView = nil senderView = nil recipientView = nil dataLabel = nil } // Load everything from API in viewDidLoad? // viewDidLoadͰAPI͔Βϩʔυʁ! }
  34. None
  35. class StoryDetailViewController: UIViewController { private let titleView: StoryTitleView private let

    senderView: AvatarView private let recipientView: AvatarView private let dateLabel: DateLabel init(story: Story) { titleView = StoryTitleView(story: story) senderView = AvatarView(user: story.sender) recipientView = AvatarView(user: story.recipient) dateLabel = DateLabel(date: story.date) } // ... }
  36. None
  37. ୈೋ INDEPENDENT CORES CONNECTIVE SHELL ಠཱͨ͠ਊͱܨ͛Δද໘

  38. None
  39. COORDINATORS SOROUSH KHANLOU @ NSSPAIN

  40. VIEW CONTROLLERS DON’T KNOW ABOUT OTHER VIEW CONTROLLERS

  41. @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? private

    lazy var applicationCoordinator: ApplicationCoordinator = { return ApplicationCoordinator(window: self.window!) }() func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { window = UIWindow(frame: UIScreen.mainScreen().bounds) applicationCoordinator.start() return true } }
  42. protocol Coordinator { func start() }

  43. class ApplicationCoordinator: Coordinator { let window: UIWindow let rootViewController =

    UITabBarController() let wordsNavigationController = UINavigationController() let phrasesNavigationController = UINavigationController() let wordsCoordinator: WordsCoordinator let phrasesCoordinator: PhrasesCoordinator init(window: UIWindow) { self.window = window let viewControllers = [wordsNavigationController, phrasesNavigationController] self.rootViewController.setViewControllers(viewControllers, animated: false) self.wordsCoordinator = WordsCoordinator(presenter: wordsNavigationController) self.phrasesCoordinator = PhrasesCoordinator(presenter: phrasesNavigationController) } func start() { window.rootViewController = rootViewController wordsCoordinator.start() phrasesCoordinator.start() window.makeKeyAndVisible() } }
  44. class WordsCoordinator: Coordinator { let presenter: UINavigationController private let listViewController:

    ListViewController<Word> private let dataSource: WordsDataSource init(presenter: UINavigationController) { self.presenter = presenter self.dataSource = WordsDataSource() self.listViewController = ListViewController<Word>() self.listViewController.title = "Words" self.listViewController.items = dataSource.words self.listViewController.configureCell = { cell, item in cell.item = item } self.listViewController.didSelectItem = { item in presenter.pushViewController(WordViewController(word: item), animated: true) } } func start() { presenter.pushViewController(listViewController, animated: false) } }
  45. class PhrasesCoordinator: Coordinator { let presenter: UINavigationController private let listViewController:

    ListViewController<Phrase> private let dataSource: PhrasesDataSource init(presenter: UINavigationController) { self.presenter = presenter self.dataSource = PhrasesDataSource() self.listViewController = ListViewController<Phrase>() self.listViewController.title = "Phrases" self.listViewController.items = dataSource.phrases self.listViewController.configureCell = { cell, item in cell.item = item } self.listViewController.didSelectItem = { item in presenter.pushViewController(PhraseViewController(phrase: item), animated: true) } } func start() { presenter.pushViewController(listViewController, animated: false) } }
  46. ApplicationCoordinator / \ WordsCoordinator PhrasesCoordinator

  47. ApplicationCoordinator / \ WordsCoordinator PhrasesCoordinator / SearchCoordinator

  48. ApplicationCoordinator / \ DictionaryCoordinator LoginCoordinator / \ WordsCoordinator PhrasesCoordinator /

    SearchCoordinator
  49. ApplicationCoordinator / \ \ DictionaryCoordinator LoginCoordinator SignUpCoordinator / \ WordsCoordinator

    PhrasesCoordinator / SearchCoordinator
  50. None
  51. None
  52. ந৅Խ ABSTRACTION

  53. ݎ࣮ SOLID

  54. ྲྀಈత FLUID

  55. ྲྀಈత 㲗 ݎ࣮ FLUID 㲗 SOLID

  56. কདྷੑ FUTURE PROOF

  57. THANK YOU ͋Γ͕ͱ͏ʂ

  58. None