[BUG] NSInternalInconsistencyException crash in `NSAttributedString+HTML.swift:245`
Summary
This is a fresh crash in Stripe Identity SDK.
Code to reproduce
N/A
iOS version
iOS 17
Installation method
SPM
SDK version
23.29.1
Other information
NSInternalInconsistencyException: unexpected start state
0 CoreFoundation +0x83f1c ___exceptionPreprocess
1 libobjc.A.dylib +0x172b4 _objc_exception_throw
2 Foundation +0x6de930 -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:]
3 UIKitCore +0x1c5810 __UIApplicationDrainManagedAutoreleasePool
4 CoreFoundation +0x24654 ___CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
5 CoreFoundation +0x24410 ___CFRunLoopDoObservers
6 CoreFoundation +0x52d20 _CFRunLoopRunSpecific
7 UIFoundation +0xdbe04 -[NSHTMLReader _loadUsingWebKit]
8 UIFoundation +0xdc9b4 -[NSHTMLReader attributedString]
9 UIFoundation +0x9c5c8 __NSReadAttributedStringFromURLOrDataCommon
10 UIFoundation +0x9793c __NSReadAttributedStringFromURLOrData
11 UIFoundation +0x977b4 -[NSAttributedString(NSAttributedStringUIFoundationAdditions) initWithData:options:documentAttributes:error:]
12 openphone +0x7023d4 NSMutableAttributedString.init(htmlText:style:) (NSAttributedString+HTML.swift:245:18)
13 openphone +0x703408 specialized static NSAttributedString.createHtmlString(htmlText:style:) (NSAttributedString+HTML.swift:202:37)
14 openphone +0x79738c HTMLTextView.configure(with:) (<compiler-generated>)
15 openphone +0x7708d8 IdentityFlowViewController.configure(backButtonTitle:viewModel:) (IdentityFlowView.swift:349:42)
16 openphone +0x75b3e4 BiometricConsentViewController.scrollViewFullyLaiedOut(_:) (BiometricConsentViewController.swift:164:9)
17 openphone +0x795278 protocol witness for IdentityFlowViewDelegate.scrollViewFullyLaiedOut(_:) in conformance BiometricConsentViewController (<compiler-generated>)
18 openphone +0x7952e4 @objc IdentityFlowView.layoutSubviews() (<compiler-generated>)
19 UIKitCore +0x10914 -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
20 QuartzCore +0x7f268 CA::Layer::layout_if_needed(CA::Transaction*)
21 QuartzCore +0x7edec CA::Layer::layout_and_display_if_needed(CA::Transaction*)
22 QuartzCore +0xd9fd4 CA::Context::commit_transaction(CA::Transaction*, double, double*)
23 QuartzCore +0x4eedc CA::Transaction::commit()
24 UIKitCore +0xec897c ___83-[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]_block_invoke_4
25 UIKitCore +0xec80ac -[UIApplication _performWithUICACommitStateSnapshotting:]
26 UIKitCore +0xec88ec ___83-[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]_block_invoke_2
27 UIKitCore +0x95430 +[UIView(Animation) performWithoutAnimation:]
28 UIKitCore +0xec86d8 ___83-[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]_block_invoke
29 UIKitCore +0x693c64 -[UIScene _applyOverrideSettings:forActions:]
30 UIKitCore +0x10ad920 -[UIWindowScene _applySnapshotSettings:forActions:]
31 UIKitCore +0xec85b8 -[UIApplication _createSnapshotContextForScene:withName:performLayoutWithSettings:]
32 UIKitCore +0xec97a0 ___65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_3
33 FrontBoardServices +0x6b074 -[FBSSceneSnapshotAction _executeNextRequest]
34 FrontBoardServices +0x6b090 -[FBSSceneSnapshotAction _executeNextRequest]
35 FrontBoardServices +0x6ab08 -[FBSSceneSnapshotAction executeRequestsWithHandler:completionHandler:expirationHandler:]
36 UIKitCore +0xec96f0 ___65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_2
37 UIKitCore +0xec8fa4 -[UIApplication _beginSnapshotSessionForScene:withSnapshotBlock:]
38 UIKitCore +0xec95a0 ___65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke
39 UIKitCore +0x693b70 -[UIScene _enableOverrideSettingsForActions:]
40 UIKitCore +0x693d88 -[UIScene _performSystemSnapshotWithActions:]
41 UIKitCore +0xec941c -[UIApplication _performSnapshotsWithAction:forScene:completion:]
42 UIKitCore +0x1319988 ___98-[_UISceneSnapshotBSActionsHandler _respondToActions:forFBSScene:inUIScene:fromTransitionContext:]_block_invoke_3
43 UIKitCore +0x131980c ___98-[_UISceneSnapshotBSActionsHandler _respondToActions:forFBSScene:inUIScene:fromTransitionContext:]_block_invoke_2
44 UIKitCore +0x237f80 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:]
45 UIKitCore +0x302f44 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:]
46 UIKitCore +0x302b24 -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:]
47 FrontBoardServices +0x66e0 -[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:]
48 FrontBoardServices +0x16840 ___94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke_2
49 FrontBoardServices +0x166c8 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
50 FrontBoardServices +0x1b7f8 ___94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke
51 libdispatch.dylib +0x3dd0 __dispatch_client_callout
52 libdispatch.dylib +0x7868 __dispatch_block_invoke_direct
53 FrontBoardServices +0x17d54 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
54 FrontBoardServices +0x17cd4 -[FBSMainRunLoopSerialQueue _targetQueue_performNextIfPossible]
55 FrontBoardServices +0x17bac -[FBSMainRunLoopSerialQueue _performNextFromRunLoopSource]
56 CoreFoundation +0x56830 ___CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
57 CoreFoundation +0x567c4 ___CFRunLoopDoSource0
58 CoreFoundation +0x54294 ___CFRunLoopDoSources0
59 CoreFoundation +0x53480 ___CFRunLoopRun
60 CoreFoundation +0x52cd4 _CFRunLoopRunSpecific
61 GraphicsServices +0x11a4 _GSEventRunModal
62 UIKitCore +0x40aae4 -[UIApplication _run]
63 UIKitCore +0x4bed94 _UIApplicationMain
64 openphone +0x4e74a8 main (AppDelegate.swift:20:21)
65 dyld +0x3d150 start
nalexn can you please add steps to reproduce this issue?
After spending some time investigating this, I think this is likely caused by a temporary content issue coming from identity/verification_pages/ and is not reproducible by me.
This issue happens if you transition into background, the system wants to take a snapshot (UIApplication _performSnapshotsWithAction), which triggers layoutSubviews on UICollectionView / UITableView (IdentityFlowView.layoutSubviews()). If it happens that one of your cells loads HTML into NSAttributedString then the crash occurs.
It's SDK bug and most likely we can't do anything about it, but in my case, the only workaround I have found is to prevent layoutSubviews in the background:
final class FixedCollectionView: UICollectionView {
override func layoutSubviews() {
guard UIApplication.shared.applicationState != .background else { return }
super.layoutSubviews()
}
}
It's likely that preventing HTML loading would also fix it, but then you'd probably need to refresh your collection view after returning to the foreground.