From 14383871aa232bc846d637db45e952adc03d2987 Mon Sep 17 00:00:00 2001 From: Alex Grebenyuk Date: Mon, 20 Apr 2026 19:16:15 -0400 Subject: [PATCH] Fix getAppIcon() crash on iOS 26 Simulator UIImage(named:) can raise NSInternalInconsistencyException ("Need an imageRef") for app icon assets that can't be rasterized for the current trait, bypassing Swift's optional-based error handling. Wrap the call in PulseObjCExceptionCatcher via a new reusable `attempt` helper. Fixes #364. --- CHANGELOG.md | 6 ++++++ Sources/Pulse/Helpers/CoreData+Extensions.swift | 7 +++++++ Sources/Pulse/LoggerStore/LoggerStore+Info.swift | 5 ++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b7d1b93..8869045a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Pulse 5.x +## Pulse 5.2.1 + +*Apr 20, 2026* + +- Fix a crash on iOS 26 Simulator where `UIImage(named:)` raises `NSInternalInconsistencyException` ("Need an imageRef") for app icon assets that can't be rasterized for the current trait ([#364](https://github.com/kean/Pulse/issues/364)) + ## Pulse 5.2 *Apr 19, 2026* diff --git a/Sources/Pulse/Helpers/CoreData+Extensions.swift b/Sources/Pulse/Helpers/CoreData+Extensions.swift index 771925c6..a6fdc1ba 100644 --- a/Sources/Pulse/Helpers/CoreData+Extensions.swift +++ b/Sources/Pulse/Helpers/CoreData+Extensions.swift @@ -15,6 +15,13 @@ extension PulseObjCExceptionCatcher { } return try result!.get() } + + /// Returns the result of `block`, or `nil` if it returns `nil` or raises + /// an Objective-C exception. Useful for UIKit APIs that raise exceptions + /// from otherwise-optional call sites (e.g. `UIImage(named:)` on iOS 26+). + public static func attempt(_ block: () -> T?) -> T? { + (try? perform(block)) ?? nil + } } extension NSManagedObjectContext { diff --git a/Sources/Pulse/LoggerStore/LoggerStore+Info.swift b/Sources/Pulse/LoggerStore/LoggerStore+Info.swift index 2915ce9c..8aa78e58 100644 --- a/Sources/Pulse/LoggerStore/LoggerStore+Info.swift +++ b/Sources/Pulse/LoggerStore/LoggerStore+Info.swift @@ -3,6 +3,7 @@ // Copyright (c) 2020-2026 Alexander Grebenyuk (github.com/kean). import Foundation +import PulseObjCHelpers extension LoggerStore { /// The store info. @@ -93,7 +94,9 @@ private func getAppIcon() -> Data? { let primaryIcons = icons["CFBundlePrimaryIcon"] as? [String: Any], let files = primaryIcons["CFBundleIconFiles"] as? [String], let lastIcon = files.last, - let image = PlatformImage(named: lastIcon), + // On iOS 26+, `UIImage(named:)` can raise `NSInternalInconsistencyException` + // ("Need an imageRef") for assets that can't be rasterized for the current trait. + let image = PulseObjCExceptionCatcher.attempt({ PlatformImage(named: lastIcon) }), let thumbnail = Graphics.resize(image, to: CGSize(width: 44, height: 44)) else { return nil } return Graphics.encode(thumbnail, compressionQuality: 0.9) }