邮件编辑器示例 | 报告示例 |
---|---|
![]() |
![]() |
Diagnostics 是一个用 Swift 编写的库,它可以非常轻松地将诊断报告分享给您的支持团队。
该库允许轻松地将诊断报告作为附件附加到 MFMailComposeViewController
。
MFMailComposeViewController
集成DiagnosticsReportFilter
过滤敏感数据DiagnosticsLogger
来添加您自己的日志默认报告已经包含大量有价值的信息,可能足以让您开始。
确保尽早设置 DiagnosticsLogger
以捕获所有系统日志,例如在 didLaunchWithOptions
中
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
try DiagnosticsLogger.setup()
} catch {
print("Failed to setup the Diagnostics Logger")
}
return true
}
然后,只需使用以下代码显示 MFMailComposeViewController
import UIKit
import MessageUI
import Diagnostics
class ViewController: UIViewController {
@IBAction func sendDiagnostics(_ sender: UIButton) {
/// Create the report.
let report = DiagnosticsReporter.create()
guard MFMailComposeViewController.canSendMail() else {
/// For debugging purposes you can save the report to desktop when testing on the simulator.
/// This allows you to iterate fast on your report.
report.saveToDesktop()
return
}
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["support@yourcompany.com"])
mail.setSubject("Diagnostics Report")
mail.setMessageBody("An issue in the app is making me crazy, help!", isHTML: false)
/// Add the Diagnostics Report as an attachment.
mail.addDiagnosticReport(report)
present(mail, animated: true)
}
}
extension ViewController: MFMailComposeViewControllerDelegate {
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
在 macOS 上,您可以使用 NSSharingService
发送报告
import AppKit
import Diagnostics
func send(report: DiagnosticsReport) {
let service = NSSharingService(named: NSSharingService.Name.composeEmail)!
service.recipients = ["support@yourcompany.com"]
service.subject = "Diagnostics Report"
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Diagnostics-Report.html")
// remove previous report
try? FileManager.default.removeItem(at: url)
do {
try report.data.write(to: url)
} catch {
print("Failed with error: \(error)")
}
service.perform(withItems: [url])
}
为了使用 UserDefaultsReporter
,您需要指定所需的 UserDefaults
实例以及您想要读取的所有键,并在 DiagnosticsReporter.create(filename:using:filters:smartInsightsProvider)
中使用它来创建一个 DiagnosticsReport
。
let userDefaultsReporter = UserDefaultsReporter(
userDefaults: UserDefaults(suiteName: "a.userdefaults.instance"),
keys: ["key_1"]
)
let diagnosticsReport = DiagnosticsReporter.create(using: [userDefaultsReporter])
您的报告可能包含敏感数据。您可以通过创建一个 DiagnosticsReportFilter
来过滤掉这些数据。
示例项目包含一个示例
struct DiagnosticsDictionaryFilter: DiagnosticsReportFilter {
// This demonstrates how a filter can be used to filter out sensible data.
static func filter(_ diagnostics: Diagnostics) -> Diagnostics {
guard let dictionary = diagnostics as? [String: Any] else { return diagnostics }
return dictionary.filter { keyValue -> Bool in
if keyValue.key == "App Display Name" {
// Filter out the key with the value "App Display Name"
return false
} else if keyValue.key == "AppleLanguages" {
// Filter out a user defaults key.
return false
}
return true
}
}
}
可以通过将过滤器传递到 create(..)
方法中使用该示例
let report = DiagnosticsReporter.create(using: reporters, filters: [DiagnosticsDictionaryFilter.self])
要使您自己的日志出现在日志诊断中,您需要使用 DiagnosticsLogger
。
/// Support logging simple `String` messages.
DiagnosticsLogger.log(message: "Application started")
/// Support logging `Error` types.
DiagnosticsLogger.log(error: ExampleError.missingData)
如果可用,错误记录器将使用本地化描述,您可以通过使您的错误符合 LocalizedError
来添加本地化描述。
可以为给定的 URL 集合添加目录树报告,从而产生以下输出
└── Documents
+-- contents
| +-- B3F2F9AD-AB8D-4825-8369-181DEAAFF940.png
| +-- 5B9C090E-6CE1-4A2F-956B-15897AB4B0A1.png
| +-- 739416EF-8FF8-4502-9B36-CEB778385BBF.png
| +-- 27A3C96B-1813-4553-A6B7-436E6F3DBB20.png
| +-- 8F176CEE-B28F-49EB-8802-CC0438879FBE.png
| +-- 340C2371-A81A-4188-8E04-BC19E94F9DAE.png
| +-- E63AFEBC-B7E7-46D3-BC92-E34A53C0CE0A.png
| +-- 6B363F44-AB69-4A60-957E-710494381739.png
| +-- 9D31CA40-D152-45D9-BDCE-9BB09CCB825E.png
| +-- 304E2E41-9697-4F9A-9EE0-8D487ED60C45.jpeg
| └── 7 more file(s)
+-- diagnostics_log.txt
+-- Okapi.sqlite
+-- Library
| +-- Preferences
| | └── group.com.wetransfer.app.plist
| └── Caches
| └── com.apple.nsurlsessiond
| └── Downloads
| └── com.wetransfer
+-- Coyote.sqlite-shm
+-- Coyote.sqlite
+-- Coyote.sqlite-wal
+-- Okapi.sqlite-shm
+-- Okapi.sqlite-wal
└── 1 more file(s)
您可以通过添加 DirectoryTreesReporter
来做到这一点
var reporters = DiagnosticsReporter.DefaultReporter.allReporters
let documentsURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let directoryTreesReporter = DirectoryTreesReporter(
directories: [
documentsURL
]
)
reporters.insert(directoryTreesReporter, at: 1)
要添加您自己的报告,您需要使用 DiagnosticsReporting
协议。
/// An example Custom Reporter.
struct CustomReporter: DiagnosticsReporting {
static func report() -> DiagnosticsChapter {
let diagnostics: [String: String] = [
"Logged In": Session.isLoggedIn.description
]
return DiagnosticsChapter(title: "My custom report", diagnostics: diagnostics)
}
}
然后,您可以将此报告添加到创建方法中
var reporters = DiagnosticsReporter.DefaultReporter.allReporters
reporters.insert(CustomReporter.self, at: 1)
let report = DiagnosticsReporter.create(using: reporters)
UpdateAvailableInsight
使用您的 bundle 标识符来获取最新的可用应用版本。无论是否有可供用户使用的更新,都会显示一个洞察。DeviceStorageInsight
显示用户是否存储空间不足可以根据报告中的章节提供您自己的自定义洞察。一个常见的例子是解析错误并显示关于发生的错误的智能洞察
struct SmartInsightsProvider: SmartInsightsProviding {
func smartInsights(for chapter: DiagnosticsChapter) -> [SmartInsightProviding] {
guard let html = chapter.diagnostics as? HTML else { return [] }
if html.errorLogs.contains(where: { $0.contains("AppDelegate.ExampleLocalizedError") }) {
return [
SmartInsight(
name: "Localized data",
result: .warn(message: "An error was found regarding missing localisation.")
)
]
}
return []
}
}
示例项目为您提供了上述示例代码以供试用。您可以利用 html.errorLogs
、.debugLogs
和 .systemLogs
快速访问报告中的特定日志。
您可以使用 HTMLFormatting
协议来自定义 HTML 的报告方式。
只需将格式化程序传递到 DiagnosticsChapter
初始化程序中
DiagnosticsChapter(title: "UserDefaults", diagnostics: userDefaults, formatter: <#HTMLFormatting.Type#>)
Swift Package Manager 是一种用于管理 Swift 代码分发的工具。 它与 Swift 构建系统集成,可以自动执行下载、编译和链接依赖项的过程。
将 Diagnostics 作为包添加到您的 Package.swift
文件中,然后将其指定为您希望使用它的 Target 的依赖项。
import PackageDescription
let package = Package(
name: "MyProject",
platforms: [
.macOS(.v10_15)
],
dependencies: [
.package(url: "https://github.com/WeTransfer/Diagnostics.git", .upToNextMajor(from: "1.8.0"))
],
targets: [
.target(
name: "MyProject",
dependencies: ["Diagnostics"]),
.testTarget(
name: "MyProjectTests",
dependencies: ["MyProject"]),
]
)
要将 Diagnostics 作为 依赖项 添加到您的 Xcode 项目中,请选择 *File > Swift Packages > Add Package Dependency*,然后输入存储库 URL:https://github.com/WeTransfer/Diagnostics.git
。
Carthage 是一个分散的依赖管理工具,它可以构建您的依赖项并为您提供二进制框架。
您可以使用以下命令通过 Homebrew 安装 Carthage
$ brew update
$ brew install carthage
要使用 Carthage 将 Diagnostics 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它
github "WeTransfer/Diagnostics" ~> 1.00
运行 carthage update
以构建框架,并将构建的 Diagnostics.framework
拖到您的 Xcode 项目中。
如果您不想使用上述任何依赖管理器,您可以手动将 Diagnostics 集成到您的项目中。
打开终端,cd
到您的顶级项目目录,并运行以下命令“如果”您的项目未初始化为 git 仓库
$ git init
通过运行以下命令将 Diagnostics 添加为 git 子模块
$ git submodule add https://github.com/WeTransfer/Diagnostics.git
打开新的 Diagnostics
文件夹,并将 Diagnostics
文件夹拖到应用程序的 Xcode 项目的项目导航器中。 这会将 SPM 包添加为本地包。
它应该出现在应用程序的蓝色项目图标下嵌套。 它是在所有其他 Xcode 组之上还是之下并不重要。
接下来,在项目导航器中选择您的应用程序项目(蓝色项目图标),以导航到目标配置窗口,然后在侧栏中的“Targets”标题下选择应用程序目标。
在该窗口顶部的选项卡栏中,打开“General”面板。
单击“Embedded Binaries”部分下的 +
按钮。
选择 Diagnostics.framework
。
就是这样!
Diagnostics.framework
被自动添加为目标依赖项、链接框架和嵌入式框架,这对于在模拟器和设备上构建都是您需要的。
有关更改列表,请参阅 CHANGELOG.md。
该库是作为 WeTransfer 黑客马拉松的一部分创建的。 流程已在 Twitter 上报告。
感谢
此外,还要向 1Password 致敬,感谢他们 激励我们 创建了这个库。
Diagnostics 在 MIT 许可证下可用。 有关更多信息,请参阅 LICENSE 文件。