iOS14で強化されたMetricKitで不具合対応に備える

# MetricKitとは
* MXMetricPayload
  * iOS13以降対応
  * アプリの基本情報、診断、パフォーマンスを取得可能
  * 過去24時間のデータを含むレポートを最大で1日1回受信
* MXDiagnosticPayload　
  * iOS14以降対応
  * クラッシュやユーザがアプリを利用する上での障害情報 

# 利用用途
* 利用者の端末情報蓄積
* 不具合対応の情報取得

# 取得できるデータについて
## MXMetricPayload
* パフォーマンス
  * CPU情報
  * ディスプレイ情報
  * GPU情報
  * 位置情報
  * ネットワーク情報
* 応答性
  * アプリの起動と再開
  * アニメーションの応答性
  * ユーザインタラクションに対する応答性
* ストレージ
  * ディスク情報

## MXDiagnosticPayload
* クラッシュ
  * クラッシュレポート
  * CPU例外レポート
  * アプリビジーレポート
  * ディスク書き込み例外レポート


# 導入方法について
```AppDelegate.swift
/// 1. 使用の為にimport
import MetricKit
~ 略 ~
extension AppDelegate: UIApplicationDelegate {
~ 略 ~
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        if #available(iOS 13.0, *) {
            /// 2.起動時に登録
            MXMetricManager.shared.add(self)
        }
~ 略 ~
    }

    func applicationWillTerminate(_ application: UIApplication) {
        if #available(iOS 13.0, *) {
            /// 3.終了時に解除
            MXMetricManager.shared.remove(self)
        }
    }
}

extension AppDelegate: MXMetricManagerSubscriber {
    /// 4.情報を取得する(MXMetricPayload)
    @available(iOS 13.0, *)
    func didReceive(_ payloads: [MXMetricPayload]) {
        for payload in payloads {
            let json = payload.jsonRepresentation()
            let jsonString = String(data: json, encoding: .utf8) ?? .empty
            Log.info("[MXMetricPayload] \(jsonString)")
        }
    }

    /// 5.情報を取得する(MXDiagnosticPayload)
    @available(iOS 14.0, *)
    func didReceive(_ payloads: [MXDiagnosticPayload]) {
        for payload in payloads {
            let json = payload.jsonRepresentation()
            let jsonString = String(data: json, encoding: .utf8) ?? .empty
            Log.info("[MXDiagnosticPayload] \(jsonString)")
        }
    }
}
```

# サンプルログ
## MXMetricPayloadのログ
* Xcodeのデバッグ機能を用いたログ


