LoginSignup
8
6

More than 5 years have passed since last update.

HTML -> NSAttributedString はメインスレッドで実行しよう

Posted at

HTML文字列をNSAttributedStringに変換する

iOS/macOS で装飾付きテキストを扱うことができる NSAttributedString はイニシャライザ init(data:options:documentAttributes:) により HTML テキストから変換することができます。下記がサンプルコードと結果です。

Swift
import Foundation
import UIKit

let htmlString = """
<html>
<body>
<p><strong>H</strong>ello, <strong>W</strong>orld!<p>
</body>
</html>
"""
let htmlData = htmlString.data(using: .utf8)!
let attributedText = try? NSAttributedString(data: htmlData,
                                             options: [.documentType: NSAttributedString.DocumentType.html],
                                             documentAttributes: nil)

結果
result.png

バックグラウンドスレッドでは実行してはいけない

このイニシャライザはバックグラウンドスレッドでは実行してはいけません。これは下記のようにドキュメントに記述されています。

The HTML importer should not be called from a background thread (that is, the options dictionary includes documentType with a value of html). It will try to synchronize with the main thread, fail, and time out. Calling it from the main thread works (but can still time out if the HTML contains references to external resources, which should be avoided at all costs).

バックグラウンドスレッドで実行するとどうなるか?

ほとんどのケースでは特に問題なく成功するように見えます。ただし、稀にアプリケーションはクラッシュしてしまいます。このクラッシュレポートを確認すると、WebKit に含まれる WebCore モジュールの中でクラッシュしていることがわかります。つまり変換には WebKit が使われているということですね。

NSAttributedString 自体は Foundation モジュールにありますが、このイニシャライザは UIKit/AppKit で追加されているものです。僕は Foundation のものだからと安易にバックグラウンドで処理してしまったがためにこのクラッシュに遭遇しました。他にも Foundation のクラスに対して UIKit/AppKit が生やしたメソッドはありますので、その場合は UIKit と同様に実行スレッドを注意しておいたほうが良いでしょう。

これは、Xcode 9 で追加された Main Thread Checker でも警告されませんし、この時発生する例外は Swift の do-catch ブロックでは捕捉することができないもの(未確認ですが NSException と思われます)なので発見が遅れてしまいました。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6