21

I have an extension to convert HTML string to NSAttributedString.

After iOS 15 is released, I've seen a lot of crashes from my user. All those crashes are on iOS 15 and occur on the main thread.

Here is a typical crash report from Crashlytics.

Crashed: com.apple.main-thread
0  libsystem_platform.dylib       0x1f29d60c0 _os_unfair_lock_recursive_abort + 36
1  libsystem_platform.dylib       0x1f29d0a10 _os_unfair_lock_lock_slow + 304
2  Foundation                     0x183d5e730 -[NSProcessInfo(NSProcessInfoHardwareState) isLowPowerModeEnabled] + 68
3  WebCore                        0x192011004 <redacted> + 56
4  CoreFoundation                 0x182535ee8 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 28
5  CoreFoundation                 0x1825d1b9c ___CFXRegistrationPost_block_invoke + 52
6  CoreFoundation                 0x1825a4f54 _CFXRegistrationPost + 456
7  CoreFoundation                 0x18254bd54 _CFXNotificationPost + 716
8  Foundation                     0x183d50028 -[NSNotificationCenter postNotificationName:object:userInfo:] + 96
9  Foundation                     0x183dc89d4 NSProcessInfoNotifyPowerState + 188
10 Foundation                     0x183d5e768 -[NSProcessInfo(NSProcessInfoHardwareState) isLowPowerModeEnabled] + 124
11 WebCore                        0x191595118 <redacted> + 96
12 WebCore                        0x19201116c WebCore::LowPowerModeNotifier::LowPowerModeNotifier(WTF::Function<void (bool)>&&) + 52
13 WebCore                        0x192f36c4c WebCore::Page::Page(WebCore::PageConfiguration&&) + 1848
14 WebKitLegacy                   0x1a605ae34 -[WebView(WebPrivate) _commonInitializationWithFrameName:groupName:] + 3060
15 WebKitLegacy                   0x1a605a214 -[WebView(WebPrivate) _initWithFrame:frameName:groupName:] + 116
16 UIFoundation                   0x18d306028 -[NSHTMLReader _loadUsingWebKit] + 832
17 UIFoundation                   0x18d30715c -[NSHTMLReader attributedString] + 32
18 UIFoundation                   0x18d2c04e8 _NSReadAttributedStringFromURLOrData + 8420
19 UIFoundation                   0x18d2be378 -[NSAttributedString(NSAttributedStringUIFoundationAdditions) initWithData:options:documentAttributes:error:] + 156
20 MyAppExtensions                 0x108aa0fc4 _hidden#912_ + 4374482884 (__hidden#360_:4374482884)
21 MyAppExtensions                 0x108aa0894 NSAttributedString.init(htmlString:font:useDocumentFontSize:) + 36 (__hidden#925_:36)
22 MyAppName                           0x1045eca18 {foo_class}.convertHTML(_:) + 670 (xxxx.swift:670)
23 MyAppName                           0x1045e5c58 {foo_class}.some_function_3 + 620 (xxxx.swift:620)
24 MyAppName                           0x1045e4760 {foo_class}.some_function_2 + 212 (xxxx.swift:212)
25 MyAppName                           0x1045e4200 {foo_class}.some_function_1 + 138 (xxxx.swift:138)
.
.
.
42 UIKitCore                      0x184b5e1d0 _UIGestureRecognizerSendTargetActions + 116
43 UIKitCore                      0x184b2705c _UIGestureRecognizerSendActions + 284
44 UIKitCore                      0x184b60580 -[UIGestureRecognizer _updateGestureForActiveEvents] + 636
45 UIKitCore                      0x184b186fc _UIGestureEnvironmentUpdate + 1988
46 CoreFoundation                 0x18254c570 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36
47 CoreFoundation                 0x18251b854 __CFRunLoopDoObservers + 572
48 CoreFoundation                 0x1825168ec __CFRunLoopRun + 1052
49 CoreFoundation                 0x18252a3c8 CFRunLoopRunSpecific + 600
50 GraphicsServices               0x19dd3b38c GSEventRunModal + 164
51 UIKitCore                      0x184ed00bc -[UIApplication _run] + 1100
52 UIKitCore                      0x184c4dbe8 UIApplicationMain + 2124
53 libswiftUIKit.dylib            0x199afe184 UIApplicationMain(_:_:_:_:) + 104
54 MyAppName                      0x1043a5894 main + 7 (main.swift:7)
55 dyld                           0x106b81a24 start

The following is code of converting HTML to NSAttributedString:

extension NSAttributedString {
  convenience public init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = true) throws {
    let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
      .documentType: NSAttributedString.DocumentType.html,
      .characterEncoding: String.Encoding.utf8.rawValue
    ]

    guard let data = html.data(using: .utf8, allowLossyConversion: true) else {
      self.init(string: "")
      return
    }

    guard let fontFamily = font?.familyName else {
      try self.init(data: data, options: options, documentAttributes: nil)
      return
    }

    let attr = try NSMutableAttributedString(data: data, options: options, documentAttributes: nil)

    let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize
    let range = NSRange(location: 0, length: attr.length)
    attr.enumerateAttribute(.font, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in
      if let htmlFont = attrib as? UIFont {
        let traits = htmlFont.fontDescriptor.symbolicTraits
        var descrip = htmlFont.fontDescriptor.withFamily(fontFamily)

        if (traits.rawValue & UIFontDescriptor.SymbolicTraits.traitBold.rawValue) != 0 {
          descrip = descrip.withSymbolicTraits(.traitBold)!
        }

        if (traits.rawValue & UIFontDescriptor.SymbolicTraits.traitItalic.rawValue) != 0 {
          descrip = descrip.withSymbolicTraits(.traitItalic)!
        }

        attr.addAttribute(.font, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range)
      }
    }

    self.init(attributedString: attr)
  }

