在基于SwiftUI的iOS应用中使用Instabug

203 阅读6分钟

SwiftUI有很多值得喜欢的地方。它的声明式API使我们可以用很少的代码来构建许多常见类型的iOS视图,它强大的状态管理系统确保我们的UI与它们的底层状态和数据保持完全同步,而Xcode的新预览功能让我们比以前更快地迭代我们的视图。

然而,尽管SwiftUI有这么多优点,但在与其他工具的整体兼容性方面,它还有很长的路要走。即使是苹果自己的许多框架也还没有更新,以提供原生的SwiftUI集成,而且要使第三方工具和库在SwiftUI的声明式世界中运行良好,至少要做一些工作是很常见的。

值得庆幸的是,Instabug在基于SwiftUI的应用程序中已经运行得非常好了。网络和UI事件仍然被自动记录,就像使用UIKit时一样,用户和测试人员可以像以前一样继续调用Instabug反馈UI。

也就是说,我们可以做一些简单的事情,使 Instabug 在 SwiftUI 的背景下更容易使用,这正是我们将在本文中看到的。

在使用新的 SwiftUI 应用程序生命周期时启动 Instabug

当使用iOS 14中引入的新的基于SwiftUI的应用程序生命周期时,最初可能不明显,像Instabug这样的SDK最好在哪里初始化。毕竟,没有更多的didFinishLaunchingWithOptions 应用程序委托方法来实现,那么我们可以把我们希望在应用程序启动后立即运行的逻辑放在哪里?

也许最简单的方法是给我们的主App 结构一个初始化器,执行我们所有的启动设置工作。在这种情况下,我们将在初始化器中直接调用Instabug的start 方法,这将确保Instabug在我们的UI构建之前就已经启动并运行。

import SwiftUI
import Instabug

@main struct MyApp: App {
    init() {
        Instabug.start(
            withToken: "your-instabug-token",
            invocationEvents: [.shake, .screenshot]
        )
    }
    
    var body: some Scene {
        WindowGroup {
            ...
        }
    }
}

虽然上面的做法非常好,但我们也可以说,我们想利用Instabug的许多定制API来定制它以满足我们的需要。在这一点上,我们可能不想直接在我们的App 结构的初始化器中编写所有的设置代码,因为这样做很快就会变得非常混乱--所以让我们创建一个专门的InstabugConfigurator 类型,然后我们可以从我们的App 实现中调用。

由于我们要为整个应用程序全局配置Instabug,让我们把新的InstabugConfigurator 完全静态化,通过把它实现为一个无案例的枚举,我们甚至可以防止它被意外初始化。

import Instabug

enum InstabugConfigurator {
    private static var hasBeenConfigured = false
    private static let token = "your-instabug-token"

    static func configureIfNeeded() {
        #if !DEBUG
        guard !hasBeenConfigured else { return }
        hasBeenConfigured = true

        Instabug.start(
            withToken: token,
            invocationEvents: [.shake, .screenshot]
        )

        BugReporting.shouldCaptureViewHierarchy = true
        Instabug.welcomeMessageMode = AppConfig.isBeta ? .beta : .disabled
        #endif
    }
}

注意我们是如何将我们的configureIfNeeded 实现包裹在一个#if !DEBUG 编译器指令中的。这是为了防止这段代码在开发过程中运行,因为当我们在模拟器或设备上测试或调试我们的代码时,我们可能不希望初始化Instabug。当我们的配置方法第一次被调用时,我们还将静态hasBeenConfigured 属性设置为true ,从而防止我们的配置代码运行两次。

有了上述措施,我们现在可以简单地从我们的App 结构的初始化器中调用我们的新InstabugConfigurator ,这仍然可以确保Instabug在我们的应用程序启动时就被完全配置好,同时也让我们的App 实现继续专注于配置我们应用程序的场景和它们的主用户界面。

import SwiftUI

@main struct MyApp: App {
    init() {
        InstabugConfigurator.configureIfNeeded()
    }
    
    ...
}

现在我们已经完成了在我们基于SwiftUI的应用程序中配置Instabug的工作,让我们也来看看我们如何能让直接从SwiftUI视图中发送Instabug UI事件变得非常简单。

视图外观和消失事件

默认情况下,每当我们基于 UIKit 的视图控制器之一出现在屏幕上时,Instabug 就会自动记录,由于 SwiftUI 仍然在引擎盖下使用UIViewController 子类来管理我们的整体 UI 及其导航栈,所以即使我们自己不再创建任何UIViewController 实例,我们仍然可以使用这些自动事件。

不过,为了使我们的 Instabug 时间线更漂亮,让我们也为我们每个顶级的 SwiftUI 视图记录一个单独的外观事件。这样,我们就可以为我们的应用程序的每个屏幕设置自定义名称,这将使我们更容易获得用户在遇到错误或崩溃之前如何浏览我们的应用程序的概况。

让我们先用一个方法来扩展SwiftUI的View 协议,这个方法可以让我们把一个给定的视图识别为一个命名的屏幕,只要这个视图出现,我们就会调用Instabug的logViewDidAppearEvent API来报告它的名字--像这样:

extension View {
    func identifyAsScreen(named name: String) -> some View {
        onAppear {
            Instabug.logViewDidAppearEvent(name)
        }
    }
}

有了上述方法,我们现在就可以很容易地将我们的每个顶级视图与我们的Instabug报告联系起来,这将使我们更容易看到每个报告的问题中涉及哪些视图。例如,在这里,我们将一个HomeListView ,作为我们应用程序的 "主页 "屏幕:

struct HomeListView: View {
    var body: some View {
        List {
            ...
        }
        .identifyAsScreen(named: "Home")
    }
}

Instabug也提供了对记录自定义用户事件的全面支持--所以让我们也给我们的identifyAsScreen 方法添加第二个参数,这将让我们在视图消失时自动记录一个特定的事件:

extension View {
    func identifyAsScreen(
        named name: String,
        disappearEventName: String? = nil
    ) -> some View {
        onAppear {
            Instabug.logViewDidAppearEvent(name)
        }
        .onDisappear {
            disappearEventName.map(Instabug.logUserEvent)
        }
    }
}

利用上述方法,我们现在只需在调用我们的identifyAsScreen 方法时传递一个disappearEventName ,就可以记录消失事件(例如当一个入职流程被驳回并完成时)--像这样:

struct AppOnboardingView: View {
    var body: some View {
        VStack {
            Text("Welcome to My App!").font(.title)
            ...
        }
        .identifyAsScreen(
            named: "Onboarding",
            disappearEventName: "Onboarding.Completed"
        )
    }
}

当然,为了使Instabug为我们产生丰富的bug报告,上述步骤都不是真正需要的,但在调查bug和崩溃时,特别是与UI相关的bug和崩溃时,一点点额外的元数据可以起到很大的作用。

总结

由于Instabug包括与UIKit的深度集成,它在SwiftUI的范围内也已经很好用了,因为SwiftUI的大部分仍然直接建立在基于UIKit的类之上,如UIViewUIViewController

但是,通过在这里和那里添加一些调整,我们可以使在基于SwiftUI的应用程序中使用Instabug的体验更好 - 所以我希望你发现本文中的提示是有用的,如果你有任何问题、意见或反馈,请随时通过Twitter 或电子邮件联系我们。

谢谢你的阅读!