sentry-cocoa icon indicating copy to clipboard operation
sentry-cocoa copied to clipboard

Auto Performance Monitoring for SwiftUI

Open philipphofmann opened this issue 3 years ago • 2 comments

Description

With https://github.com/getsentry/sentry-cocoa/pull/1242 we disabled swizzling UIViewControllers generated by SwiftUI because they have long confusing names. @brustolin had an idea of how to make sense of these names. If we can extract some meaningful information out of these names, we could enable APM for SwiftUI again.

  • [ ] Collect Slow and Frozen frames

This is part of a general SwiftUI support improvement:

  • https://github.com/getsentry/sentry-cocoa/issues/1814

philipphofmann avatar Apr 05 '22 07:04 philipphofmann

SwiftUI generates the UIViewControllers for SwiftUI views during runtime. It does that between UIApplicationDidFinishLaunchingNotification and UIApplicationDidBecomeActiveNotification. In order to swizzle these UIViewControllers, we need to go through all classes with objc_getClassList. Looking into classes of the SwiftUI.framework image with copyClassNamesForImage doesn't work as these generated UIViewControllers don't end up in the SwiftUI.framework image. The UIViewControllers lifecycle seems to be a bit different. It seems like SwiftUI skips a few. That's why we need to create a transaction in different lifecycle methods.

For a work in progress branch checkout https://github.com/getsentry/sentry-cocoa/tree/feat/swift-ui-performance.

Internal Notion page.

philipphofmann avatar Jun 28 '22 13:06 philipphofmann

SwiftUI view are not really UI elements, its more like a description on what the screen should looks like. Thats why we can't manipulate Views directly. And all the code responsible to convert View into UIViews is private. If we try to manipulate it, we may cause apps to be rejected during review.

The alternative is to create a Root View or a view modifier that tracks its children performance. Of course, this will no be an automatic solution, the user must actively use this.

Also, we need to create an extra package for this. Im convinced that we should not include Swift code in the SDK.

brustolin avatar Sep 07 '22 13:09 brustolin

An update on some findings

  1. Getting a meaningful name out of SwiftUI-generated UIViewControllers seems almost impossible. We have a somewhat working solution, but it is fragile and only works for a specific use case.
  2. Combining Swift-UI view spans and SwiftUI-generated UIViewControllers spans in one transaction would be fantastic as it gives you the full picture.
  3. SwiftUI only has a callback for onAppear , which doesn’t tell you when the view finished rendering. There seems to be no proper API for when a SwiftUI view finished appearing.
  4. SwiftUI doesn’t provide a global way or hook for instrumenting all views. We have to use a wrapper for every view we want to instrument. SentryWrapper for SwiftUI views (we have to come up with a better name of course). Users can pass in a name, so we can properly name transactions.
  5. The first iteration could only support plain SwiftUI views.
  6. The first iteration could only have UIViewController spans, but the users need to identify the screen name with a SentryWrapper in SwiftUI.
  7. We still need to figure out how this would work in combination with UIViewControllers.

brustolin avatar Oct 06 '22 13:10 brustolin

Current Challenges

While working on this issue, we identified many new challenges that we did not face before.

Adding Swift

First, to be able to build SwiftUI performance monitoring, we need Swift code on the Sentry SDK. Challenges we face for that:

  1. Minimum breaking changes (none If possible)
  2. Only one import for the SDK in Swift and ObjC code
  3. Allow customers to target Swift API in an Objc project and vice-versa
  4. Same imports for all different package managers
  5. Distribution with package mangers. More details are below.
  6. Write new code in Swift (this would be nice, but not really necessary) without breaking changes
  7. Moving exiting classes from ObjC to Swift without breaking changes (nice to have)

Distribution with Package Managers

We use 3 different ways to distribute the Sentry framework: Swift Package Manager (SPM), CocoaPods, and Carthage.

  • SPM does not allow mixed languages in the same target; we need multiple targets. One of these targets will depend on the other; most likely ObjC target depends on the Swift target.
  • CocoaPods does not allow multiple targets in the same podspec.
  • Carthage has no problems but is the least used package manager.

SwiftUI Performance Monitoring

Once we have Swift in our codebase, we can talk about how we gonna introduce SwiftUI performance monitoring. While there are multiple ideas on what we can achieve, the first step is a wrapper view that will be explicitly used in the UI for tracking the performance of SwiftUI views.

On a pair programming session with @philipphofmann, we identified that by including this wrapper view in our Sentry SDK, every project that uses the Sentry Cocoa SDK would also load the SwiftUI framework at runtime, even if they don't use it. This is unacceptable, as the SwiftUI framework is quite large (around 25 MB); we don't want to do this.

The proposal is to create another library for those project that uses SwiftUI.

This extra library introduces more challenges.

  1. This new library needs to access Sentry SDK private classes to create transactions that play nicely with the rest of the SDK.
  2. How does the release process look like for the extra SwiftUI package?
  3. Should we use the same version number?
  4. How to make this available using CocoaPods

brustolin avatar Oct 13 '22 13:10 brustolin

@brustolin, please explain why you closed this issue and add the PR(s) completing this issue.

philipphofmann avatar Jan 03 '23 16:01 philipphofmann

Looks like it was added in https://github.com/getsentry/sentry-cocoa/pull/2271?

armcknight avatar Aug 09 '23 19:08 armcknight