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

モバイルアプリで困らないエラーハンドリングとロギングのベストプラクティス

Yoichi Tagaya
September 16, 2017

 モバイルアプリで困らないエラーハンドリングとロギングのベストプラクティス

iOSDC 2017 in Tokyo, September 16-17, 2017
https://iosdc.jp/2017/node/1482

Yoichi Tagaya

September 16, 2017
Tweet

More Decks by Yoichi Tagaya

Other Decks in Programming

Transcript

  1. !ZPJDIJUHZ
    ϞόΠϧΞϓϦͰࠔΒͳ͍
    ΤϥʔϋϯυϦϯάͱ
    ϩΪϯάͷϕετϓϥΫςΟε
    ଟլ୩ ༸Ұ
    iOSDC JAPAN 2017

    View Slide

  2. !ZPJDIJUHZ
    ࣗݾ঺հ ʙ ଟլ୩ ༸Ұ
    • Swinject (Dependency Injection Framework) ͷ࡞ऀ
    ‣ ଟ਺ͷϓϩμΫγϣϯ࠾༻
    ‣ GitHubελʔ਺: ΋͏͙͢2000
    • ϝϧΧϦͷiOSΤϯδχΞ
    ‣ ओʹΞϝϦΧ൛ΞϓϦΛ୲౰
    ‣ 9/30 (౔) Mercari Tech Conf 2017ͰDIͷ࿩Λ͠·͢
    J04%$Ͱ͸lίʔυੜ੒ʹΑΔ੩తͳ%FQFOEFODZ*OKFDUJPOzCZ!JTILBXBPO4FQ

    View Slide

  3. !ZPJDIJUHZ
    •ɹΤϯδχΞͷεΩϧ
    •ɹίʔυϨϏϡʔ
    •ɹϢχοτςετ
    •ɹ6*ࣗಈςετ
    •ɹखಈςετ
    •ɹΤϥʔϩά
    ࠓ೔ͷ࿩
    ΞϓϦͷ඼࣭޲্ʹඞཁͳ͜ͱ

    View Slide

  4. !ZPJDIJUHZ
    ͜Μͳͷݟͨ͜ͱ͋Γ·ͤΜ͔ʁ
    ΫϥογϡͰ1ˑϨϏϡʔͷཛྷ
    ࠶ݱ͠ͳ͍
    Ͳ͏͢Ε͹͍͍͔෼͔Βͳ͍
    ΞϓϦΛόʔδϣϯΞοϓͨ͠

    View Slide

  5. !ZPJDIJUHZ
    Ϋϥογϡϩάͱ͍͑͹

    View Slide

  6. !ZPJDIJUHZ
    Crashlytics
    IUUQTXXXGBCSJDJP
    $SBTIMZUJDTΛਂ͘୳ͬͯΈΑ͏

    View Slide

  7. !ZPJDIJUHZ
    CrashlyticsͰҰ൪ॏཁͳ͜ͱ
    IUUQTEPDTGBCSJDJPBQQMFDSBTIMZUJDTUFTUDSBTIIUNMUSPVCMFTIPPUJOH
    func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
    ) -> Bool {
    Fabric.with([Crashlytics.self])
    return true
    }
    ϩά͸ΫϥογϡޙʹΞϓϦ͕࠶ىಈͨ࣌͠ʹૹΒΕΔ
    application:didFinishLaunchingWithOptionsΛແࣄʹ௨ա͠ͳ͍ͱμϝ

    View Slide

  8. !ZPJDIJUHZ
    ىಈ࣌
    application:didFinishLaunchingWithOptionsͰͦ͠͏ͳ͜ͱ
    • ֤छϑϨʔϜϫʔΫͷॳظԽ
    •ɹ%BUBCBTFͷϚΠάϨʔγϣϯ
    •ɹΩϟογϡͷΫϦΞ
    ϝΠϯεϨουΛ௕࣌ؒࢭΊͳ͍

    View Slide

  9. !ZPJDIJUHZ
    ಛʹґଘϥΠϒϥϦʹ஫ҙ
    IUUQTHJUIVCDPNQJOUFSFTU1*/3FNPUF*NBHFJTTVFT
    1*/3FNPUF*NBHFࣗମ͸͍͍ϥΠϒϥϦͰ͢

    View Slide

  10. !ZPJDIJUHZ
    ͦΕͰ

    View Slide

  11. !ZPJDIJUHZ
    Crashlyticsͬͯ
    Πϯετʔϧ͢ΔҎ֎ʹ
    Կ͔ඞཁͳͷʁ

    View Slide

  12. !ZPJDIJUHZ
    Crashlyticsʹ͸
    ศརͳ෇Ճ৘ใ͕͋Δ

    View Slide

  13. !ZPJDIJUHZ
    Ϣʔβ৘ใͷ௥Ճ
    γϯάϧτϯϝιου આ໌
    TFU6TFS*EFOUJpFS @
    6TFS*EΛઃఆ͢Δ
    TFU6TFS&NBJM @

    &NBJMΛઃఆ͢Δ
    6TFS*E͕͋Ε͹ී௨͸ෆཁ
    TFU6TFS/BNF @

    Ϣʔβͷ໊લΛઃఆ͢Δ
    6TFS*E͕͋Ε͹ී௨͸ෆཁ
    ΧελϚʔαϙʔτͷ৘ใ͔Βௐ΂Δ࣌ʹศར
    &NBJMͱϢʔβͷ໊લ͸໌Β͔ͳݸਓ৘ใͳͷͰ஫ҙ

    View Slide

  14. !ZPJDIJUHZ
    Key/Valueͷ௥Ճ
    γϯάϧτϯϝιου આ໌
    TFU0CKFDU7BMVF @GPS,FZ
    4USJOHͳͲΛ,FZʹରͯ͠ઃఆ͢Δ
    TFU#PPM7BMVF @GPS,FZ
    #PPM஋Λ,FZʹରͯ͠ઃఆ͢Δ
    TFU*OU7BMVF @GPS,FZ
    *OU஋Λ,FZʹରͯ͠ઃఆ͢Δ
    TFU'MPBU7BMVF @GPS,FZ
    'MPBU஋Λ,FZʹରͯ͠ઃఆ͢Δ
    3FNPUF$POpH "#5FTU %PXOMPBEͨ͠Ϧιʔε+4ͷ7FSTJPO౳ʹ
    ࠷େݸͷ,FZ·Ͱͭͷ,FZ7BMVF͕,#·Ͱ
    ,FZ͕ͨ͘͞ΜඞཁͳΒ+40/จࣈྻʹ͢Δ͜ͱ΋ݕ౼

    View Slide

  15. !ZPJDIJUHZ
    ΧελϜϩάͷ௥Ճ
    ؔ਺ આ໌
    $-4-PHW @GPSNBU BSHT
    Ϋϥογϡൃੜલͷϩά͕Ϋϥογϡʹඥ෇͚ΒΕΔ
    ΫϥογϡͷݪҼʹͳΔ௚લͷಈ࡞͕Θ͔Δ
    ϩά͸࠷େ,# ߦจࣈ͘Β͍ͳΒߦ͘Β͍

    ͋;ΕͨΒݹ͍΋ͷ͔Βফ͞ΕΔ
    Ϣʔβߦಈ Πϕϯτ
    ͷϩά͸"OTXFST΍'JSFCBTFͳͲΛ࢖͏

    View Slide

  16. !ZPJDIJUHZ
    ඇΫϥογϡͷϨϙʔτΛ௥Ճ
    γϯάϧτϯϝιου આ໌
    SFDPSE&SSPS @

    ॏେͳΤϥʔͰϩάΤϯτϦʔΛ࡞Δ
    Ϩϙʔτ͸/PO'BUBMTάϧʔϓʹ·ͱΊΒΕΔ
    ΞϓϦ࠶ىಈ࣌ʹ$SBTIMZUJDTʹૹ৴͞ΕΔ
    Ϋϥογϡ͠ͳ͍͕ॏେͳΤϥʔΛ$SBTIMZUJDTͰ֬ೝͰ͖Δ
    ϩάʹ࢒Δͷ͸ΞϓϦ࠶ىಈ·Ͱͷ࠷େݸͷΤϥʔ·Ͱ
    $16ίετ͕͔͔ΔͷͰΦϒβʔόʔޮՌʹ஫ҙ
    %8"3' %FCVH8JUI"UUSJCVUFE3FDPSE'PSNBU
    VOXJOEJOH

    View Slide

  17. !ZPJDIJUHZ
    CrashlyticsΛ
    OSLogͱ૊Έ߹Θͤͯ
    ࣗ࡞ͷϩΨʔΛ࡞Γ·͢

    View Slide

  18. !ZPJDIJUHZ
    OSLog/os_log
    var osLog = OSLog(
    subsystem: “com.my-company.LoggingSample",
    category: “ViewModel”
    )
    os_log(“Hello world!”, log: osLog, type: .default)
    ࢖͍ํ
    J04͔Β࢖͑ΔΑ͏ʹͳͬͨϩάͷ࢓૊Έ

    View Slide

  19. !ZPJDIJUHZ
    OSLogType
    IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOPTMPHHJOH
    ϩάϨϕϧ อଘઌ ಛ௃
    EFCVH ͳ͠ σόοά༻
    JOGP ϝϞϦʔ
    όοϑΝʔ͕͍ͬͺ͍ʹͳͬͨΒফ͑Δ
    Τϥʔ͕ى͖ͨ৔߹͸อଘ͞ΕΔ
    EFGBVMU ετϨʔδ
    ࠷ॳόοϑΝʔʹอଘ͞ΕΔ
    ͍ͬͺ͍ʹͳͬͨΒετϨʔδʹҠ͞ΕΔ
    FSSPS ετϨʔδ ϓϩηε಺ͷΤϥʔͰ࢖͏
    GBVMU ετϨʔδ ෳ਺ϓϩηε͕ؔΘΔΤϥʔͰ࢖͏

    View Slide

  20. !ZPJDIJUHZ
    Console App
    IUUQTEFWFMPQFSBQQMFDPNWJEFPTQMBZXXED
    IUUQTNFEJVNDPN!BCKVSBUPVOJpFEMPHHJOHBOEBDUJWJUZUSBDJOHBB⒎FGC
    • "DUJWJUZ5SBDJOHͱ૊Έ߹
    ΘͤΔͱศར
    • ෳ਺ϓϩηε͕ؔΘΔΞ
    ϓϦͷσόοάͰศར
    w .BD"QQ
    w 8BUDI"QQ
    • ݱঢ়͸4XJGU͔Β͸௚઀࢖
    ͑ͳ͍
    w ࢖͏ʹ͸ԼͷϦϯΫࢀর

    View Slide

  21. !ZPJDIJUHZ
    ͔͠͠ͳ͕Β
    ࠓճ͸γϯϓϧͳ࢖༻๏ʹཹΊ·͢
    #
    $SBTIMZUJDT͚ͩͰे෼ڧྗ

    View Slide

  22. !ZPJDIJUHZ
    ࣗ࡞ϩΨʔͷϩάϨϕϧ
    ϩάϨϕϧ ༻్ ॲཧ
    EFCVH
    %FCVH࣌ʹ͚ͩ
    ݟ͍ͨ৘ใ
    $POTPMFʹͷΈग़ྗ
    JOGP
    "1*ίʔϧ౳
    τϨʔε
    $POTPMFͱ$-4-PHWͷग़ྗ
    FSSPS
    ϋϯυϦϯά
    ՄೳͳΤϥʔ
    $POTPMFͱ$-4-PHWͷग़ྗ
    $-4-PHWͷϝοηʔδ͸ݕࡧੑΛ্͛Δ
    GBUBM ૝ఆ֎ͷΤϥʔ
    $POTPMFʹग़ྗ
    SFDPSE&SSPSͰ$SBTIMZUJDTͷϨϙʔτΤϯτϦʔΛ࡞Δ
    BTTFSUJPO'BJMVSF΋ೖΕͯ%FCVHϏϧυͰམͪΔΑ͏ʹ͢Δ

    View Slide

  23. !ZPJDIJUHZ
    LogΫϥε
    final class Log {
    static func debug(message: String) {
    os_log("⚒%@", type: .debug, message)
    }
    static func info(message: String) {
    os_log("ℹ%@", type: .debug, message)
    CLSLogv("%@", getVaList([message]))
    }
    static func error(message: String) {
    os_log("❗%@", type: .debug, message)
    CLSLogv("[Error] %@", getVaList([message]))
    }
    }

    View Slide

  24. !ZPJDIJUHZ
    LogΫϥε
    final class Log {
    static func fatal(
    message: String,
    file: String = #file,
    function: String = #function, line: Int = #line) {
    os_log("%@", log: .default, type: .debug, message)
    let fileName = file.components(separatedBy: "/").last ?? ""
    let error = NSError(
    domain: "\(fileName):\(function)",
    code: line,
    userInfo: ["message": message]
    )
    Crashlytics.sharedInstance().recordError(error)
    assertionFailure(message)
    }
    }

    View Slide

  25. !ZPJDIJUHZ
    final class Log {
    static func error(error: Error) {
    let message = String(reflecting: error)
    os_log("❗%@", log: .default, type: .debug, message)
    CLSLogv("[Error] %@", getVaList([message]))
    }
    static func fatal(error: Error) {
    let message = String(reflecting: error)
    os_log("%@", log: .default, type: .debug, message)
    Crashlytics.sharedInstance()
    .recordError(error as NSError)
    assertionFailure(message)
    }
    }
    LogΫϥε

    View Slide

  26. !ZPJDIJUHZ
    Demo
    -PHTXJGUIUUQTHJTUHJUIVCDPNZPJDIJUHZBEBBEFGFBBCB
    "QQ%FMFHBUFTXJGUIUUQTHJTUHJUIVCDPNZPJDIJUHZFEBGBCGFCFBCDF
    7JFX$POUSPMMFSTXJGUIUUQTHJTUHJUIVCDPNZPJDIJUHZFCBFGEECBBD

    View Slide

  27. !ZPJDIJUHZ
    Demoͨ͜͠ͱ

    View Slide

  28. !ZPJDIJUHZ
    Demoͨ͜͠ͱ

    View Slide

  29. !ZPJDIJUHZ
    guard let something = something else {
    // Swallow
    return
    }
    guard !array.isEmpty else {
    // Swallow
    return
    }
    guard let something = something else {
    Log.fatal(message: "something is nil unexpectedly.")
    return
    }
    guard !array.isEmpty else {
    Log.fatal(message: "array is empty.")
    return
    }
    ࠷ޙʹ
    ෆਖ਼ͳঢ়ଶΛҿΈࠐΜͰ͍ͨΒ࠷ѱͰ΋ϩάΛૹ͓ͬͯ͜͏
    ݱࡏͷ࣮૷Ͱ͸͋Γ͑ͳͯ͘΋কདྷͷόάΛݕ஌͢ΔͨΊ

    View Slide

  30. !ZPJDIJUHZ
    %BUF 4QFBLFS 5JUMF
    !UFOOUFOO (PʹΑΔJ04ΞϓϦͷ։ൃ
    !DIVHBO[Z ϝϧΧϦͰ࣮ࢪͨ͠աڈ࠷େن໛ͷ"#ςετʮυϩϫʔWTԼλϒʯͷ෣୆ཪ
    !LJUBTVLF *OUSPEVDJOHQSPUPCVGJO4XJGU
    !KBSJOPTVLF 64൛.FSDBSJΛ·Δ͝ͱ͔Β࡞Γ௚ͨ͠࿩
    !NPUPLJFF ݁ࠗࣜΛࢧٕ͑ͨज़'JSFCBTFΛ׆༻ͨ͠αʔόϨεJ04ΞϓϦέʔγϣϯ։ൃ
    !EUWE ϝϧΧϦΞοςΛࢧ͑ΔΦʔτϚτϯ
    !KPMMZKPFTUFS
    པΉ͔Βϓογϡ௨஌ͷ࢖͍ํΛ͓Ζ͔ͦʹ͠ͳ͍Ͱ͘Εʂ
    ʙϓογϡ௨஌ͷදݱɺྺ࢙ɺ࠷৽ಈ޲·Ͱʙ
    Thank you
    .PSFGSPN BUJ04%$

    View Slide