```MXMetricPayload.log
{
  "locationActivityMetrics" : {
    "cumulativeBestAccuracyForNavigationTime" : "20秒",
    "cumulativeBestAccuracyTime" : "30秒",
    "cumulativeHundredMetersAccuracyTime" : "30秒",
    "cumulativeNearestTenMetersAccuracyTime" : "30秒",
    "cumulativeKilometerAccuracyTime" : "20秒",
    "cumulativeThreeKilometersAccuracyTime" : "20秒"
  },
  "cellularConditionMetrics" : {
    "cellConditionTime" : {
      "histogramNumBuckets" : 3,
      "histogramValue" : {
        "0" : {
          "bucketCount" : 20,
          "bucketStart" : "1 bars",
          "bucketEnd" : "1 bars"
        },
        "1" : {
          "bucketCount" : 30,
          "bucketStart" : "2 bars",
          "bucketEnd" : "2 bars"
        },
        "2" : {
          "bucketCount" : 50,
          "bucketStart" : "3 bars",
          "bucketEnd" : "3 bars"
        }
      }
    }
  },
  "metaData" : {
    "appBuildVersion" : "53",
    "osVersion" : "iPhone OS 14.1 (18A8395)",
    "regionFormat" : "JP",
    "platformArchitecture" : "arm64e",
    "deviceType" : "iPhone13,4"
  },
  "gpuMetrics" : {
    "cumulativeGPUTime" : "20秒"
  },
  "memoryMetrics" : {
    "peakMemoryUsage" : "200,000 kB",
    "averageSuspendedMemory" : {
      "averageValue" : "100,000 kB",
      "standardDeviation" : 0,
      "sampleCount" : 500
    }
  },
  "applicationExitMetrics" : {
    "backgroundExitData" : {
      "cumulativeAppWatchdogExitCount" : 1,
      "cumulativeMemoryResourceLimitExitCount" : 1,
      "cumulativeBackgroundURLSessionCompletionTimeoutExitCount" : 1,
      "cumulativeBackgroundFetchCompletionTimeoutExitCount" : 1,
      "cumulativeAbnormalExitCount" : 1,
      "cumulativeSuspendedWithLockedFileExitCount" : 1,
      "cumulativeIllegalInstructionExitCount" : 1,
      "cumulativeMemoryPressureExitCount" : 1,
      "cumulativeBadAccessExitCount" : 1,
      "cumulativeCPUResourceLimitExitCount" : 1,
      "cumulativeBackgroundTaskAssertionTimeoutExitCount" : 1,
      "cumulativeNormalAppExitCount" : 1
    },
    "foregroundExitData" : {
      "cumulativeBadAccessExitCount" : 1,
      "cumulativeAbnormalExitCount" : 1,
      "cumulativeMemoryResourceLimitExitCount" : 1,
      "cumulativeNormalAppExitCount" : 1,
      "cumulativeCPUResourceLimitExitCount" : 1,
      "cumulativeIllegalInstructionExitCount" : 1,
      "cumulativeAppWatchdogExitCount" : 1
    }
  },
  "displayMetrics" : {
    "averagePixelLuminance" : {
      "averageValue" : "50 apl",
      "standardDeviation" : 0,
      "sampleCount" : 500
    }
  },
  "signpostMetrics" : [
    {
      "signpostIntervalData" : {
        "histogrammedSignpostDurations" : {
          "histogramNumBuckets" : 3,
          "histogramValue" : {
            "0" : {
              "bucketCount" : 50,
              "bucketStart" : "0 ms",
              "bucketEnd" : "100 ms"
            },
            "1" : {
              "bucketCount" : 60,
              "bucketStart" : "100 ms",
              "bucketEnd" : "400 ms"
            },
            "2" : {
              "bucketCount" : 30,
              "bucketStart" : "400 ms",
              "bucketEnd" : "700 ms"
            }
          }
        },
        "signpostCumulativeCPUTime" : "30,000 ms",
        "signpostAverageMemory" : "100,000 kB",
        "signpostCumulativeLogicalWrites" : "600 kB"
      },
      "signpostCategory" : "TestSignpostCategory1",
      "signpostName" : "TestSignpostName1",
      "totalSignpostCount" : 30
    },
    {
      "signpostIntervalData" : {
        "histogrammedSignpostDurations" : {
          "histogramNumBuckets" : 3,
          "histogramValue" : {
            "0" : {
              "bucketCount" : 60,
              "bucketStart" : "0 ms",
              "bucketEnd" : "200 ms"
            },
            "1" : {
              "bucketCount" : 70,
              "bucketStart" : "201 ms",
              "bucketEnd" : "300 ms"
            },
            "2" : {
              "bucketCount" : 80,
              "bucketStart" : "301 ms",
              "bucketEnd" : "500 ms"
            }
          }
        },
        "signpostCumulativeCPUTime" : "50,000 ms",
        "signpostAverageMemory" : "60,000 kB",
        "signpostCumulativeLogicalWrites" : "700 kB"
      },
      "signpostCategory" : "TestSignpostCategory2",
      "signpostName" : "TestSignpostName2",
      "totalSignpostCount" : 40
    }
  ],
  "cpuMetrics" : {
    "cumulativeCPUTime" : "100秒",
    "cumulativeCPUInstructions" : "100 kiloinstructions"
  },
  "networkTransferMetrics" : {
    "cumulativeCellularDownload" : "80,000 kB",
    "cumulativeWifiDownload" : "60,000 kB",
    "cumulativeCellularUpload" : "70,000 kB",
    "cumulativeWifiUpload" : "50,000 kB"
  },
  "diskIOMetrics" : {
    "cumulativeLogicalWrites" : "1,300 kB"
  },
  "applicationLaunchMetrics" : {
    "histogrammedTimeToFirstDrawKey" : {
      "histogramNumBuckets" : 3,
      "histogramValue" : {
        "0" : {
          "bucketCount" : 50,
          "bucketStart" : "1,000 ms",
          "bucketEnd" : "1,010 ms"
        },
        "1" : {
          "bucketCount" : 60,
          "bucketStart" : "2,000 ms",
          "bucketEnd" : "2,010 ms"
        },
        "2" : {
          "bucketCount" : 30,
          "bucketStart" : "3,000 ms",
          "bucketEnd" : "3,010 ms"
        }
      }
    },
    "histogrammedResumeTime" : {
      "histogramNumBuckets" : 3,
      "histogramValue" : {
        "0" : {
          "bucketCount" : 60,
          "bucketStart" : "200 ms",
          "bucketEnd" : "210 ms"
        },
        "1" : {
          "bucketCount" : 70,
          "bucketStart" : "300 ms",
          "bucketEnd" : "310 ms"
        },
        "2" : {
          "bucketCount" : 80,
          "bucketStart" : "500 ms",
          "bucketEnd" : "510 ms"
        }
      }
    }
  },
  "applicationTimeMetrics" : {
    "cumulativeForegroundTime" : "700秒",
    "cumulativeBackgroundTime" : "40秒",
    "cumulativeBackgroundAudioTime" : "30秒",
    "cumulativeBackgroundLocationTime" : "30秒"
  },
  "timeStampEnd" : "2021-06-21 14:59:00 +0000",
  "animationMetrics" : {
    "scrollHitchTimeRatio" : "1,000 ms per s"
  },
  "applicationResponsivenessMetrics" : {
    "histogrammedAppHangTime" : {
      "histogramNumBuckets" : 3,
      "histogramValue" : {
        "0" : {
          "bucketCount" : 50,
          "bucketStart" : "0 ms",
          "bucketEnd" : "100 ms"
        },
        "1" : {
          "bucketCount" : 60,
          "bucketStart" : "100 ms",
          "bucketEnd" : "400 ms"
        },
        "2" : {
          "bucketCount" : 30,
          "bucketStart" : "400 ms",
          "bucketEnd" : "700 ms"
        }
      }
    }
  },
  "appVersion" : "9.3.3",
  "timeStampBegin" : "2021-06-20 15:00:00 +0000"
}
```

# 参考
* [MetricKit | Apple Developer Documentation](https://developer.apple.com/documentation/metrickit)
* [MXMetricPayload | Apple Developer Documentation](https://developer.apple.com/documentation/metrickit/mxmetricpayload)
* [MXDiagnosticPayload | Apple Developer Documentation](https://developer.apple.com/documentation/metrickit/mxdiagnosticpayload)