Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Samples/iOS-ObjectiveC/iOS-ObjectiveC.xcodeproj
Samples/iOS-Swift/iOS-Swift.xcodeproj
Samples/iOS-Swift6/iOS-Swift6.xcodeproj
Samples/iOS-SwiftUI/iOS-SwiftUI.xcodeproj
Samples/iOS-SwiftUI-Widgets/iOS-SwiftUI-Widgets.xcodeproj
Samples/iOS15-SwiftUI/iOS15-SwiftUI.xcodeproj
Samples/macOS-Swift/macOS-Swift.xcodeproj
Samples/macOS-SwiftUI/macOS-SwiftUI.xcodeproj
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### Breaking Changes

- App hang tracking is now automatically disabled for Widgets, Live Activities, Action Extensions, (Siri) Intent Extensions, and Share Extensions (#6670).
These components run in separate processes or sandboxes with different execution characteristics, which can cause false positive app hang reports.

## 9.0.0-alpha.1

### Breaking Changes
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ xcode-ci:
xcodegen --spec Samples/iOS-Swift/iOS-Swift.yml
xcodegen --spec Samples/iOS-Swift6/iOS-Swift6.yml
xcodegen --spec Samples/iOS-SwiftUI/iOS-SwiftUI.yml
xcodegen --spec Samples/iOS-SwiftUI-Widgets/iOS-SwiftUI-Widgets.yml
xcodegen --spec Samples/iOS15-SwiftUI/iOS15-SwiftUI.yml
xcodegen --spec Samples/macOS-SwiftUI/macOS-SwiftUI.yml
xcodegen --spec Samples/macOS-Swift/macOS-Swift.yml
Expand Down
20 changes: 20 additions & 0 deletions Samples/iOS-Swift/iOS-Swift-ActionExtension.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "../Shared/Config/_Common.xcconfig"

PRODUCT_BUNDLE_IDENTIFIER = io.sentry.sample.iOS-Swift.ActionExtension
INFOPLIST_FILE = iOS-Swift-ActionExtension/Resources/Info.plist

PROVISIONING_PROFILE_SPECIFIER_Debug = match Development io.sentry.sample.iOS-Swift.ActionExtension
PROVISIONING_PROFILE_SPECIFIER_Test = match Development io.sentry.sample.iOS-Swift.ActionExtension
PROVISIONING_PROFILE_SPECIFIER_TestCI = match Development io.sentry.sample.iOS-Swift.ActionExtension
PROVISIONING_PROFILE_SPECIFIER_Release = match AppStore io.sentry.sample.iOS-Swift.ActionExtension
PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*] = $(PROVISIONING_PROFILE_SPECIFIER_$(CONFIGURATION))
PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*] =

CODE_SIGN_STYLE = Manual

SUPPORTED_PLATFORMS = iphoneos iphonesimulator

SWIFT_OBJC_BRIDGING_HEADER = iOS-Swift/Tools/iOS-Swift-Bridging-Header.h

// Runtime search paths for app extensions to find frameworks in parent app
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>GIT_BRANCH</key>
<string>&lt;branch&gt;</string>
<key>GIT_COMMIT_HASH</key>
<string>&lt;sha&gt;</string>
<key>GIT_STATUS_CLEAN</key>
<string>&lt;status&gt;</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.ui-services</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ActionViewController</string>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import Sentry
@_spi(Private) import Sentry
import SentrySampleShared
import UIKit
import UniformTypeIdentifiers

class ActionViewController: UIViewController {

required init?(coder: NSCoder) {
super.init(coder: coder)
setupSentry()
}

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setupSentry()
}

private func setupSentry() {
// Prevent double initialization - SentrySDK.start() can be called multiple times
// but we want to avoid unnecessary re-initialization
guard !SentrySDK.isEnabled else {
return
}

// For this extension we need a specific configuration set, therefore we do not use the shared sample initializer
SentrySDK.start { options in
options.dsn = SentrySDKWrapper.defaultDSN
options.debug = true

// App Hang Tracking must be enabled, but should not be installed
options.enableAppHangTracking = true
}
}

override func viewDidLoad() {
super.viewDidLoad()

setupUI()
}

func setupUI() {
view.backgroundColor = .systemBackground

setupDoneButton()
setupStatusChecklist()
}

var isANRInstalled: Bool {
return isSentryEnabled && SentrySDKInternal.trimmedInstalledIntegrationNames().contains("ANRTracking")
}

var isSentryEnabled: Bool {
SentrySDK.isEnabled
}

// MARK: - UI

func setupDoneButton() {
var configuration = UIButton.Configuration.borderedProminent()
configuration.title = "Done"
configuration.baseBackgroundColor = .systemBlue
configuration.buttonSize = .large

let button = UIButton(configuration: configuration)
button.addTarget(self, action: #selector(doneAction(_:)), for: .touchUpInside)
view.addSubview(button)

button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
button.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
button.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16)
])
}

@objc func doneAction(_ sender: UIButton) {
SentrySDK.capture(message: "iOS-Swift-ActionExtension: done called")
let returnItems = extensionContext?.inputItems as? [NSExtensionItem] ?? []
extensionContext?.completeRequest(returningItems: returnItems, completionHandler: nil)
}

