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 full-size slide

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

    View full-size slide

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

    ͋͛ΒΕΔਓ
    ͸͡Ίʹ

    View full-size slide

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

    కΊ੾Γʹؒʹ߹Θͳ͍

    View full-size slide

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

    ಈ࡞֬ೝΛ͖͠Εͳͯ͘

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

    View full-size slide

  6. 4JNVMBUPSΆͪΆͪʹ

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

    View full-size slide

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

    ͥͻUXFFU͍ͯͩ͘͠͞ʂ

    View full-size slide

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

    View full-size slide

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





    View full-size slide

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

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

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

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

    View full-size slide

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

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

    View full-size slide

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

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

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

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

  18. 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 full-size 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)
    }
    ίʔϧελοΫ
    Ҿ਺
    ϩʔΧϧม਺
    GPP"SH
    GPP7BS
    GPPؔ਺
    ؔ਺͕ݺͼग़͞ΕΔͱɺ

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

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

    View full-size 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 full-size 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ؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CBS"SH
    CBS7BS
    CBSؔ਺
    ݺͼग़͞Εͨؔ਺ͷதͰ

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

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

    View full-size 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 full-size 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ؔ਺
    Ҿ਺
    ϩʔΧϧม਺
    CB["SH
    CB[7BS
    CB[ؔ਺
    ίʔϧελοΫʹੵ·ΕΔ

    View full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

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

    View full-size slide

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

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

    "UUBDIUP1SPDFTTͰ΋

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

    View full-size slide

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

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

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

    Α͘ॏๅͨ͠ςΫχοΫ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  36. (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 full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

  54. GVODDMBTTTUSVDUFOVNͱ͍ͬͨ

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

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

    View full-size slide

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

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

    มߋ
    ಈ࡞֬ೝ

    View full-size slide

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

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

    มߋ
    ಈ࡞֬ೝ

    View full-size slide

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

    View full-size slide

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

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

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

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

    ࣌ؒ͸Ή͠ΖݮΔ
    ͭ·Γ

    View full-size slide

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

    View full-size slide

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

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

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    ෼ۀ࠷େ

    View full-size slide

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

    ෼ۀ࠷େ ख໭Γ࠷େ

    View full-size slide

  78. ख໭Γ࠷খ ෼ۀ࠷খ

    ෼ۀ࠷େ ख໭Γ࠷େ

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

    View full-size slide

  79. Ͳ͔͜ͷଥڠ఺Λ

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

    View full-size slide

  80. ීวతʹ࠷దͳ

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

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

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

    View full-size slide

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

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

    View full-size slide

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

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

    ݁ͼͭ͘Α͏ʹײ֮Λ

    ߋ৽͠ଓ͚Α͏

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

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

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

    ख໭Γ͕ࢥͬͨΑΓ

    ݮΒͳ͘ͳΔ

    View full-size slide

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

    View full-size slide

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

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

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

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

    View full-size slide

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

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

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

    View full-size slide

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

    View full-size slide

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

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

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

    View full-size slide

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

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

    View full-size slide

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

    4DIFNFͷͱ͖͚ͩΛ

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

    View full-size slide

  97. #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 full-size slide

  98. #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 full-size slide

  99. #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 full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  104. @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 full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

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

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

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

    View full-size slide

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

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

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

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

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    ઌ΄Ͳ࡞੒ͨ͠UBSHFUʹ͢Δ

    View full-size slide

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


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

    View full-size slide

  120. 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 full-size slide

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

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

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    ҰཡͰݟΒΕΔ

    View full-size slide

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

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

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

    Կ͔ͷϥΠϒϥϦͷ

    4FBSDI1BUITͷ

    ઃఆ΋Ε

    View full-size slide

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

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

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ
    Φεεϝ౓ʜ

    View full-size slide

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

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

    TUSVDU΁෼͚ͯ͠·͓͏


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

    View full-size slide

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

    కΊ੾Γʹؒʹ߹Θͳ͍

    View full-size slide

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

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

    ਐΉΑ͏ʹͳͬͨʂ

    View full-size slide

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

    ಈ࡞֬ೝΛ͖͠Εͳͯ͘

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

    View full-size slide

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

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

    View full-size slide

  140. 4JNVMBUPSΆͪΆͪʹ

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

    View full-size slide

  141. ಈ࡞֬ೝͷࣗಈԽͰ

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

    View full-size slide

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

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

    ,VOJXBL
    ࢲͷࣄྫͰ͸

    View full-size slide

  143. ࣮ࡍ͸͜Μͳʹ

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

  146. 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 full-size slide

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

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

    View full-size slide

  148. 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 full-size 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)!"
    }
    }
    }
    αʔόʔͱͷ௨৴͕

    Τϥʔʹͳͬͨ৔߹͸

    6*"MFSU$POUSPMMFSͰදࣔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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)!"
    }
    }
    }
    αʔόʔ͔ΒϢʔβʔ໊͕

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  151. 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 full-size 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"))
    }
    }
    4UPSZCPBSE͔Β7JFX$POUSPMMFSΛॳظԽ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    @IBActionΛ࣮ߦͯ͠ϘλϯλοϓΛ࠶ݱ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    UILabel͕Ϣʔβʔ໊ʹมΘͬͨ͜ͱΛ֬ೝ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    viewController.label͕nil


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    viewDidLoadΛ଴ͬͯͳ͍ͷͰɺ

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  157. 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 full-size 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()
    }
    }
    viewDidLoadҎ߱ͷॲཧΛผͷΫϥεʹ੾Γग़͢
    ʢͳͥ͜͏͢Δͷ͔͸΋͏গ͠ޙͰΘ͔Γ·͢ʣ
    ॏཁͳͷ͸ɺviewDidLoadͰॳظԽ͞Εͨ

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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()
    }
    }
    Ϙλϯͷλοϓॲཧ͸

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  160. 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 full-size 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)!"
    }
    }
    }
    ஫ɿ͜ͷ໊લ͸ద౰ͳͷͰɺ࣮ࡍ͸֤ʑͷ

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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)!"
    }
    }
    }
    7JFX$POUSPMMFS͔Β౉͞ΕͨUILabel


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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ͷॲཧΛͦͷ··Ҡಈ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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)!"
    }
    }
    }
    self.present͕ଘࡏ͠ͳ͍
    ʢ͜Ε͸গ͠ޙͰमਖ਼͢Δʣ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  165. मਖ਼ޙͷಈ࡞֬ೝίʔυ
    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 full-size 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"))
    }
    }
    7JFX$POUSPMMFSͰ͸ͳ͘ɺ

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

    ಈ࡞֬ೝ͢ΔΑ͏ʹ͢Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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͔ΒUILabel͕౉͞ΕΔͷΛ࠶ݱ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    @IBActionͰλοϓΛ࠶ݱ͢Δ෦෼͸
    ੾Γग़ͨ͠Ϋϥεͷํͷϝιουͷ࣮ߦͰ୅ସ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    ઌ΄Ͳ౉ͨ͠UILabelͷ಺༰͕

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  170. 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 full-size slide

  171. 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 full-size 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)
    }
    }
    7JFX$POUSPMMFSͷpresentΛ

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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)
    }
    }
    presentʹ࢖͏7JFX$POUSPMMFSΛ಺෦ʹอ࣋͢Δ

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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ͷpresent͕࣮ߦ͞ΕΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  175. 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 full-size 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͔ΒUILabelͱҰॹʹ

    ઌ΄ͲͷModalPresenterΛड͚औΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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)!"
    }
    }
    }
    self.presentΛModalPresenterͷ

    presentͷݺͼग़͠΁ม͑Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  178. 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 full-size 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"))
    }
    }
    Ϗϧυ͕௨Γ͑͢͞Ε͹͍͍ͷͰɺ

    ద౰ͳModalPresenterΛ࡞੒͢Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    7JFX$POUSPMMFS͔Β

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    Ϗϧυ͕௨ΔΑ͏ʹͳͬͨ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    XCTAssertEqual failed: ("nil") is

    not equal to ("Optional(true)")


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    ͜ͷ࣌ͷlabel.textΛݟΔͱ

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    ݪҼɿ
    αʔόʔ͔Βͷฦ৴͕·ͩ

    ฦ͖͍ͬͯͯͳ͍ͷͰɺ

    UILabelΛߋ৽Ͱ͖͍ͯͳ͍


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  185. Ұൠతʹɺ"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 full-size slide

  186. ਖ਼ղ͸ɺʮ"ͱ#ͷͲͪΒ΋͋Γ͑Δʯ
    ͭͷ"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 full-size slide

  187. ݟͨ໨ͷߋ৽Λ"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 full-size slide

  188. ݟͨ໨ͱཪଆͷ෼཭ͷͨΊͷ४උ
    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 full-size 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 full-size 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)
    }
    ཪଆͷ࠷ऴతͳ݁Ռ͸ɺ
    w ੒ޭͯ͠userName
    w ࣦഊͯ͠error
    ͷͲͪΒ͔ͳͷͰɺFOVNͰදݱ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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)
    }
    ཪଆͷ࢓ࣄͷ݁Ռ͕Equatableͩͱ

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  192. ཪଆͷ࢓ࣄΛ͓͜ͳ͏Ϋϥε
    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 full-size 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 full-size 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))
    }
    }
    }
    }
    ཪଆͷ४උ͕੔ͬͨΒ௨஌͢Δઌͷ%FMFHBUF
    ͜ͷྫͰ͸ɺ7JFX$POUSPMMFS͔Β੾Γग़ͨ͠

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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))
    }
    }
    }
    }
    "1*ͷݺͼग़͠͸ɺ͜ͷΫϥεͷ

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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*ͷ݁Ռ͕ࣦഊͷΑ͏ͳΒ.failure Λ
    %FMFHBUF΁௨஌͠ɺ੒ޭ͍ͯ͠ΔΑ͏

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  197. 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 full-size slide

  198. 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 full-size 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)!"
    }
    }
    }
    දࣔͷҰׅߋ৽ϝιουΛ࣮૷

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  200. 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 full-size 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 full-size 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()
    }
    }
    ϘλϯͷλοϓͰཪଆͷ࢓ࣄΛ࢝ΊΔ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  203. मਖ਼ޙͷ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 full-size slide

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

  205. 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 full-size 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 full-size 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"))
    }
    }
    ͔͠͠ɺ͜͏ͯ͠͠·͏ͱϘλϯΛλοϓͯ͠

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

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    Ͱ͸ಈ࡞֬ೝΛ࣮ߦͯ͠ΈΑ͏


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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 full-size 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"))
    }
    }
    ͔͠͠ɺ·ͩ"1*ݺͼग़͕͠Τϥʔʹ
    ͳͬͨ৔߹ͷಈ࡞֬ೝΛͰ͖͍ͯͳ͍ʜ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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"))
    }
    }
    ͜Ε΋ίʔυͰಈ࡞֬ೝͰ͖Δ͕ɺ

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

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

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

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

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size 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 7JFX$POUSPMMFSͷॲཧΛผ΁੾Γग़ͨ͠
    w ผͷΫϥεͰUIViewController.presentΛ

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

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

    ͱͯ΋ଟ͍͜ͱ͕Θ͔Δ


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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

    View full-size slide

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



    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ

    View full-size slide

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


    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ

    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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

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

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


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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

    ͕͔͔࣌ؒΓ͗͢Δʜʜ


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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

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


    View full-size slide

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


    --%#ͷ

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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

    ࣗಈԽ͢ΔͨΊͷ஌ࣝΛ

    ͍࣋ͬͯͨΒʜ

    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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

    ͕͔͔࣌ؒΓ͗͢Δʜʜ


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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

    ͜ͷςΫχοΫ࢖͑͹

    গ͠ϚγʹͳΔ͸ͣʜ


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ΊͬͪΌָʹͳͬͨ


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

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

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


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ
    ࣗಈԽͷ୩


    View full-size slide

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

    ׆༻
    มߋ௚ޙͷ

    σόοά
    ෆ҆ۦಈ

    νΣοΫ
    ಈ࡞֬ೝͷ

    ࣗಈԽ
    ؒҧ͑ʹ͍͘

    ઃܭ


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

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

    ͱͯ΋େࣄͩͱ͍͏͜ͱ

    View full-size slide

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

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

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

    View full-size slide

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

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

    View full-size slide

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

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

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

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

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

    View full-size slide

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

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

    View full-size slide

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

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

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

    View full-size slide

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

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

    View full-size slide

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

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

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

    View full-size slide

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

    View full-size slide

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

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


    Ͱ
    ͸
    ׂ
    Ѫ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

  242. ຊΛॻ͖·͢

    View full-size slide

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

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

    400/

    View full-size slide

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

    View full-size slide

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

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

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

    View full-size slide

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

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

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

    View full-size slide

  247. ཱͪࢭ·ͬͯ΋

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

    ͪΐ͏Ͳ͍͍

    View full-size slide

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





    View full-size slide

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

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

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

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

    View full-size slide

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

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

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

    View full-size slide