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

iOSアプリの開発速度を170%に向上させたデバッグノウハウ / Debugging knowhow that improved our development velocity to 170%

Kuniwak
September 02, 2018

iOSアプリの開発速度を170%に向上させたデバッグノウハウ / Debugging knowhow that improved our development velocity to 170%

開発時間に占めるデバッグ時間の割合は少なくないため、この時間の短縮は開発速度を上げるためにとても重要です。この発表では、実際の中規模なアプリ開発で70%増もの開発速度向上を支えたデバッグノウハウを初心者にもわかりやすく紹介します。

https://fortee.jp/iosdc-japan-2018/proposal/7286f755-e980-4f6f-b268-2c56a224b727

Kuniwak

September 02, 2018
Tweet

More Decks by Kuniwak

Other Decks in Programming

Transcript

  1. J04 ΞϓϦͷ։ൃ଎౓Λ

    ʹ޲্ͤͨ͞

    σόοάϊ΢ϋ΢
    Kuniwak - DeNA Co.,Ltd.

    2018.09.02 iOSDC Japan 2018

    View Slide

  2. "CPVUNF

    View Slide

  3. ,VOJXBL
    w ॴଐˠ
    w HJUIVCDPN,VOJXBL
    w RJJUBDPN,VOJXBL
    w "QQ$PEF-PWFS

    View Slide

  4. ͜ͷൃදͰ։ൃ଎౓Λ

    ͋͛ΒΕΔਓ
    ͸͡Ίʹ

    View Slide

  5. ్த·Ͱ͏·͍͘͘ͷʹ

    కΊ੾Γʹؒʹ߹Θͳ͍

    View Slide

  6. కΊ੾Γ͕ഭͬͯ͘Δͱ

    ಈ࡞֬ೝΛ͖͠Εͳͯ͘

    େৎ෉ͩͱ৴͡Δ͔͠ͳ͍

    View Slide

  7. 4JNVMBUPSΆͪΆͪʹ

    ͕͔͔࣌ؒͬͯΠϥΠϥ͢Δ

    View Slide

  8. ͜Μͳ͓೰Έɺօ͞Μʹ΋͋Γ·ͤΜ͔ʁ
    ͜͏վળͨ͠Αʂͱ͍ͬͨମݧ͕͋Ε͹ɺ

    ͥͻUXFFU͍ͯͩ͘͠͞ʂ

    View Slide

  9. ͜ͷൃදͰ఻͍͑ͨ͜ͱ
    ͓೰Έͷ͋ͳͨ΁

    View Slide

  10. ΁ͷ։ൃ଎౓޲্Λࢧ͑ͨͭͷϊ΢ϋ΢ɿ
    --%#Λ࢖͍͜ͳ͢
    σόοάͷλΠϛϯάΛมߋ௚ޙʹ͢Δ
    ෆ҆ۦಈͰηϧϑνΣοΫ͢Δ
    ಈ࡞֬ೝΛࣗಈԽ͢Δ
    ؒҧ͍ʹ͍͘ઃܭʹ͢Δ





    View Slide

  11. ͜ͷൃදͷޙ൒͔Β͸ɺσόοάࣗମͷϊ΢ϋ΢ͷ

    આ໌ΑΓ΋ɺߴ౓ͳσόοάΛඞཁͱ͠ͳ͍ͨΊͷ

    ϊ΢ϋ΢ͷઆ໌͕ओʹͳ͍͖ͬͯ·͢
    ͜͏ͨ͠ҙਤ͸ɺσόοά͸ඇޮ཰తͳ࡞ۀͰ͋Γɺ

    σόοάͷස౓ΛԼ͛Δ͜ͱ͕ͦ͜σόοάʹର͢Δ

    ༗ޮͳྟΈํͩͱ͍͏ࢲͷ৴೦͔Β͖͍ͯ·͢
    ஫ҙॻ͖

    View Slide

  12. ϊ΢ϋ΢ͷ঺հ
    ։ൃ଎౓Λ޲্ͤ͞Δ

    View Slide

  13. --%#Λ࢖͍͜ͳ͢
    Φεεϝ౓ʜ
    ϊ΢ϋ΢

    View Slide

  14. σόοάͷ࣌ؒͰϦϏϧυʹ

    ͔͔͍ͬͯΔ࣌ؒ͸গͳ͘ͳ͍
    ϦϏϧυΛආ͚ΒΕΕ͹

    σόοά࣌ؒΛ୹ॖͰ͖Δ

    View Slide

  15. --%#ͱ͍͏σόοάπʔϧΛۦ࢖ͯ͠

    ࣍ͷͭͷํ๏ͰϦϏϧυΛආ͚ΒΕΔɿ
    ίʔϧελοΫΛḪΔ
    ϦϏϧυͳ͠Ͱ࠶࣮ߦ͢Δ
    ىಈͨ͠··ঢ়ଶΛมߋ͢Δ
    "
    #
    $

    View Slide

  16. ϊ΢ϋ΢ʵ" ίʔϧελοΫΛḪΔ
    CSFBLQPJOUΛ࢖͏໨తͷͭ͸ɺ͋Δ࣌఺ͷ

    ม਺ͳͲΛ֬ೝ͢Δ͜ͱ
    Կ͔͓͔͚͠Ε͹ɺ΋͏গ͠աڈʹḪͬͯ

    ม਺Λ֬ೝ͢Δ͜ͱʹͳΔ
    ͜ΕΛCSFBLQPJOUͷషΓ௚͠ͱ

    ϦϏϧυͰ΍Δͱ͕͔͔࣌ؒΔ

    View Slide

  17. ίʔϧελοΫʹ࢒͍ͬͯΔม਺ͳΒɺ

    ϦϏϧυ͠ͳ͍Ͱ΋֬ೝͰ͖Δ
    ࣮͸

    View Slide

  18. ίʔϧελοΫͱ͸ʜʁ

    View Slide

  19. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ᶃؔ਺Λ࣮ߦ
    ίʔϧελοΫ

    View Slide

  20. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    ؔ਺͕ݺͼग़͞ΕΔͱɺ

    ελοΫϑϨʔϜ͕࡞੒͞Εɺ

    ίʔϧελοΫʹੵ·ΕΔ

    View Slide

  21. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ᶄ಺෦Ͱؔ਺Λ࣮ߦ
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺

    View Slide

  22. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CBS"SH
    CBS7BS
    CBSؔ਺
    ݺͼग़͞Εͨؔ਺ͷதͰ

    ͞Βʹผͷؔ਺͕ݺ͹Εͯ΋

    ίʔϧελοΫʹੵ·ΕΔ

    View Slide

  23. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ᶅ͞Βʹ಺෦Ͱؔ਺Λ࣮ߦ
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CBS"SH
    CBS7BS
    CBSؔ਺

    View Slide

  24. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CBS"SH
    CBS7BS
    CBSؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CB["SH
    CB[7BS
    CB[ؔ਺
    ίʔϧελοΫʹੵ·ΕΔ

    View Slide

  25. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    } ᶆϒϨʔΫϙΠϯτͰࢭΊΔ
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CBS"SH
    CBS7BS
    CBSؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CB["SH
    CB[7BS
    CB[ؔ਺

    View Slide

  26. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CBS"SH
    CBS7BS
    CBSؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CB["SH
    CB[7BS
    CB[ؔ਺
    ࢭ·ͬͨ࣌ʹݟ͍͑ͯΔ΋ͷ

    View Slide

  27. func foo(_ fooArg: Int) {
    let fooVar = fooArg + 5
    bar(fooVar)
    }
    func bar(_ barArg: Int) {
    let barVar = barArg * 7
    baz(barVar)
    }
    func baz(_ bazArg: Int) {
    let bazVar = "\(bazArg)"
    print(bazVar)
    }
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CBS"SH
    CBS7BS
    CBSؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CB["SH
    CB[7BS
    CB[ؔ਺
    ࣮͸શ෦--%#͔ΒݟΒΕΔ

    View Slide

  28. 9DPEF "QQ$PEF
    ίʔϧελοΫͷḪΓํ
    ͕͜͜ίʔϧελοΫ

    View Slide

  29. 9DPEF "QQ$PEF
    ݟ͍ͨελοΫϑϨʔϜΛબ΂Δ
    ίʔϧελοΫͷḪΓํ

    View Slide

  30. 9DPEF "QQ$PEF
    ͦͷ࣌఺ͷม਺΍Ҿ਺ΛݟΒΕΔ
    ίʔϧελοΫͷḪΓํ

    View Slide

  31. ϊ΢ϋ΢ʵ# ϦϏϧυͳ͠Ͱ࠶࣮ߦ͢Δ
    ίʔϧελοΫΛḪΔํ๏Ͱ͸ɺ͢Ͱʹ

    ࣮ߦ͕ऴΘͬͯ͠·ͬͨؔ਺ͷม਺ΛݟΒΕͳ͍
    ͦͷ৔߹͸࠶࣮ߦ͠ͳ͚Ε͹ͳΒͳ͍͕ɺίʔυʹ

    มߋΛՃ͍͑ͯͳ͚Ε͹ϦϏϧυ͠ͳ͍Ͱ

    ࠶࣮ߦͨ͠΄͏͕࣌ؒΛઅ໿Ͱ͖Δ

    View Slide

  32. 9DPEF
    ϦϏϧυͳ͠ͷ࠶࣮ߦͷ΍Γํ
    "QQ$PEF
    ᶃϏϧυઃఆฤू
    ᶄ࠶࣮ߦ͍ͨ͠

    ઃఆΛίϐʔ
    ᶅ#VJMEΛ࡟আ
    ᶆ࡞੒ͨ͠ઃఆΛ࣮ߦ
    ΞϓϦΛ࠶ىಈͯ͠

    "UUBDIUP1SPDFTTͰ΋

    ࣅͨΑ͏ͳ͜ͱΛͰ͖·͢
    ผͷํ๏

    View Slide

  33. ϊ΢ϋ΢ʵ$ ىಈͨ͠··ঢ়ଶΛมߋ͢Δ
    ม਺ͷ಺༰Λॻ͖׵͑Δ͚ͩͳΒɺ

    ϦϏϧυͳ͠Ͱ΋Մೳ
    ίʔυม਺ͷ಺༰ΛҰ࣌తʹॻ͖׵͑ͯ

    ಈ࡞Λ͔֬Ί͍ͨ৔߹ʹศར
    σβΠφʔͱͷ࣮ػ֬ೝͳͲͰ

    Α͘ॏๅͨ͠ςΫχοΫ

    View Slide

  34. มߋΛ࢝ΊΔͨΊͷҰ࣌ఀࢭͷ΍Γํ
    9DPEF "QQ$PEF

    View Slide

  35. --%#ͷೖྗΠϯλʔϑΣʔεΛ֬ೝ
    9DPEF "QQ$PEF
    --%#ͷίϚϯυϥΠϯΠϯλʔϑΣʔε͕ىಈ͢Δ

    View Slide

  36. (lldb) help
    Debugger commands:
    apropos -- List debugger commands related to a
    breakpoint -- Commands for operating on breakpoint
    shorthand.)
    ...
    IFMQΛ࣮ߦ͢Δͱɺ࣮ߦͰ͖Δૢ࡞ͷҰཡ͕දࣔ͞ΕΔ

    View Slide

  37. (lldb) help po

    Evaluate an expression on the current

    thread. Displays any returned value

    with formatting controlled by the type's

    author. Expects 'raw' input (see 'help

    raw-input'.)
    ࠓճ͸--%#ܦ༝ͰίʔυΛ࣮ߦ͢ΔQPΛ࢖͏
    ҙ༁: ݱࡏͷεϨουͰίʔυΛධՁ͠ɺ݁Ռͷ

    ஋ΛਓؒʹಡΈ΍͍͢ܗͰදࣔ͠·͢ɻ

    View Slide

  38. (lldb) po print("Hello, World")
    error: use of undeclared identifier 'print'
    QPίϚϯυͰ)FMMP 8PSMEΛදࣔͯ͠ΈΑ͏
    "QQ$PEFͷਓ͸͜ͷΤϥʔ͕ग़ͳ͍͔΋

    View Slide

  39. ݱࡏͷελοΫϑϨʔϜΛ֬ೝ
    9DPEF "QQ$PEF
    4XJGUͷελοΫϑϨʔϜ΁Ҡಈ
    ࠷ॳ͔Β4XJGUͷελοΫϑϨʔϜΛ

    બ୒ͯ͘͠Ε͍ͯΔ͜ͱ͕ଟ͍

    View Slide

  40. (lldb) po print("Hello, World")
    Hello, World
    ΋͏Ұ౓QPίϚϯυͰ)FMMP 8PSMEΛදࣔͯ͠ΈΑ͏
    ͏·࣮͘ߦͰ͖ͨ

    View Slide

  41. QPίϚϯυΛ࢖͏ͱɺىಈதͷΞϓϦͷ

    ద౰ͳ6*7JFXͷঢ়ଶΛม͑ΒΕΔ
    ͜ͷදࣔจࣈྻΛม͑ͯΈΑ͏

    View Slide

  42. (lldb) po UIApplication.shared.keyWindow!

    .value(forKey: "recursiveDescription")!
    | | | | | | | | | | | | | | | | | <_UILabelContentLayer: 0x6040006336e0> (layer)
    ·ͣɺදࣔ͞Ε͍ͯΔ6*7JFXͷҰཡΛදࣔ

    View Slide

  43. (lldb) po UIApplication.shared.keyWindow!

    .value(forKey: "recursiveDescription")!
    | | | | | | | | | | | | | | | | | <_UILabelContentLayer: 0x6040006336e0> (layer)
    දࣔ͞Ε͍ͯΔΫϥεͷܕ΍ϝϞϦΞυϨεΛೖखͰ͖Δ

    View Slide

  44. (lldb) po let $label = unsafeBitCast(0x7fbee0610fe0,
    to: UILabel.self)
    ܕͱϝϞϦΞυϨε͕Θ͔ΔͱɺunsafeBitCastͰ

    ΠϯελϯεΛऔಘͰ͖Δ

    View Slide

  45. (lldb) po let $label = unsafeBitCast(0x7fbee0610fe0,
    to: UILabel.self)
    ม਺΍ؔ਺ͳͲͷએݴΛ͢Δ৔߹͸ɺ໊લΛ͔Β࢝Ίͳ͍ͱແࢹ͞ΕΔͷͰ஫ҙ

    View Slide

  46. (lldb) po $label
    ΠϯελϯεΛऔಘͰ͖͔ͨͲ͏͔֬ೝͯ͠ΈΑ͏

    View Slide

  47. (lldb) po $label.text = "Hello, World!"
    $label ͷςΩετΛॻ͖׵͑ͯΈΑ͏

    View Slide

  48. ΞϓϦͷ࣮ߦΛ࠶։͢Δ΍Γํ
    9DPEF "QQ$PEF

    View Slide

  49. UILabelͷද͕ࣔมΘͬͨʂ

    View Slide

  50. 5*14 ελοΫϑϨʔϜΛม͑ΔҎ֎ͷํ๏
    ઌ΄Ͳ͸ελοΫϑϨʔϜΛ4XJGUͷ΋ͷʹ

    ߹Θͤͳ͍ͱΤϥʔʹͳ͍ͬͯͨ
    ࣮͸ɺԼͷΑ͏ʹ࣮ߦ͢ΔͱɺελοΫϑϨʔϜΛ

    ม͑ͳͯ͘΋Τϥʔ͸ൃੜ͠ͳ͍
    ҙຯ͸help expr Ͱௐ΂ͯΈΑ͏
    (lldb) expr -l swift -o -- print("Hello, World")


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  51. ݟͨ໨ͷมߋΛ΋ͬͱ؆୯ʹ
    3FWFBMͱ͍͏ΞϓϦʢ༗ྉʣ͕ͱͯ΋ศར

    'SFF5SJBM͕͋ΔͷͰɺࢼͯ͠ΈΔͱ͍͍͔΋ʁ
    ྫ͑͹6*-BCFMͷ

    ςΩετΛม͑ΒΕΔ
    ಛʹ"VUP-BZPVUͷ

    σόοάʹॏๅ͢Δ
    https://revealapp.com
    5*14


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  52. ϊ΢ϋ΢ͷ·ͱΊ
    σόοΨΛ࢖͏ͱͭͷํ๏Ͱ

    ϦϏϧυΛආ͚ΒΕΔɿ
    ίʔϧελοΫΛḪΔ
    ϦϏϧυͳ͠Ͱ࠶࣮ߦ͢Δ
    ىಈͨ͠··ঢ়ଶΛมߋ͢Δ
    "
    #
    $

    View Slide

  53. σόοάͷλΠϛϯάΛ

    มߋ௚ޙʹ͢Δ
    ϊ΢ϋ΢
    Φεεϝ౓ʜ

    View Slide

  54. ݪҼͷજΉൣғΛߜΓࠐΉͷ΋

    σόοά࣌୹ͷ༗ޮͳςΫχοΫ

    View Slide

  55. GVODDMBTTTUSVDUFOVNͱ͍ͬͨ

    খ͞ͳ୯Ґͷ࣮૷௚ޙʹσόοάΛ

    ͢Ε͹ɺݪҼ͸௚લͷൣғʹߜΒΕΔ
    ۩ମతʹ͸

    View Slide

  56. ॏཁͳͷ͸ɺมߋͱ֬ೝͷαΠΫϧΛ

    ͳΔ΂͘୹ִ͍ؒͰճͤΔΑ͏ʹอͭ͜ͱ
    ίʔυͷ

    มߋ
    ಈ࡞֬ೝ

    View Slide

  57. struct Example {
    static func intToString(_ i: Int?) -> String {
    let x: Int! = i
    return "\(x)"
    }
    }
    ྫ͑͹ɺ੔਺Λਐ਺දهͷจࣈྻ΁

    ม׵͢ΔίʔυΛ࣮૷ͨ͠ͱ͢Δ
    ྫ͑͹ɺΛҾ਺ʹ͢Ε͹ɺਐ਺

    จࣈྻͰ͋Δ͕ฦ͖ͬͯͯ΄͍͠

    View Slide

  58. ผͷίʔυͰ࢖͏લʹ--%#Ͱಈ࡞Λ֬ೝ͠Α͏
    ʢ--%#͸ΞϓϦͷҰ࣌ఀࢭͰىಈͰ͖Δʣ
    (lldb) po Example.intToString(42)

    View Slide

  59. (lldb) po Example.intToString(42)
    "Optional(42)"

    View Slide

  60. struct Example {
    static func intToString(_ i: Int?) -> String {
    let x: Int! = i
    return "\(x)"
    }
    }
    όάΛݟ͚ͭͨΒ௚લͷ࣮૷Օॴʹ໭Ζ͏

    View Slide

  61. struct Example {
    static func intToString(_ i: Int?) -> String {
    let x: Int = i!
    return "\(x)"
    }
    }
    Int!ΛจࣈྻԽ͢Δͱ"Optional(...)"ʹ

    ͳͬͯ͠·͏ͷͰɺIntʹͳΔΑ͏मਖ਼͢Δ

    View Slide

  62. struct Example {
    static func intToString(_ i: Int?) -> String {
    let x: Int! = i
    return "\(x)"
    }
    }
    ࣮૷௚ޙʹσόοάͨ͠ͷͰɺ

    ݪҼՕॴ͕͙ۙ͘͢ʹݟ͔ͭͬͨ

    View Slide

  63. मਖ਼ͨ͠Βɺ·͙ͨ͢ʹಈ࡞֬ೝ͠Α͏
    (lldb) po Example.intToString(42)
    "42"
    ࠓ౓͸େৎ෉ͦ͏ͩ

    View Slide

  64. 5*14 --%#ͰCSFBLQPJOUΛޮ͔ͤΔํ๏
    --%#ͷpo΍exprʹΑΔ࣮ߦͰCSFBLQPJOU͕

    ޮ͔ͳͯ͘ࠔΔ͜ͱ͕͋Δ
    ͦ͜Ͱɺexprͷ-iΦϓγϣϯΛfalseʹ͢Δͱɺ

    --%#͔Βͷ࣮ߦͰ΋CSFBLQPJOU͕ޮ͘Α͏ʹͳΔ
    (lldb) expr -l swift -o -i false -- Example.intToString(42)


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  65. ϊ΢ϋ΢ͷ·ͱΊ
    ͳΔ΂͘୹͘ૉૣ͘ճ͢
    ίʔυͷ

    มߋ
    ಈ࡞֬ೝ

    View Slide

  66. ෆ҆ۦಈͰηϧϑνΣοΫ͢Δ
    ϊ΢ϋ΢
    Φεεϝ౓ʜ

    View Slide

  67. Α͋͘Δ։ൃ଎౓௿ԼͷݪҼ͸ख໭Γ
    ࣗ෼ͷಈ࡞֬ೝ͕ෆ׬શͩͬͨΓɺ

    ࢥ͍΋͔ͭͳ͍όά͕͋ͬͨΓ͢Δͱ

    σόοά·Ͱख໭Γͯ͠͠·͏

    View Slide

  68. ͕࣌ؒܦͭ΄ͲݪҼ͕Θ͔Βͳ͘ͳΔ
    ༨ܭͳίϛϡχέʔγϣϯ͕૿͑Δ
    కΊ੾Γ௒աͷ࠷ޙͷҰԡ͠͸͍͍ͩͨख໭Γ
    ख໭ΓʹΑͬͯੜ͡Δ໰୊

    View Slide

  69. ͕࣌ؒܦͭ΄ͲݪҼ͕Θ͔Βͳ͘ͳΔ
    ͜ΕมͳͷͰݟͯ΋Β͑·͢ʁ
    ઌि࣮૷ͨ͠΍͚ͭͩͲ

    ͲΜͳײ͡ʹ͚ͨͬ͠ʜʁ
    ಈ࡞֬ೝ୲౰
    ࣮૷ऀ

    View Slide

  70. ༨ܭͳίϛϡχέʔγϣϯ͕૿͑Δ
    ͜Εόάͬͯ·ͤΜ͔ʁ
    खॱΛڭ͑ͯ΋Β͑·͔͢ʁ
    ͑ʔͱɺ"Λ#͔ͯ͠Βʜ
    ಈ࡞֬ೝ୲౰
    ࣮૷ऀ

    View Slide

  71. కΊ੾Γ௒աͷ࠷ޙͷҰԡ͠͸͍͍ͩͨख໭Γ
    ࣮૷
    ࣮૷
    ࣮૷
    ϨϏϡʔ
    ϨϏϡʔ
    ϨϏϡʔ
    ಈ࡞֬ೝ
    ख໭ΓରԠ
    ಈ࡞֬ೝ
    ਓ਺Ͱεέʔϧ͠ͳ͍
    $͞Μ
    ,VOJXBL
    "͞Μ
    #͞Μ

    View Slide

  72. ͳΔ΂ࣗ͘෼ͰόάΛݟ͚ͭͯɺ

    ख໭ΓΛݮΒ͍ͯ͘͜͠ͱ͕ॏཁ
    ଟগ͕͔͔࣌ؒͬͨͱͯ͠΋ɺ

    ͋ͱͰݟ͔ͭΔΑ͏ͳόάͰ

    ख໭Γ͠ͳ͚Ε͹શମతͳ

    ࣌ؒ͸Ή͠ΖݮΔ
    ͭ·Γ

    View Slide

  73. Ͳ͜·ͰΛηϧϑνΣοΫ͢Δ͔
    ͱ͸͍͑

    View Slide

  74. ͜ͷ··ಥ͖ਐΉͱɺ͢΂ͯͷόάΛ

    શ෦ࣗ෼Ͱݟ͚ͭΔ͜ͱʹͳͬͯ

    ͠·Θͳ͍ͩΖ͏͔ʁ
    ͦΕ͸ਖ਼͍͠ํ਑ͳͷͩΖ͏͔ʁ

    View Slide

  75. ࣗ෼ͰશͯΛݟ͚ͭΔ
    ख໭Γ࠷খ

    View Slide

  76. ಈ࡞֬ೝ୲౰ͷਓͷ࣌ؒΛ

    ༗ޮ׆༻Ͱ͖ͳ͍
    ख໭Γ࠷খ ෼ۀ࠷খ

    View Slide

  77. ࣮૷͚ͩʹઐ೦͢Δ
    ख໭Γ࠷খ ෼ۀ࠷খ

    ෼ۀ࠷େ

    View Slide

  78. ։ൃ͕࣌ؒ৳ͼΔ
    ख໭Γ࠷খ ෼ۀ࠷খ

    ෼ۀ࠷େ ख໭Γ࠷େ

    View Slide

  79. ख໭Γ࠷খ ෼ۀ࠷খ

    ෼ۀ࠷େ ख໭Γ࠷େ

    ͲͪΒ΋ۃ୺ʹ͢Ε͹͕࣌ؒ૿͑ͯ͠·͏

    View Slide

  80. Ͳ͔͜ͷଥڠ఺Λ

    બ͹ͳ͍ͱ͍͚ͳ͍
    ͱ͍͏͜ͱ͸

    View Slide

  81. ීวతʹ࠷దͳ

    ଥڠ఺͸ଘࡏ͠ͳ͍
    ͔͠͠

    View Slide

  82. ͢΂ͯͷෆ҆ͳߦΛҰ౓͸

    ௨ա͢Δఔ౓Λ໨҆ʹͨ͠
    ͦ͜ͰɺࢲͷࣄྫͰ͸

    View Slide

  83. ෆ҆ͳίʔυͷྫ
    struct Example {
    static func intToString(_ i: Int?) -> String {
    let x: Int! = i
    return "\(x)"
    }
    }
    ͨͱ͑͹ɺઌ΄Ͳͷ୹͍ίʔυͷதʹ΋

    ෆ҆ͳ৔ॴ͕͋ͬͨ
    ࢲͷෆ҆ϙΠϯτ

    View Slide

  84. "\(x)"͸ͱ͖Ͳ͖৴༻ͷͳΒͳ͍ڍಈΛ͢Δ
    Int?ͩͱμϝʹͳΔͷ͸༗໊ͳͷͰ

    ஌͍͕ͬͯͨɺInt!Ͱ΋ಉ͡Α͏ʹ

    μϝͳͷ͔Ͳ͏͔֬৴Λ΋ͯͳ͔ͬͨ
    ͦ͏͍͏࣌͸ɺ--%#ͳͲͰ

    ࣮ࡍʹಈ͔͔ͯ֬͠ΊΑ͏
    --%#Ҏ֎ͷํ๏͸ϊ΢ϋ΢Ͱ΋঺հ

    View Slide

  85. w සൟʹ௨ա͢Δ෼ذʢ͜͜ʹόά͕͋Δͱਃ͠༁ͳ͍ʣ
    if (x < 0)ͷ෼ذͰxʹͲΜͳ΋ͷ͕དྷΔ͔

    Θ͔Βͳ͍ͳΒɺগͳ͘ͱ΋ਖ਼ͱෛͷ྆ํΛࢼ͢
    w ෼ذ৚݅ͷڥ໨
    x < 0ͳΒڥ໨͸ɺ഑ྻ͸ۭͷͱ͖ʹڥ໨ʹͳΓ΍͍͢

    DoubleͳΒ+0 -0 +infinity -infinity nanͳͲ
    w ཧղͰ͖͍ͯͳ͍ίϯϙʔωϯτͷར༻
    ྫ͑͹ɺඪ४ϥΠϒϥϦͷ6*5BCMF7JFX͸࢖͍ํ͕

    ͱͯ΋ෳࡶͳͷͰ͍ͭ΋ؒҧ͑ͯ͠·͏
    ࢲͷ࠷ۙͷෆ҆ϙΠϯτ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  86. ։ൃऀͷෆ҆͸ɺաڈʹࣗ਎͕౿Μͩ

    όάͷܦݧΛ൓ө͍ͯ͠Δ͔Β
    ͔͠͠ɺͳͥෆ҆ͱ͍͏ج४ͳͷ͔ʁ

    View Slide

  87. ෆ҆ΛΞοϓσʔτ͠ଓ͚Δ͜ͱͰ

    ͦͷݱ৔ʹదͨ͠ଥڠ఺΁͍͍͚ۙͮͯΔ
    ෆ͕࣮҆ࡍͷόάͷ܏޲ʹ

    ݁ͼͭ͘Α͏ʹײ֮Λ

    ߋ৽͠ଓ͚Α͏

    View Slide

  88. ϊ΢ϋ΢ͷ·ͱΊ
    ෆ͕҆ͳ͘ͳΔ·Ͱ

    ηϧϑνΣοΫ͠Α͏
    ෆ҆ͷਫ਼౓Λ্͍͛ͯ͜͏

    View Slide

  89. ಈ࡞֬ೝΛࣗಈԽ͢Δ
    ϊ΢ϋ΢
    Φεεϝ౓ʜ

    View Slide

  90. ϊ΢ϋ΢Λଥڠͤͣʹ

    ࣮ફ͢Δͷ͸͔ͳΓ೉͍͠
    ͳͥͳΒɺෆ҆Λײ͡ΔՕॴ͸

    ࢥͬͨΑΓଟ͍͔Β
    ͢Δͱ్தͰଥڠ͕ͪ͠ʹͳΓɺ

    ख໭Γ͕ࢥͬͨΑΓ

    ݮΒͳ͘ͳΔ

    View Slide

  91. Ͳ͜·Ͱ֬ೝ͢Ε͹҆৺Ͱ͖ΔͩΖ͏ʁ
    ཧ૝తͳൣғΛ૝૾ͯ͠ΈΑ͏

    View Slide

  92. ࢲʹͱͬͯͷཧ૝తͳಈ࡞֬ೝ
    Өڹ͞ΕΔίϯϙʔωϯτ

    ͢΂ͯͬͯແཧͰ͸ʜʁ
    ͜Ε͸Ͱ͖ͦ͏
    w ͦΕҎ֎ʹӨڹ͢Δίϯϙʔωϯτ΋

    ͢΂ͯ֬ೝ͠ͳ͍ͱຊདྷ͸·͍ͣ
    w ࣮૷मਖ਼ͨ͠ίϯϙʔωϯτΛ

    ֬৴Ͱ͖ΔϨϕϧ·Ͱ֬ೝ

    View Slide

  93. w ͕͔͔࣌ؒΓ͗͢Δ
    w Өڹ͢Δίϯϙʔωϯτͷಈ࡞֬ೝͷ

    ࢓ํΛ͍֮͑ͯΔ͸͕ͣͳ͍
    Өڹ͢ΔίϯϙʔωϯτΛ͢΂ͯ֬ೝ͢Δ

    ͱ͖ͷΑ͋͘Δ໰୊఺ɿ
    ͋Εʁ͜ͷڍಈͰ͋ͬͯΔΜ͚ͩͬʜʁ
    ਖ਼͍͠Ҿ਺ͬͯͲ͏΍ͬͯ࡞ΔΜ͚ͩͬʁ

    View Slide

  94. ಈ࡞֬ೝΛࣗಈԽ͠Α͏
    ͦ͏͍͏ͱ͖͸

    View Slide

  95. ίʔυͷಈ࡞֬ೝΛࣗಈԽ͢Δར఺ɿ
    w ܁Γฦ࣮͠ߦ͢Δίετ͕௿͍ͷͰ

    ࣌ؒ͸ͦ͜·Ͱ͔͔Βͳ͘ͳΔ
    w ૢ࡞֬ೝͷखॱ͕ίʔυͱͯ͠

    ࢒͞ΕΔͷͰ΍ΓํΛ๨Εͳ͍

    View Slide

  96. ಈ࡞֬ೝΛࣗಈԽ͢Δͭͷํ๏ɿ
    ಈ࡞֬ೝͷͨΊͷඪ४ϥΠϒϥϦΛ

    ࢖ͬͯಈ࡞֬ೝ
    "QQ%FMFHBUFܦ༝Ͱಈ࡞֬ೝ
    "
    #

    View Slide

  97. ϊ΢ϋ΢ʵ" "QQ%FMFHBUFͰಈ࡞֬ೝ
    ಈ࡞֬ೝͷίʔυΛॻ͘
    ৽͍͠4DIFNFΛ࡞੒͢Δ
    "QQ%FMFHBUFͰͷ

    4DIFNFͷͱ͖͚ͩΛ

    ࣮ߦ͢ΔΑ͏ʹ͢Δ
    ͨͩܽ͠఺͕ଟ͍ͷͰɺൃදͰ͸ׂѪ͠·͢

    View Slide

  98. #if DEBUG
    import Foundation
    let debugTargets: [String: () -> Void] = [
    "intToString": {
    let string = Example.intToString(42)
    guard string == "48" else {
    fatalError("intToString(42) ͕ \"42\" Λฦ͞ͳ͔ͬͨ")
    }
    },
    ]
    func debugAll() {
    let targets = Array(debugTargets.values)
    DispatchQueue.concurrentPerform(iterations: targets.count) { index in
    targets[index]()
    }
    }
    #endif
    ಈ࡞֬ೝͷίʔυΛ·ͱΊΔ%JDUJPOBSZΛ࡞੒͢Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  99. #if DEBUG
    import Foundation
    let debugTargets: [String: () -> Void] = [
    "intToString": {
    let string = Example.intToString(42)
    guard string == "42" else {
    fatalError("intToString(42) ͕ \"42\" Λฦ͞ͳ͔ͬͨ")
    }
    },
    ]
    func debugAll() {
    let targets = Array(debugTargets.values)
    DispatchQueue.concurrentPerform(iterations: targets.count) { index in
    targets[index]()
    }
    }
    #endif
    ಈ࡞֬ೝͷίʔυΛॻ͘
    ͜ͷίʔυ͸ɺintToString(42)ͷ

    ݁Ռ͕"42"ʹͳΔ͜ͱΛ֬ೝ͍ͯ͠Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  100. #if DEBUG
    import Foundation
    let debugTargets: [String: () -> Void] = [
    "intToString": {
    let string = Example.intToString(42)
    guard string == "48" else {
    fatalError("intToString(42) ͕ \"42\" Λฦ͞ͳ͔ͬͨ")
    }
    },
    ]
    func debugAll() {
    let targets = Array(debugTargets.values)
    DispatchQueue.concurrentPerform(iterations: targets.count) { index in
    targets[index]()
    }
    }
    #endif
    ͢΂ͯͷಈ࡞֬ೝίʔυΛ࣮ߦ͢Δ

    EFCVH"MMؔ਺Λ࣮૷͢Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  101. ৽͍͠4DIFNFΛ࡞੒͢Δ΍Γํ
    9DPEF
    ᶃ4DIFNFΛ࡞੒
    ᶄద౰ͳ໊લΛ͚ͭΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  102. ৽͍͠4DIFNFΛ࡞੒͢Δ΍Γํ
    ᶃϏϧυઃఆΛ։͘
    ᶆద౰ͳ໊લΛ͚ͭΔ
    "QQ$PEF
    ᶅ"QQMJDBUJPOΛࢦఆ
    ᶄ4DIFNFΛ࡞੒


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  103. ؀ڥม਺Λ௥Ճ͢Δ΍Γํ
    9DPEF
    ᶃ4DIFNFΛฤू
    ᶄ؀ڥม਺Λ௥Ճ
    "QQ$PEF
    ᶃ؀ڥม਺Λฤू
    ᶄ؀ڥม਺Λ௥Ճ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  104. ؀ڥઃఆͷ஋Λઃఆ͢Δ΍Γํ
    9DPEF
    ᶃAUTO_DEBUG=1Λઃఆ
    "QQ$PEF
    ᶃAUTO_DEBUG=1Λઃఆ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  105. @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
    #if DEBUG
    if ProcessInfo.processInfo.environment["AUTO_DEBUG"] == "1" {
    debugAll()
    return true
    }
    #endif
    // ...
    return true
    }
    }
    "QQ%FMFHBUFʹ؀ڥม਺ͷ෼ذΛ௥Ճ
    AUTO_DEBUG=1ͷͱ͖ͷΈɺ

    ઌ΄ͲͷdebugAllΛ࣮ߦ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  106. ΞϓϦΛ࣮ߦͯ͠ΈΑ͏
    ͦΕͰ͸


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  107. ΞϓϦΛ࣮ߦ͢Δͱಈ࡞֬ೝͰ͖Δ
    9DPEF
    Ͳ͜Ͱࣦഊ͔ͨ͠Θ͔Δ
    ࣦഊͨ͠ཧ༝͕Θ͔Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  108. ΞϓϦΛ࣮ߦ͢Δͱಈ࡞֬ೝͰ͖Δ
    "QQ$PEF
    Ͳ͜Ͱࣦഊ͔ͨ͠Θ͔Δ
    ࣦഊͨ͠ཧ༝͕Θ͔Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  109. 5*14 ࢦఆͨ͠ίʔυ͚ͩಈ࡞֬ೝ
    ݸผͷಈ࡞֬ೝ͚ͩΛ࣮ߦ͍ͨ͜͠ͱ͸Α͋͘Δ
    ͦ͏͍͏৔߹͸ɺ--%#͔Β໊લΛࢦఆͯ͠ݸผʹ

    ಈ࡞֬ೝΛ࣮ߦͰ͖Δ
    ಈ࡞֬ೝͷίʔυΛ഑ྻͰ͸ͳ͘%JDUJPOBSZʹ

    ֨ೲͨ͠ͷ͸͜Ε͕໨త
    (lldb) po debugTargets["intToString"]!()
    Fatal error: intToString(42) ͕ "42" Λฦ͞ͳ͔ͬͨ:


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  110. "QQ%FMFHBUFͷํ๏ͷ໰୊఺ɿ
    w ෳ਺ͷಈ࡞֬ೝ͕ࣦഊ͢Δͱɺઌʹ

    ࣮ߦ͞Εͨํ͔͠೺ѲͰ͖ͳ͍
    w fatalErrorΛprint΁ม͑Ε͹

    ෳ਺ͷࣦഊΛ೺ѲͰ͖ΔΑ͏ʹ

    ͳΔ͕ɺࠓ౓͸ࣦഊͨ͠ࡍʹ*%&͕

    δϟϯϓͯ͘͠Εͳ͍ͷͰෆศ
    ͜ΕΒͷ໰୊͸ඪ४ϥΠϒϥϦͰղܾͰ͖·͢

    View Slide

  111. ϊ΢ϋ΢ʵ# ඪ४ϥΠϒϥϦͰಈ࡞֬ೝ
    ৽͍͠5BSHFUΛ࡞੒͢Δ
    ಈ࡞֬ೝͷίʔυΛॻ͘
    ಈ࡞֬ೝͷίʔυͷ5BSHFUΛ

    ઌ΄Ͳ࡞੒ͨ͠΋ͷʹ͢Δ
    J04ʹ͸ಈ࡞֬ೝ༻ͷඪ४ϥΠϒϥϦʮ9$5FTUʯ͕

    ༻ҙ͞Ε͍ͯΔ
    ͜ΕͰ"QQ%FMFHBUFʹ͋ͬͨෳ਺ࣦഊͨ͠ͱ͖ͷ

    ໰୊ΛղܾͰ͖Δ͕ɺ࣍ͷΑ͏ͳϏϧυઃఆ͕ඞཁɿ

    View Slide

  112. Ϗϧυઃఆͷ΍Γํ
    9DPEF
    ᶃ5BSHFUΛ৽ن࡞੒
    ᶅඞཁ߲໨Λهೖ
    ᶄJ046OJU5FTUJOH#VOEMFΛબ୒


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  113. ᶃ1SPKFDU4FUUJOHTΛ։͘
    ᶄ5BSHFUΛ௥Ճ
    ᶅJ046OJU5FTUJOH#VOEMFΛબ୒
    Ϗϧυઃఆͷ΍Γํ
    "QQ$PEF


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  114. Ϗϧυઃఆͷ΍Γํ
    9DPEF
    ᶆ5FTUJOHΛ࣮ߦ
    ᶇ5FTU4VDDFFEFE͕දࣔ͞ΕΕ͹0,


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  115. ᶇϏϧυઃఆΛฤू
    ᶈ4DIFNFΛ௥Ճ
    ᶆඞཁ߲໨Λهೖ
    Ϗϧυઃఆͷ΍Γํ
    "QQ$PEF


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  116. ᶌ"MM5FTUT1BTTFEͱදࣔ͞ΕΕ͹0,
    ᶉ9$5FTUΛબ୒
    ᶊద౰ͳ໊લΛ͚ͭΔ
    ᶋ࡞੒ͨ͠4DIFNFΛ࣮ߦ
    Ϗϧυઃఆͷ΍Γํ
    "QQ$PEF


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  117. ͜ΕͰಈ࡞֬ೝ༻ͷ5BSHFUΛ࡞੒Ͱ͖ͨ

    View Slide

  118. import XCTest
    @testable import IOSDC2018Debugging
    class ExampleTest: XCTestCase {
    func testIntToString() {
    let str = Example.intToString(42)
    XCTAssertEqual(str, "42")
    }
    }
    ඪ४ϥΠϒϥϦΛ࢖͏৔߹ͷ

    ಈ࡞֬ೝͷίʔυ͸͜͏ͳΔ

    View Slide

  119. import XCTest
    @testable import IOSDC2018Debugging
    class ExampleTest: XCTestCase {
    func testIntToString() {
    let str = Example.intToString(42)
    XCTAssertEqual(str, "42")
    }
    }
    ಈ࡞֬ೝͷίʔυͷUBSHFU͸

    ઌ΄Ͳ࡞੒ͨ͠UBSHFUʹ͢Δ

    View Slide

  120. 9DPEF
    ᶃ࡞੒ͨ͠5BSHFUΛબ୒
    "QQ$PEF


    Ͱ
    ͸
    ׂ
    Ѫ
    ᶃ.BOBHF5BSHFUTΛબ୒
    ᶄ࡞੒ͨ͠5BSHFUΛબ୒
    5BSHFUͷࢦఆͷ΍Γํ

    View Slide

  121. import XCTest
    @testable import IOSDC2018Debugging
    class ExampleTest: XCTestCase {
    func testIntToString() {
    let str = Example.intToString(42)
    XCTAssertEqual(str, "42")
    }
    }
    ඪ४ϥΠϒϥϦ9$5FTUΛಡΈࠐΉ
    ΞϓϦͷίʔυ͸ผͷ5BSHFUͳͷͰ

    ࢖͏ࡍʹ͸import͕ඞཁ
    ී௨ʹimport͢Δͱpublicͷ΋ͷ͔͠

    ݟ͑ͳ͘ͳͬͯ͠·͏͕ɺ@testableΛͭ
    ͚Δͱinternal·Ͱݟ͑ΔΑ͏ʹͳΔ

    View Slide

  122. import XCTest
    @testable import IOSDC2018Debugging
    class ExampleTest: XCTestCase {
    func testIntToString() {
    let str = Example.intToString(42)
    XCTAssertEqual(str, "42")
    }
    }
    ಈ࡞֬ೝͷίʔυ͸XCTestCaseͱ͍͏

    ΫϥεͰάϧʔϐϯάͰ͖ΔΑ͏ʹͳ͍ͬͯΔ
    ಈ࡞֬ೝͷίʔυΛॻ͘৔߹ʹ͸ɺ֬ೝର৅ͷ

    Ϋϥε΍ؔ਺୯ҐͰXCTestCaseΛ෼͚Δͱ

    ಈ࡞֬ೝͷ݁Ռ͕Θ͔Γ΍͘͢ͳΔ

    View Slide

  123. import XCTest
    @testable import IOSDC2018Debugging
    class ExampleTest: XCTestCase {
    func testIntToString() {
    let str = Example.intToString(42)
    XCTAssertEqual(str, "42")
    }
    }
    ࣮ࡍͷಈ࡞֬ೝͷίʔυ͸ɺΠϯελϯεϝιουͱ࣮ͯ͠૷͢Δ
    ͜ͷϝιουͷ໊લ͸ઌ಄ʹtestͱ͚ͭͳ͍ͱ࣮ߦ͞Εͳ͍ͷͰ஫ҙ

    View Slide

  124. import XCTest
    @testable import IOSDC2018Debugging
    class ExampleTest: XCTestCase {
    func testIntToString() {
    let str = Example.intToString(42)
    XCTAssertEqual(str, "42")
    }
    }
    ಈ࡞֬ೝͷର৅Λ࣮ࡍʹ࣮ߦ͢Δ

    View Slide

  125. import XCTest
    @testable import IOSDC2018Debugging
    class ExampleTest: XCTestCase {
    func testIntToString() {
    let str = Example.intToString(42)
    XCTAssertEqual(str, "42")
    }
    }
    ਖ਼ޡ൑ఆ͸XCTAssertEqualͳͲͷؔ਺Λ࢖͏
    ͜ͷؔ਺͸ɺͭͷҾ਺͕౳͘͠ͳ͍ͱɺ

    ಈ࡞֬ೝΛࣦഊͱΈͳ͢Α͏ʹͳ͍ͬͯΔ

    View Slide

  126. ඪ४ϥΠϒϥϦΛ࢖ͬͨ݁Ռը໘
    9DPEF
    Ͳ͜Ͱࣦഊ͔ͨ͠Θ͔Δ
    ࣦഊͨ͠ཧ༝΋Θ͔Δ
    ෳ਺ͷࣦഊ͕

    ҰཡͰݟΒΕΔ

    View Slide

  127. "QQ$PEF
    Ͳ͜Ͱࣦഊ͔ͨ͠Θ͔Δ
    ෳ਺ͷࣦഊ͕

    ҰཡͰݟΒΕΔ ࣦഊͨ͠ཧ༝΋Θ͔Δ
    ඪ४ϥΠϒϥϦΛ࢖ͬͨ݁Ռը໘


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  128. ඪ४ϥΠϒϥϦͷํ๏ͷ໰୊఺ɿ
    w Ϗϧυઃఆ͕յΕ͍ͯΔͱɺ௚͢·Ͱ

    ಈ࡞֬ೝͷίʔυΛ࣮ߦͰ͖ͳ͍
    w ઃఆΛ͙͢ʹ͸௚ͤͳͦ͞͏ͳΒɺ

    "QQ%FMFHBUFͷํ๏ΛҰ࣌తʹ

    ࢖͏ͷ΋༗ޮͳखஈ
    ͋Γ͕ͪͳͷ͕

    Կ͔ͷϥΠϒϥϦͷ

    4FBSDI1BUITͷ

    ઃఆ΋Ε

    View Slide

  129. ϊ΢ϋ΢ͷ·ͱΊ
    ಈ࡞֬ೝΛࣗಈԽ͢Δͭͷํ๏ɿ
    ಈ࡞֬ೝͷͨΊͷඪ४ϥΠϒϥϦΛ

    ࢖ͬͯಈ࡞֬ೝ
    "QQ%FMFHBUFܦ༝Ͱಈ࡞֬ೝ
    "
    #
    #ͷํ͕Φεεϝ͕ͩɺ͙͢ʹ͸೉ͦ͠͏ͳΒҰ࣌తʹ"Ͱ΋0,ʂ

    View Slide

  130. ؒҧ͍ʹ͍͘ઃܭʹ͢Δ
    ϊ΢ϋ΢


    Ͱ
    ͸
    ׂ
    Ѫ
    Φεεϝ౓ʜ

    View Slide

  131. σόοάΛͦ΋ͦ΋͍Βͳ͍Α͏ʹͰ͖ΔͳΒ

    ͦΕ͕Ұ൪࣌ؒΛ୹ॖͰ͖Δ
    ྫͱͯ࣍͠ͷΑ͏ͳ޻෉Λ঺հ͢Δɿ
    Կ͔ͷ*%Λ௚઀ͷ*OU΍4USJOHʹ͠ͳ͍
    ίʔυΛࣗಈੜ੒͢Δ
    "
    #


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  132. ϊ΢ϋ΢ʵ" *%ΛݸผͷTUSVDUʹ͢Δ
    ΞϓϦΛ࡞੒͍ͯ͠Δͱෳ਺ͷछྨͷ*%Λ

    औΓѻ͏͜ͱ͕ଟ͍ʢ6TFS*%΍$PNNFOU*%ͳͲʣ
    ͜ͷ*%ΛऔΓҧ͑ͯॲཧͯ͠͠·ͬͨΓ͢Δͱɺ

    ݪҼಛఆ͕େมͳ໘౗ͳόάΛੜΈग़͠΍͍͢
    ͜͏͍͏࣌͸ɺࢥ͍੾ͬͯݸผͷ

    TUSVDU΁෼͚ͯ͠·͓͏


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  133. *%ΛݸผͷTUSVDU΁෼͚ͨྫ
    // BAD: औΓҧ͑ͯ΋ؾ͖ͮͮΒ͍
    let userID: Int = 1234
    let productID: Int = 5678
    // GOOD: औΓҧ͑ΔͱίϯύΠϧΤϥʔʹͳΔ
    let userID = UserID(number: 1234)
    let productID = ProductID(number: 5678)


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  134. ϊ΢ϋ΢ʵ# ίʔυΛࣗಈੜ੒͢Δ
    Α͋͘ΔόάͷݪҼʹ6*4UPSZCPBSE΍6*/JCͷ

    ໊લࢦఆͷϛε΍ϦωʔϜ๨Ε͕͋Δ
    ͜ΕΒͷίʔυΛࣗಈੜ੒ͯ͘͠ΕΔπʔϧ͕

    ͍͔ͭ͋͘ΔͷͰɺͲΕ͔Λ࢖͑͹͜ͷछͷόάΛ

    ࣄલʹ༧๷Ͱ͖Δɿ
    w HJUIVCDPNNBDDBJO3TXJGU
    w HJUIVCDPN4XJGU(FO4XJGU(FO


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  135. ϊ΢ϋ΢ͷ·ͱΊ
    ͜ͷछͷςΫχοΫ͸ແ਺ʹ͋ΔͨΊɺ

    ঺հͨ͠ͷ͸͘͝Ұ෦
    Կ͔ͷ*%Λ௚઀ͷ*OU΍

    4USJOHʹ͠ͳ͍
    ίʔυΛࣗಈੜ੒͢Δ
    "
    #


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  136. վળલͱͷൺֱ
    ײ͍ͯͩ͘͡͞

    View Slide

  137. ్த·Ͱ͏·͍͘͘ͷʹ

    కΊ੾Γʹؒʹ߹Θͳ͍

    View Slide

  138. ෆ҆ۦಈνΣοΫͰख໭Γ͕

    গͳ͘ͳͬͨͷͰɺ༧ఆ௨Γʹ

    ਐΉΑ͏ʹͳͬͨʂ

    View Slide

  139. కΊ੾Γ͕ഭͬͯ͘Δͱ

    ಈ࡞֬ೝΛ͖͠Εͳͯ͘

    େৎ෉ͩͱ৴͡Δ͔͠ͳ͍

    View Slide

  140. ಈ࡞֬ೝͷࣗಈԽͰɺӨڹൣғͷ

    ಈ࡞֬ೝ·ͰͰ͖ΔΑ͏ʹͳͬͨʂ

    View Slide

  141. 4JNVMBUPSΆͪΆͪʹ

    ͕͔͔࣌ؒͬͯΠϥΠϥ͢Δ

    View Slide

  142. ಈ࡞֬ೝͷࣗಈԽͰ

    ΆͪΆ͍ͪΒͳ͘ͳͬͨʂ

    View Slide

  143. ͜͏͍͏खॱ·Ͱ͍࣋ͬͯͬͨ݁Ռɺ

    ։ൃ଎౓͕ʹͳͬͯ·ͨ͠
    څྉ্͕͕ͬͨ

    ,VOJXBL
    ࢲͷࣄྫͰ͸

    View Slide

  144. ࣮ࡍ͸͜Μͳʹ

    ͢ΜͳΓ͍͔ͳ͍
    Τ
    ο
    ʂ
    ʁ

    View Slide

  145. ࣗಈԽͷ୩໰୊
    ཱͪ͸͔ͩΔ

    View Slide

  146. ϊ΢ϋ΢dͷ࣮ફ͕ͱͯ΋೉͍͠
    ͜ͷ͋ͱྫΛग़͕͢ɺࣗಈͰͷ

    ಈ࡞֬ೝ͸༧૝֎ʹͰ͖ͳ͍΋ͷ
    ࣮͸ʜ

    View Slide

  147. import UIKit
    class ExampleViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBAction
    func buttonTapped(_ sender: Any) {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    dump(error)
    let alertViewController = UIAlertController(
    title: "Τϥʔ͕ൃੜ͠·ͨ͠",
    message: nil,
    preferredStyle: .alert
    )
    alertViewController.addAction(UIAlertAction(title: "OK", style: .default))
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    ಈ࡞֬ೝͷࣗಈԽ͕͔ͳΓ೉͍͠ྫ

    View Slide

  148. ͜Μͳ୯७ͳ7JFX$POUSPMMFSͰ΋ɺ
    ಈ࡞֬ೝΛࣗಈԽ͢ΔͨΊͷ஌͕ࣝ

    ͔ͳΓଟ͍ͱ͍͏͜ͱΛײ͡Ͱ͍ͩ͘͞

    View Slide

  149. import UIKit
    class ExampleViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBAction
    func buttonTapped(_ sender: Any) {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    dump(error)
    let alertViewController = UIAlertController(
    title: "Τϥʔ͕ൃੜ͠·ͨ͠",
    message: nil,
    preferredStyle: .alert
    )
    alertViewController.addAction(UIAlertAction(title: "OK", style: .default))
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    Ϙλϯ͕λοϓ͞ΕͨΒ

    αʔόͱ௨৴Λ࢝ΊΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  150. import UIKit
    class ExampleViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBAction
    func buttonTapped(_ sender: Any) {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    dump(error)
    let alertViewController = UIAlertController(
    title: "Τϥʔ͕ൃੜ͠·ͨ͠",
    message: nil,
    preferredStyle: .alert
    )
    alertViewController.addAction(UIAlertAction(title: "OK", style: .default))
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    αʔόʔͱͷ௨৴͕

    Τϥʔʹͳͬͨ৔߹͸

    6*"MFSU$POUSPMMFSͰදࣔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  151. import UIKit
    class ExampleViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBAction
    func buttonTapped(_ sender: Any) {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    dump(error)
    let alertViewController = UIAlertController(
    title: "Τϥʔ͕ൃੜ͠·ͨ͠",
    message: nil,
    preferredStyle: .alert
    )
    alertViewController.addAction(UIAlertAction(title: "OK", style: .default))
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    αʔόʔ͔ΒϢʔβʔ໊͕

    ਖ਼ৗʹฦ͖ͬͯͨΒදࣔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  152. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let viewController = UIStoryboard(
    name: "ExampleViewController",
    bundle: Bundle(for: ExampleViewController.self)
    ).instantiateInitialViewController() as! ExampleViewController
    let dummySender = NSObject()
    viewController.buttonTapped(dummySender)
    XCTAssertTrue(viewController.label?.text?.contains("Kuniwak"))
    }
    }
    ͱΓ͋͑ͣಈ࡞֬ೝͷίʔυΛॻ͍ͯΈͨ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  153. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let viewController = UIStoryboard(
    name: "ExampleViewController",
    bundle: Bundle(for: ExampleViewController.self)
    ).instantiateInitialViewController() as! ExampleViewController
    let dummySender = NSObject()
    viewController.buttonTapped(dummySender)
    XCTAssertTrue(viewController.label!.text!.contains("Kuniwak"))
    }
    }
    4UPSZCPBSE͔Β7JFX$POUSPMMFSΛॳظԽ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  154. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let viewController = UIStoryboard(
    name: "ExampleViewController",
    bundle: Bundle(for: ExampleViewController.self)
    ).instantiateInitialViewController() as! ExampleViewController
    let dummySender = NSObject()
    viewController.buttonTapped(dummySender)
    XCTAssertTrue(viewController.label!.text!.contains("Kuniwak"))
    }
    }
    @IBActionΛ࣮ߦͯ͠ϘλϯλοϓΛ࠶ݱ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  155. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let viewController = UIStoryboard(
    name: "ExampleViewController",
    bundle: Bundle(for: ExampleViewController.self)
    ).instantiateInitialViewController() as! ExampleViewController
    let dummySender = NSObject()
    viewController.buttonTapped(dummySender)
    XCTAssertTrue(viewController.label!.text!.contains("Kuniwak"))
    }
    }
    UILabel͕Ϣʔβʔ໊ʹมΘͬͨ͜ͱΛ֬ೝ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  156. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let viewController = UIStoryboard(
    name: "ExampleViewController",
    bundle: Bundle(for: ExampleViewController.self)
    ).instantiateInitialViewController() as! ExampleViewController
    let dummySender = NSObject()
    viewController.buttonTapped(dummySender)
    XCTAssertTrue(viewController.label!.text!.contains("Kuniwak"))
    }
    }
    viewController.label͕nil


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  157. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let viewController = UIStoryboard(
    name: "ExampleViewController",
    bundle: Bundle(for: ExampleViewController.self)
    ).instantiateInitialViewController() as! ExampleViewController
    let dummySender = NSObject()
    viewController.buttonTapped(dummySender)
    XCTAssertTrue(viewController.label!.text!.contains("Kuniwak"))
    }
    }
    viewDidLoadΛ଴ͬͯͳ͍ͷͰɺ

    ·ͩUILabel͕ଘࡏ͍ͯ͠ͳ͍
    ݪҼɿ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  158. class ExampleViewController: UIViewController {
    private var actions: ExampleViewsAction?
    @IBOutlet weak var label: UILabel!
    override func viewDidLoad() {
    super.viewDidLoad()
    self.actions = ExampleViewsAction(
    label: self.label
    )
    }
    @IBAction
    func buttonTapped(_ sender: Any) {
    self.actions?.buttonDidTapped()
    }
    }
    मਖ਼ޙͷ7JFX$POUSPMMFS


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  159. class ExampleViewController: UIViewController {
    private var actions: ExampleViewsAction?
    @IBOutlet weak var label: UILabel!
    override func viewDidLoad() {
    super.viewDidLoad()
    self.actions = ExampleViewsAction(
    label: self.label
    )
    }
    @IBAction
    func buttonTapped(_ sender: Any) {
    self.actions?.buttonDidTapped()
    }
    }
    viewDidLoadҎ߱ͷॲཧΛผͷΫϥεʹ੾Γग़͢
    ʢͳͥ͜͏͢Δͷ͔͸΋͏গ͠ޙͰΘ͔Γ·͢ʣ
    ॏཁͳͷ͸ɺviewDidLoadͰॳظԽ͞Εͨ

    UILabelΛ੾Γग़ͨ͠Ϋϥεʹ౉͓ͯ͘͜͠ͱ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  160. class ExampleViewController: UIViewController {
    private var actions: ExampleViewsAction?
    @IBOutlet weak var label: UILabel!
    override func viewDidLoad() {
    super.viewDidLoad()
    self.actions = ExampleViewsAction(
    label: self.label
    )
    }
    @IBAction
    func buttonTapped(_ sender: Any) {
    self.actions?.buttonDidTapped()
    }
    }
    Ϙλϯͷλοϓॲཧ͸

    @IBActionΛԣྲྀ͢͠Ε͹0,


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  161. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    init(label: UILabel) {
    self.label = label
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    7JFX$POUSPMMFS͔Β෼཭͞Εͨίʔυ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  162. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    init(label: UILabel) {
    self.label = label
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    ஫ɿ͜ͷ໊લ͸ద౰ͳͷͰɺ࣮ࡍ͸֤ʑͷ

    ΞʔΩςΫνϟʹԊ໋໊ͬͨʹ͍ͯͩ͘͠͞


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  163. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    init(label: UILabel) {
    self.label = label
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    7JFX$POUSPMMFS͔Β౉͞ΕͨUILabel


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  164. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    init(label: UILabel) {
    self.label = label
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    7JFX$POUSPMMFSͷॲཧΛͦͷ··Ҡಈ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  165. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    init(label: UILabel) {
    self.label = label
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    self.present͕ଘࡏ͠ͳ͍
    ʢ͜Ε͸গ͠ޙͰमਖ਼͢Δʣ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  166. मਖ਼ޙͷಈ࡞֬ೝίʔυ
    import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let label = UILabel()
    let actions = ExampleViewsAction(label: label)
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  167. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let label = UILabel()
    let actions = ExampleViewsAction(label: label)
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    7JFX$POUSPMMFSͰ͸ͳ͘ɺ

    ੾Γग़ͨ͠ΫϥεͷํΛ

    ಈ࡞֬ೝ͢ΔΑ͏ʹ͢Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  168. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let label = UILabel()
    let actions = ExampleViewsAction(label: label)
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    7JFX$POUSPMMFS͔ΒUILabel͕౉͞ΕΔͷΛ࠶ݱ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  169. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let label = UILabel()
    let actions = ExampleViewsAction(label: label)
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    @IBActionͰλοϓΛ࠶ݱ͢Δ෦෼͸
    ੾Γग़ͨ͠Ϋϥεͷํͷϝιουͷ࣮ߦͰ୅ସ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  170. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let label = UILabel()
    let actions = ExampleViewsAction(label: label)
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ઌ΄Ͳ౉ͨ͠UILabelͷ಺༰͕

    มΘ͍ͬͯΔ͔͔֬ΊΒΕ͍ͯΔ
    ͜͏͢Δ͜ͱͰUILabel͕·ͩ

    ଘࡏ͠ͳ͍໰୊ΛղܾͰ͖Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  171. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    init(label: UILabel) {
    self.label = label
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    self.present͕ଘࡏ͠ͳ͍ͷͰ

    ·ͩϏϧυ͕௨Βͳ͍
    7JFX$POUSPMMFS͔Β෼཭͞Εͨίʔυ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  172. import UIKit
    class ModalPresenter {
    private weak var viewController: UIViewController?
    init(willPresentOn viewController: UIViewController) {
    self.viewController = viewController
    }
    func present(_ viewController: UIViewController, animated: Bool) {
    self.viewController?.present(viewController, animated: animated)
    }
    }
    self.present͕ଘࡏ͠ͳ͍໰୊ͷղܾ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  173. import UIKit
    class ModalPresenter {
    private weak var viewController: UIViewController?
    init(willPresentOn viewController: UIViewController) {
    self.viewController = viewController
    }
    func present(_ viewController: UIViewController, animated: Bool) {
    self.viewController?.present(viewController, animated: animated)
    }
    }
    7JFX$POUSPMMFSͷpresentΛ

    ผͷΫϥεͰ΋ୟ͚ΔΑ͏ʹ͢ΔΫϥε


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  174. import UIKit
    class ModalPresenter {
    private weak var viewController: UIViewController?
    init(willPresentOn viewController: UIViewController) {
    self.viewController = viewController
    }
    func present(_ viewController: UIViewController, animated: Bool) {
    self.viewController?.present(viewController, animated: animated)
    }
    }
    presentʹ࢖͏7JFX$POUSPMMFSΛ಺෦ʹอ࣋͢Δ

    ʢϝϞϦϦʔΫΛආ͚ΔͨΊʹweakʹ͢Δʣ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  175. import UIKit
    class ModalPresenter {
    private weak var viewController: UIViewController?
    init(willPresentOn viewController: UIViewController) {
    self.viewController = viewController
    }
    func present(_ viewController: UIViewController, animated: Bool) {
    self.viewController?.present(viewController, animated: animated)
    }
    }
    ͜ͷΫϥεͷpresentΛ࣮ߦ͢Δͱɺอ͍࣋ͯͨ͠

    7JFX$POUSPMMFSͷpresent͕࣮ߦ͞ΕΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  176. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    private let modalPresenter: ModalPresenter
    init(label: UILabel, modalPresenter: ModalPresenter) {
    self.label = label
    self.modalPresenter = modalPresenter
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.modalPresenter.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    मਖ਼ޙͷ7JFX$POUSPMMFS͔Β෼཭͞Εͨίʔυ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  177. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    private let modalPresenter: ModalPresenter
    init(label: UILabel, modalPresenter: ModalPresenter) {
    self.label = label
    self.modalPresenter = modalPresenter
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.modalPresenter.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    7JFX$POUSPMMFS͔ΒUILabelͱҰॹʹ

    ઌ΄ͲͷModalPresenterΛड͚औΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  178. import UIKit
    class ExampleViewsAction {
    private let label: UILabel
    private let modalPresenter: ModalPresenter
    init(label: UILabel, modalPresenter: ModalPresenter) {
    self.label = label
    self.modalPresenter = modalPresenter
    }
    func buttonDidTapped() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, error) in
    guard let `self` = self else { return }
    if let error = error {
    // লུ
    self.modalPresenter.present(alertViewController, animated: true)
    return
    }
    self.label.text = "Hello, \(user!.name)!"
    }
    }
    }
    self.presentΛModalPresenterͷ

    presentͷݺͼग़͠΁ม͑Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  179. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(
    willPresentOn: UIViewController()
    )
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter
    )
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    मਖ਼ޙͷಈ࡞֬ೝίʔυ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  180. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(
    willPresentOn: UIViewController()
    )
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter
    )
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    Ϗϧυ͕௨Γ͑͢͞Ε͹͍͍ͷͰɺ

    ద౰ͳModalPresenterΛ࡞੒͢Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  181. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(
    willPresentOn: UIViewController()
    )
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter
    )
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    7JFX$POUSPMMFS͔Β

    ੾Γग़ͨ͠Ϋϥε΁౉͢


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  182. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(
    willPresentOn: UIViewController()
    )
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter
    )
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    Ϗϧυ͕௨ΔΑ͏ʹͳͬͨ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  183. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(
    willPresentOn: UIViewController()
    )
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter
    )
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    XCTAssertEqual failed: ("nil") is

    not equal to ("Optional(true)")


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  184. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(
    willPresentOn: UIViewController()
    )
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter
    )
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ͜ͷ࣌ͷlabel.textΛݟΔͱ

    ॳظ஋ͷ··ʹͳ͍ͬͯͨ
    Ϣʔβʔ໊͕·ͩදࣔ͞Ε͍ͯͳ͍Α͏ͩ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  185. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(
    willPresentOn: UIViewController()
    )
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter
    )
    actions.buttonDidTapped()
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ݪҼɿ
    αʔόʔ͔Βͷฦ৴͕·ͩ

    ฦ͖͍ͬͯͯͳ͍ͷͰɺ

    UILabelΛߋ৽Ͱ͖͍ͯͳ͍


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  186. Ұൠతʹɺ"1*͕ฦ͖ͬͯͨޙͷ7JFXͷ

    ߋ৽ͷ׬ྃΛݕ஌͢Δͷ͸೉͍͠
    ྫ͑͹ɺ࣍ͷίʔυͰ7JFXͷߋ৽͕

    ׬ྃ͢Δͷ͸"ͱ#ͷͲͪΒʁ
    if isAdminMode {
    callAdminAPI { [weak self] (admin, error) in
    guard let `self` = self else { return }
    self.label.color = admin != nil ? .red : .blue
    }
    }
    callUserAPI { [weak self] (user, error) in
    guard let `self` = self else { return }
    self.label.text = user.name
    }
    "
    #


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  187. ਖ਼ղ͸ɺʮ"ͱ#ͷͲͪΒ΋͋Γ͑Δʯ
    ͭͷ"1*ͷฦͬͯ͘ΔλΠϛϯάʹΑͬͯ

    "ͷͱ͖΋͋Ε͹#ͷͱ͖΋͋Δ
    if isAdminMode {
    callAdminAPI { [weak self] (admin, error) in
    guard let `self` = self else { return }
    self.label.color = admin != nil ? .red : .blue
    }
    }
    callUserAPI { [weak self] (user, error) in
    guard let `self` = self else { return }
    self.label.text = user.name
    }
    callAdminAPI͕

    ޙʹ׬ྃ͢Ε͹"
    callUserAPI͕

    ޙʹ׬ྃ͢Ε͹#


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  188. ݟͨ໨ͷߋ৽Λ"1*ͳͲͷཪଆͷࣄ৘͔Β੾Γ཭͢ͱղܾͰ͖Δ
    ݟͨ໨Λ࢘ΔίϯϙʔωϯτʹදࣔΛ

    ҰׅͰ൓ө͢Δ͚ͩͷϝιουΛੜ΍͢
    "1*ݺͼग़͠౳Ͱ͢΂ͯͷ৘ใ͕ἧͬͨΒ

    ϝιουΛݺͼग़͢ઃܭن໿ʹ͢Δ
    ͜͏͍͏ͱ͖͸
    func apply(userName: String, isAdmin: Bool) {
    self.label.text = userName
    self.label.backgroundColor = isAdmin ? .red : .black
    }
    // API ͷ྆ํ͕ฦ͖͔ͬͯͯΒදࣔΛ൓ө͢Δ
    myView.apply(userName: "Kuniwak", isAdmin: false)


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  189. ݟͨ໨ͱཪଆͷ෼཭ͷͨΊͷ४උ
    protocol ExampleModelDelegate: class {
    func apply(state: ExampleModelState)
    }
    enum ExampleModelState: Equatable {
    case success(userName: String)
    case failure(error: ExampleModelUpdateError)
    }
    enum ExampleModelUpdateError: Error, Equatable {
    case unspecified(debugInfo: String)
    }


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  190. protocol ExampleModelDelegate: class {
    func apply(state: ExampleModelState)
    }
    enum ExampleModelState: Equatable {
    case success(userName: String)
    case failure(error: ExampleModelUpdateError)
    }
    enum ExampleModelUpdateError: Error, Equatable {
    case unspecified(debugInfo: String)
    }
    ཪଆͷ࢓ࣄͷ४උ͕੔ͬͨͱ͖ʹୟ͍ͯ΋Β͏

    ݟͨ໨ͷҰׅ൓өϝιου


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  191. protocol ExampleModelDelegate: class {
    func apply(state: ExampleModelState)
    }
    enum ExampleModelState: Equatable {
    case success(userName: String)
    case failure(error: ExampleModelUpdateError)
    }
    enum ExampleModelUpdateError: Error, Equatable {
    case unspecified(debugInfo: String)
    }
    ཪଆͷ࠷ऴతͳ݁Ռ͸ɺ
    w ੒ޭͯ͠userName
    w ࣦഊͯ͠error
    ͷͲͪΒ͔ͳͷͰɺFOVNͰදݱ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  192. protocol ExampleModelDelegate: class {
    func apply(state: ExampleModelState)
    }
    enum ExampleModelState: Equatable {
    case success(userName: String)
    case failure(error: ExampleModelUpdateError)
    }
    enum ExampleModelUpdateError: Error, Equatable {
    case unspecified(debugInfo: String)
    }
    ཪଆͷ࢓ࣄͷ݁Ռ͕Equatableͩͱ

    ޙͰศརʹͳΔͷͰɺEquatableͳ

    ΤϥʔͷܕΛ࡞੒͓ͯ͘͠


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  193. ཪଆͷ࢓ࣄΛ͓͜ͳ͏Ϋϥε
    class ExampleModel {
    weak var delegate: ExampleModelDelegate?
    func update() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, erro
    guard let `self` = self else { return }
    if let error = error {
    self.delegate?.apply(state: .failure(error: .unspecified(debugInfo: "\(error)")
    }
    else if let user = user {
    self.delegate?.apply(state: .success(userName: user.name))
    }
    }
    }
    }


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  194. class ExampleModel {
    weak var delegate: ExampleModelDelegate?
    func update() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, erro
    guard let `self` = self else { return }
    if let error = error {
    self.delegate?.apply(state: .failure(error: .unspecified(debugInfo: "\(error)")
    }
    else if let user = user {
    self.delegate?.apply(state: .success(userName: user.name))
    }
    }
    }
    }
    ஫ɿ͜ͷ໊લ͸ద౰ͳͷͰɺ࣮ࡍ͸֤ʑͷ

    ΞʔΩςΫνϟʹԊ໋໊ͬͨʹ͍ͯͩ͘͠͞


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  195. class ExampleModel {
    weak var delegate: ExampleModelDelegate?
    func update() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, erro
    guard let `self` = self else { return }
    if let error = error {
    self.delegate?.apply(state: .failure(error: .unspecified(debugInfo: "\(error)")
    }
    else if let user = user {
    self.delegate?.apply(state: .success(userName: user.name))
    }
    }
    }
    }
    ཪଆͷ४උ͕੔ͬͨΒ௨஌͢Δઌͷ%FMFHBUF
    ͜ͷྫͰ͸ɺ7JFX$POUSPMMFS͔Β੾Γग़ͨ͠

    Ϋϥε͕͜ͷ%FMFHBUFΛ࣮૷͢Δ͜ͱʹͳΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  196. class ExampleModel {
    weak var delegate: ExampleModelDelegate?
    func update() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, erro
    guard let `self` = self else { return }
    if let error = error {
    self.delegate?.apply(state: .failure(error: .unspecified(debugInfo: "\(error)")
    }
    else if let user = user {
    self.delegate?.apply(state: .success(userName: user.name))
    }
    }
    }
    }
    "1*ͷݺͼग़͠͸ɺ͜ͷΫϥεͷ

    updateϝιουͷ࣮ߦͰ͸͡ΊΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  197. class ExampleModel {
    weak var delegate: ExampleModelDelegate?
    func update() {
    APIClient.shared.get(type: User.self, url: URLs.getSomething) { [weak self] (user, erro
    guard let `self` = self else { return }
    if let error = error {
    self.delegate?.apply(state: .failure(error: .unspecified(debugInfo: "\(error)")
    }
    else if let user = user {
    self.delegate?.apply(state: .success(userName: user.name))
    }
    }
    }
    }
    "1*ͷ݁Ռ͕ࣦഊͷΑ͏ͳΒ.failure Λ
    %FMFHBUF΁௨஌͠ɺ੒ޭ͍ͯ͠ΔΑ͏

    ͳΒ .successΛ%FMFHBUF΁௨஌͢Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  198. 7JFX$POUSPMMFS͔Β෼཭ͨ͠ΫϥεͷFYUFOTJPO
    extension ExampleViewsAction: ExampleModelDelegate {
    func apply(state: ExampleModelState) {
    switch state {
    case .failure(let error):
    dump(error)
    let alertViewController = UIAlertController(
    title: "Τϥʔ͕ൃੜ͠·ͨ͠",
    message: nil,
    preferredStyle: .alert
    )
    alertViewController.addAction(UIAlertAction(title: "OK", style: .default))
    self.modalPresenter.present(alertViewController, animated: true)
    case .success(let userName):
    self.label.text = "Hello, \(userName)!"
    }
    }
    }


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  199. extension ExampleViewsAction: ExampleModelDelegate {
    func apply(state: ExampleModelState) {
    switch state {
    case .failure(let error):
    dump(error)
    let alertViewController = UIAlertController(
    title: "Τϥʔ͕ൃੜ͠·ͨ͠",
    message: nil,
    preferredStyle: .alert
    )
    alertViewController.addAction(UIAlertAction(title: "OK", style: .default))
    self.modalPresenter.present(alertViewController, animated: true)
    case .success(let userName):
    self.label.text = "Hello, \(userName)!"
    }
    }
    }
    ઌ΄Ͳͷ%FMFHBUFΛ࣮૷ͤ͞Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  200. extension ExampleViewsAction: ExampleModelDelegate {
    func apply(state: ExampleModelState) {
    switch state {
    case .failure(let error):
    dump(error)
    let alertViewController = UIAlertController(
    title: "Τϥʔ͕ൃੜ͠·ͨ͠",
    message: nil,
    preferredStyle: .alert
    )
    alertViewController.addAction(UIAlertAction(title: "OK", style: .default))
    self.modalPresenter.present(alertViewController, animated: true)
    case .success(let userName):
    self.label.text = "Hello, \(userName)!"
    }
    }
    }
    දࣔͷҰׅߋ৽ϝιουΛ࣮૷

    ʢ಺༰͸मਖ਼લͱಉ͡ʣ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  201. class ExampleViewsAction {
    private let label: UILabel
    private let modalPresenter: ModalPresenter
    private let model: ExampleModel
    init(label: UILabel, modalPresenter: ModalPresenter,
    model: ExampleModel) {
    self.label = label
    self.modalPresenter = modalPresenter
    self.model = model
    }
    func buttonDidTapped() {
    self.model.update()
    }
    }
    7JFX$POUSPMMFS͔Β෼཭ͨ͠Ϋϥε


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  202. class ExampleViewsAction {
    private let label: UILabel
    private let modalPresenter: ModalPresenter
    private let model: ExampleModel
    init(label: UILabel, modalPresenter: ModalPresenter,
    model: ExampleModel) {
    self.label = label
    self.modalPresenter = modalPresenter
    self.model = model
    }
    func buttonDidTapped() {
    self.model.update()
    }
    }
    ཪଆͷ࢓ࣄΫϥεͷ࡞੒Λ͜ͷதͰ΍ͬͯ΋͍͍
    ͕ɺ7JFX$POUSPMMFS͔Β౉ͯ͠΋Β͏ͱɺͷͪ
    ͷָͪʹͳΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  203. class ExampleViewsAction {
    private let label: UILabel
    private let modalPresenter: ModalPresenter
    private let model: ExampleModel
    init(label: UILabel, modalPresenter: ModalPresenter,
    model: ExampleModel) {
    self.label = label
    self.modalPresenter = modalPresenter
    self.model = model
    }
    func buttonDidTapped() {
    self.model.update()
    }
    }
    ϘλϯͷλοϓͰཪଆͷ࢓ࣄΛ࢝ΊΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  204. मਖ਼ޙͷ7JFX$POUSPMMFS
    import UIKit
    class ExampleViewController: UIViewController {
    // লུ
    override func viewDidLoad() {
    super.viewDidLoad()
    self.actions = ExampleViewsAction(
    label: self.label,
    modalPresenter: ModalPresenter(willPresentOn: self),
    model: ExampleModel()
    )
    }
    }


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  205. import UIKit
    class ExampleViewController: UIViewController {
    // লུ
    override func viewDidLoad() {
    super.viewDidLoad()
    self.actions = ExampleViewsAction(
    label: self.label,
    modalPresenter: ModalPresenter(willPresentOn: self),
    model: ExampleModel()
    )
    }
    }
    UILabelͳͲΛ౉͍ͭ͢Ͱʹཪଆͷ

    ࢓ࣄΫϥε΋࡞੒ͯ͠౉͓ͯ͘͠


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  206. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    मਖ਼ޙͷಈ࡞֬ೝίʔυ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  207. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    Ϙλϯͷλοϓͷ࠶ݱͰ͸ͳ͘ɺ

    ௚઀ݟͨ໨ͷ൓өϝιουΛୟ͘


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  208. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ͔͠͠ɺ͜͏ͯ͠͠·͏ͱϘλϯΛλοϓͯ͠

    ද͕ࣔߋ৽͞ΕΔ͜ͱͷ֬ೝʹͳΒͳ͍ͷͰ͸ʁ

    ͱ͍͏ෆ҆͸࣍ͷͭͷํ๏Ͱ؇࿨Ͱ͖Δɿ
    w Ϙλϯ͕λοϓ͞ΕͨΒɺཪͷ࢓ࣄΫϥε͕

    ݺ͹ΕΔ͜ͱΛ֬ೝ͢ΔίʔυΛ௥Ճ
    w ཪͷ࢓ࣄΫϥε͕ݺ͹ΕͨΒɺඞͣ׬ྃ͢Δ

    ͜ͱΛ֬ೝ͢ΔίʔυΛ௥Ճ
    ͦΕͧΕͷৄࡉ͸εϥΠυͷຕ਺ͷࣄ৘ͰׂѪ͢Δ͕ɺ
    Ҏ߱ͷεϥΠυͷ஌͚ࣝͩͰ࣮૷Ͱ͖Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  209. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    Ͱ͸ಈ࡞֬ೝΛ࣮ߦͯ͠ΈΑ͏


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  210. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ͜͜·Ͱ͖ͯ΍ͬͱ੒ޭ͠·͢


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  211. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ͔͠͠ɺ·ͩ"1*ݺͼग़͕͠Τϥʔʹ
    ͳͬͨ৔߹ͷಈ࡞֬ೝΛͰ͖͍ͯͳ͍ʜ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  212. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ͜Ε΋ίʔυͰಈ࡞֬ೝͰ͖Δ͕ɺ

    εϥΠυͷຕ਺ͷ౎߹ͰׂѪ͢Δ
    ͜ͷ࡞ۀͷώϯτΛॻ͍͓ͯ͘ɿ
    w "1*ͷ݁ՌΛಈ࡞֬ೝίʔυ͔Β

    ࢦఆͰ͖ΔΑ͏ʹ͢Δ
    ‎ *OQVU0CKFDU*OKFDUJPO
    w .PEBM1SFTFOUFSͷϝιουݺͼग़͠Λ

    ه࿥͢ΔِͷΦϒδΣΫτΛ࡞੒͢Δ
    ‎ 0VUQVU0CKFDU*OKFDUJPO
    ͦΕͧΕͷৄࡉ͸ɺεϥΠυͷޙํͰ

    ঺հ͢ΔʮJ04Ͱςετ༰қͳઃܭΛ

    ࣮ݱ͢ΔͨΊͷσβΠϯύλʔϯʯͰ

    αϯϓϧίʔυͱ߹Θͤͯղઆ͍ͯ͠Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  213. import XCTest
    import UIKit
    @testable import IOSDC2018Debugging
    class ExampleViewControllerTest: XCTestCase {
    func testMyNameIsVisible() {
    let dummyModalPresenter = ModalPresenter(willPresentOn: UIViewControlle
    let label = UILabel()
    let actions = ExampleViewsAction(
    label: label,
    modalPresenter: dummyModalPresenter,
    model: ExampleModel()
    )
    actions.apply(state: .success(userName: "Kuniwak"))
    XCTAssertTrue(label.text?.contains("Kuniwak"))
    }
    }
    ͜Ε·Ͱͷ࡞ۀͰ΍ͬͨ͜ͱɿ
    w 7JFX$POUSPMMFSͷॲཧΛผ΁੾Γग़ͨ͠
    w ผͷΫϥεͰUIViewController.presentΛ

    ࢖͑ΔΑ͏ʹͨ͠
    w ݟͨ໨ͷҰׅ൓өΛίʔυ͔Β

    ࣮ߦͰ͖ΔΑ͏ʹͨ͠
    ࣗಈͰͷಈ࡞֬ೝʹඞཁͳࡉ͔ͳ஌͕ࣝ

    ͱͯ΋ଟ͍͜ͱ͕Θ͔Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  214. ಈ࡞֬ೝͷࣗಈԽʹ͸ɺී௨ͷ

    ΞϓϦ࣮૷ͱ͸ҟͳΔ஌͕ࣝඞཁ
    ݴ͍͔ͨͬͨ͜ͱ͸

    View Slide

  215. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա



    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ

    View Slide

  216. ։ൃ଎౓ͷ૿Ճྔ
    ͜͜·Ͱ͸͢ΜͳΓ͍͘
    ࣌ؒܦա


    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ

    View Slide

  217. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ͋ΕʁࣗಈԽ͕೉͍ͧ͠ʜʁ

    Ͱ΋ηϧϑνΣοΫͪΌΜͱ

    ΍ͬͯΔ͔Β͕͔͔࣌ؒΔʜ


    View Slide

  218. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ΋͏ͪΐͬͱؤுͬͯΈ͚ͨͲɺ

    ͕͔͔࣌ؒΓ͗͢Δʜʜ


    View Slide

  219. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ͩΊͩɺࣗಈԽແཧͩʜ
    ͜Μͳঢ়گͰෆ҆ͳൣғ

    ͢΂ͯͷ֬ೝͳΜͯແཧʜ


    View Slide

  220. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա


    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ΋͠ɺޮ཰తʹಈ࡞֬ೝΛ

    ࣗಈԽ͢ΔͨΊͷ஌ࣝΛ

    ͍࣋ͬͯͨΒʜ

    View Slide

  221. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ΋͏ͪΐͬͱؤுͬͯΈ͚ͨͲɺ

    ͕͔͔࣌ؒΓ͗͢Δʜʜ


    View Slide

  222. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ͕͔͔͍࣌ؒͬͯΔͷ͸ɺ

    ͜ͷςΫχοΫ࢖͑͹

    গ͠ϚγʹͳΔ͸ͣʜ


    View Slide

  223. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ͋ɺͳΜָ͔ʹͳ͖ͬͯͨ


    View Slide

  224. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ΊͬͪΌָʹͳͬͨ


    View Slide

  225. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ͋ͱ͔ΒৼΓฦΔͱɺ

    ͜͜ʹͭΒ͍࣌ظ͕͋ͬͨ


    View Slide

  226. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ࣗಈԽͷ୩


    View Slide

  227. ։ൃ଎౓ͷ૿Ճྔ
    ࣌ؒܦա
    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ


    ࣗಈԽͷ୩Λӽ͑ΔͨΊʹ͸ɺ

    ͏·͘੾Γൈ͚ΔͨΊͷ஌͕ࣝ

    ͱͯ΋େࣄͩͱ͍͏͜ͱ

    View Slide

  228. ࢲͷྫͰ͸ɺݩ͔Βಈ࡞֬ೝΛ

    ಘҙͱ͍ͯͨ͠,VOJXBL͕ओಋͨ͠
    ͭ·Γɺ୩Λ৐Γӽ͑Δ஌ࣝΛ΋͍ͬͯͯ

    ڧͯ͘χϡʔήʔϜʹͳ͍ͬͯΔ

    View Slide

  229. ࣗಈԽͷ୩Λʮ͍ͭʯ

    ৐Γӽ͑Δ͔
    ܦݧऀ͕ޠΔ

    View Slide

  230. ίʔυ͕ߴྸͳ΄ͲɺࣗಈԽͷ୩͸

    Ͳ͜·Ͱ΋ਂ͘ͳΓɺ͔ͭ௕͘ͳΔ
    ߴྸͳίʔυͰ͍͖ͳΓࣗಈԽͷ୩ӽ͑Λ

    ໨ࢦ͢ͷ͸ແ஡Λӽ͑ͯແ๳
    ٯʹɺίʔυ͕ए͚Ε͹·ͩࣗಈԽͷ୩Λ

    ӽ͑ΒΕΔνϟϯε͕͋Δ
    ࢲͷࣄྫͰ͸ϑϧεΫϥονʹ

    ్த͔ΒࢀՃͯ࢝͠Ί͍ͯΔ

    View Slide

  231. ৽ػೳ࣮૷΍ϦχϡʔΞϧͳͲͰ

    ৽͍͠ίʔυΛ͜Ε͔Βॻ͘ਓ͸ɺ
    ࠓ͕νϟϯεͩʂ

    View Slide

  232. ୩ΛҰ౓Ͱ΋ӽ͑ΔܦݧΛͨ͠ਓ͸

    ͔࣍Βڧͯ͘χϡʔήʔϜͰ͖Δ
    िؒͰ΋͍͍ͷͰɺখ͍͞࿅श༻ΞϓϦͰ

    ࣗಈԽΛମݧ͓ͯ͘͜͠ͱ͕Φεεϝ
    ࠓλΠϛϯά͕ѱͯ͘΋
    ࢲͷࣗಈԽͷ࿅शΞϓϦ
    HJUIVCDPN,VOJXBL5FTUBCMF%FTJHO&YBNQMF

    View Slide

  233. ࣗಈԽͷ୩ΛʮͲ͏ʯ

    ৐Γӽ͑Δ͔
    ܦݧऀ͕ޠΔ

    View Slide

  234. ࣗಈԽͷ୩Λ౿ഁ͢ΔͨΊʹ͸ɺҎԼͷ

    ஌͕ࣝ໾ʹཱͭɿ
    w ޮ཰తͳಈ࡞֬ೝίʔυͷॻ͖ํ
    w ಈ࡞֬ೝ͠΍͍͢ຊମΞϓϦͷॻ͖ํ
    w ຊମΞϓϦͷม͍͖͑ͯํ
    ͜ΕΒͷτϐοΫ͸ʮςετʯͱ͍͏

    ΧςΰϦͰޠΒΕΔ͜ͱ͕ଟ͍Ͱ͢

    View Slide

  235. ࣗಈԽͷ୩ӽ͑ͷͨΊͷࢿྉ
    ྫ͑͹ʜ

    View Slide

  236. ʮJ04Ͱςετ༰қͳઃܭΛ

    ࣮ݱ͢ΔͨΊͷσβΠϯύλʔϯʯ
    https://speakerdeck.com/orgachem/ios-detesutorong-
    yi-nashe-ji-wo-shi-xian-surutamefalsedezainpatan


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  237. ʮ4XJGUͷ)551ϥΠϒϥϦͰۤ͠·ͳ͍ͨΊͷ

    ࣗ࡞"1*ΫϥΠΞϯτઃܭʯ
    https://qiita.com/Kuniwak/items/a972ff2ade643799d1fe


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  238. ʮ4XJGUͰॻ͍͓ͯ΅͑Δ5%%ʯ
    μϯϘʔాத / @ktanaka117 ฤ ஶ

    https://danbo-house.booth.pm/items/837289


    Ͱ
    ͸
    ׂ
    Ѫ

    View Slide

  239. ಠֶͷݶքΛײͨ͡Β
    ͍͔ͭ๚ΕΔ

    View Slide

  240. ʮJ045FTU/JHIUʯ
    https://testnight.connpass.com/
    PS݄ʹ౦ژͰ։࠵༧ఆ

    View Slide

  241. %JTDPSETXJGUEFWFMPQFSTKBQBOUFTUJOH
    https://discordapp.com/invite/4Scjz4J
    ΦϯϥΠϯνϟοτͰ΋૬ஊͰ͖Δ৔ॴ͕͋Δ

    View Slide

  242. ͜ͷൃදͰ఻͖͑Εͳ͔ͬͨ

    ৄࡉͳ࣮૷ύλʔϯʹ͍ͭͯ͸ʜ
    ͦͯ͠ʜ

    View Slide

  243. ຊΛॻ͖·͢

    View Slide

  244. ٕज़ॻΫϥ΢υϑΝϯσΟϯά1&",4Ͱ

    ϓϩδΣΫτΛެ։༧ఆʢ݄ʣ
    https://peaks.cc
    $0.
    */(

    400/

    View Slide

  245. ୩ӽ͑ͷ৺ߏ͑
    λΠϛϯά͕੔ͬͨͱ͖ͷ

    View Slide

  246. λΠϛϯά͕ສશͰ΋

    ୩͸ඞͣଘࡏ͢Δ
    ୩ఈʹԿ͕જΈɺ

    Ͳ͏৐Γӽ͑Δ͔Λ஌Ζ͏

    View Slide

  247. ৐Γӽ͑ΔͨΊͷ஌ࣝ͸ଟ͘ɺ

    ࠷ॳ͸೉͘͠ײ͡Δ͔΋͠Εͳ͍
    ͦΕͰ΋ɺ֮ͭ͑Δ͝ͱʹ

    ୩͔Β͸গͮͭ͠཭Ε͍ͯΔ

    View Slide

  248. ཱͪࢭ·ͬͯ΋

    ݱࡏ஍͸มΘΒͳ͍
    ਐΊΔ࣌ʹਐΉ͙Β͍Ͱ

    ͪΐ͏Ͳ͍͍

    View Slide

  249. ·ͱΊ
    ΁ͷ։ൃ଎౓޲্Λࢧ͑ͨ
    ͭͷϊ΢ϋ΢ɿ
    --%#Λ࢖͍͜ͳ͢
    σόοάͷλΠϛϯάΛมߋ௚ޙʹ͢Δ
    ෆ҆ۦಈͰηϧϑνΣοΫ͢Δ
    ಈ࡞֬ೝΛࣗಈԽ͢Δ
    ؒҧ͍ʹ͍͘ઃܭʹ͢Δ





    View Slide

  250. ,VOJXBL͔Βͷϝοηʔδ
    ։ൃ଎౓ୡ੒ͷલʹ͸

    ࣗಈԽͷ୩͕ଘࡏ͢Δ
    ৐Γӽ͑ΔͨΊͷ஌ࣝΛ

    ͍֮ͭͣͭ͑ͯ͜͏
    ཱͪࢭ·ͬͯ΋ޙ໭Γ͸͠ͳ͍͔Βɺ

    ਐΊΔ࣌ʹਐΉ͙Β͍Ͱͪΐ͏Ͳ͍͍

    View Slide

  251. ݸਓతʹ͸ɺJ04%$Ͱ΋΋ͬͱ

    ࣗಈతͳಈ࡞֬ೝʢςετʣͷ

    ϊ΢ϋ΢ͷൃද͕͋ͬͯ΋͍͍ͱࢥ͏ʜ

    View Slide