func setupStatusChecklist() {
let stack = UIStackView()
stack.axis = .vertical
stack.spacing = 8
view.addSubview(stack)

stack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])

let sdkStatusLabel = UILabel()
sdkStatusLabel.text = isSentryEnabled ? "βœ… Sentry is enabled" : "❌ Sentry is not enabled"
sdkStatusLabel.textAlignment = .center
stack.addArrangedSubview(sdkStatusLabel)

let anrStatusLabel = UILabel()
// We want the ANR integration to be disabled for share extensions due to false-positives
anrStatusLabel.text = !isANRInstalled ? "βœ… ANR Tracking not installed" : "❌ ANR Tracking installed"
anrStatusLabel.textAlignment = .center
stack.addArrangedSubview(anrStatusLabel)
}
}
20 changes: 20 additions & 0 deletions Samples/iOS-Swift/iOS-Swift-IntentExtension.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "../Shared/Config/_Common.xcconfig"

PRODUCT_BUNDLE_IDENTIFIER = io.sentry.sample.iOS-Swift.IntentExtension
INFOPLIST_FILE = iOS-Swift-IntentExtension/Resources/Info.plist

PROVISIONING_PROFILE_SPECIFIER_Debug = match Development io.sentry.sample.iOS-Swift.IntentExtension
PROVISIONING_PROFILE_SPECIFIER_Test = match Development io.sentry.sample.iOS-Swift.IntentExtension
PROVISIONING_PROFILE_SPECIFIER_TestCI = match Development io.sentry.sample.iOS-Swift.IntentExtension
PROVISIONING_PROFILE_SPECIFIER_Release = match AppStore io.sentry.sample.iOS-Swift.IntentExtension
PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*] = $(PROVISIONING_PROFILE_SPECIFIER_$(CONFIGURATION))
PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*] =

CODE_SIGN_STYLE = Manual

SUPPORTED_PLATFORMS = iphoneos iphonesimulator

SWIFT_OBJC_BRIDGING_HEADER = iOS-Swift/Tools/iOS-Swift-Bridging-Header.h

// Runtime search paths for app extensions to find frameworks in parent app
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>GIT_BRANCH</key>
<string>&lt;branch&gt;</string>
<key>GIT_COMMIT_HASH</key>
<string>&lt;sha&gt;</string>
<key>GIT_STATUS_CLEAN</key>
<string>&lt;status&gt;</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>IntentsRestrictedWhileLocked</key>
<array/>
<key>IntentsSupported</key>
<array>
<string>INSendMessageIntent</string>
<string>INSearchForMessagesIntent</string>
<string>INSetMessageAttributeIntent</string>
</array>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.intents-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import Intents
import Sentry
@_spi(Private) import Sentry
import SentrySampleShared

class IntentHandler: INExtension, INSendMessageIntentHandling {

override init() {
super.init()
setupSentry()
}

override func handler(for intent: INIntent) -> Any {
setupSentry()
return self
}

private func setupSentry() {
// Prevent double initialization - SentrySDK.start() can be called multiple times
// but we want to avoid unnecessary re-initialization
guard !SentrySDK.isEnabled else {
return
}

// For this extension we need a specific configuration set, therefore we do not use the shared sample initializer
SentrySDK.start { options in
options.dsn = SentrySDKWrapper.defaultDSN
options.debug = true

// App Hang Tracking must be enabled, but should not be installed
options.enableAppHangTracking = true
}
}

// MARK: - INSendMessageIntentHandling

func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) {
let person = INPerson(
personHandle: INPersonHandle(value: "john-snow", type: .unknown),
nameComponents: PersonNameComponents(givenName: "John", familyName: "Snow"),
displayName: "John Snow",
image: nil,
contactIdentifier: nil,
customIdentifier: nil
)
completion([INSendMessageRecipientResolutionResult.success(with: person)])
}

func resolveContent(for intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
let message = """
Sentry Enabled? \(isSentryEnabled ? "βœ…" : "❌")
ANR Disabled? \(!isANRInstalled ? "βœ…" : "❌")
"""
completion(INStringResolutionResult.success(with: message))
}

func confirm(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
completion(INSendMessageIntentResponse(code: .ready, userActivity: userActivity))
}

func handle(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
SentrySDK.capture(message: "iOS-Swift-IntentExtension: handle intent called")

let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
completion(INSendMessageIntentResponse(code: .success, userActivity: userActivity))
}

// MARK: - Helpers

var isANRInstalled: Bool {
return isSentryEnabled && SentrySDKInternal.trimmedInstalledIntegrationNames().contains("ANRTracking")
}

var isSentryEnabled: Bool {
SentrySDK.isEnabled
}
}
5 changes: 5 additions & 0 deletions Samples/iOS-Swift/iOS-Swift-ShareExtension.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*] =
CODE_SIGN_STYLE = Manual

SUPPORTED_PLATFORMS = iphoneos iphonesimulator

SWIFT_OBJC_BRIDGING_HEADER = iOS-Swift/Tools/iOS-Swift-Bridging-Header.h

// Runtime search paths for app extensions to find frameworks in parent app
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
Loading
Loading