Btw, I'm using Xcode 12.5 to archive the release build.

Raptor
  • 53,206
  • 45
  • 230
  • 366
Chen Zheng
  • 211
  • 2
  • 4
  • 3
    I don't have an answer yet, but we build SDKs, and one of our customers has seen this exact crash because our SDK is using the low power mode notifications. We believe this is definitely an iOS 15 bug, but don't have any definite ways to reproduce it yet. I'll post back if we do find anything. – CPR Sep 28 '21 at 17:36
  • 1
    @CPR Thanks. I am still trying to reproduce it and find out if there is a way to avoid it. – Chen Zheng Sep 28 '21 at 21:27
  • 1
    I'm getting exactly the same crash here. I haven't been able to reproduce it yet. Confirming that the crash only happens on iOS 15, according to Firebase logs. – Leonardo Bortolotti Oct 04 '21 at 14:05
  • Any luck with reproducing above issue? – Yashwanth Reddy Oct 05 '21 at 18:03
  • Same issue happening to my app, still not been able to replicate. – kd02 Oct 07 '21 at 09:33
  • I am also facing this issue on iOS 15 only. Any luck with reproducing this issue @ChenZheng – Anuj Vats Oct 09 '21 at 14:31
  • 1
    Do you have HTML string? Could you post it here as well? – Ramis Oct 10 '21 at 11:37
  • I am also facing same issue but cannot reproduce it, crash logs are only from firebase.... any luck? – Fast Coderz Nov 06 '21 at 07:39

1 Answers1

2

Discussion about this crash in the apple developer forum here:

You might need to avoid using NSAttributedString(data: , options: [.documentType: NSAttributedString.DocumentType.html] itself and parse the HTML string to generate attribute string by yourself. Perhaps,you can use some open-source projects on GitHub such as DTCoreText.

Shmiel
  • 1,201
  • 10
  • 25
Daemonson Dong
  • 182
  • 3
  • 11