AttributedString のスコープに含まれる属性キーを列挙する
AttributedString Scope Enumeration
このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら↗。
01 何が問題だったのか
AttributedString で扱う属性キー(AttributedStringKey)は、用途ごとに「スコープ」としてまとめて宣言します。AttributeScope に適合する型に属性キーをプロパティとして並べておくと、シリアライズ・デシリアライズや変換系 API にスコープ単位で渡せて、扱う属性の集合をまとめて切り替えたり、外部から渡されるアーカイブで許可する型を絞ったりできます。サードパーティ製の属性を Foundation に教えるための拡張ポイントとしても使われます。
スコープが特に活躍する場面のひとつが、Swift と Objective-C の橋渡しです。AttributedString と NSAttributedString、あるいは AttributeContainer と Dictionary<NSAttributedString.Key, Any> のあいだを変換するヘルパーが Foundation には用意されており、そこではスコープが「どの属性を運ぶか」の指定として活躍します。
ところが、SDK の中には、属性「値」のコレクションではなく属性「キー」の集合を取り扱う API も少なくありません。代表的なのが Set<NSAttributedString.Key> を引数に取るタイプの API で、たとえばテキストフィールドで使用を許可する属性キーをまとめて指定する、といった用途で見られます。AttributeScope も実体としては「属性キーの集まり」を表しているのに、Foundation には AttributeScope から Set<NSAttributedString.Key> を組み立てるための公式な手段がなく、同じ「キーの集合」という抽象が Swift 側と Objective-C 側で接続できない状態でした。AttributedString/AttributeScope を入り口にした新しい API を作るたびに、この橋渡しをクライアント側で書き起こす必要があったわけです。
02 どのように解決されるのか
AttributeScope に、スコープに含まれる属性キーの型を列挙するための static プロパティ attributeKeys が追加されます。FoundationPreview 6.2 以降で利用できます。
@available(FoundationPreview 6.2, *)
extension AttributeScope {
public static var attributeKeys: some Sequence<any AttributedStringKey.Type> { get }
}
戻り値の要素は、属性キーを表す AttributedStringKey の型そのもの(メタタイプ)です。some Sequence<any AttributedStringKey.Type> という不透明な戻り型になっているため、各キーの name などのプロパティへ素直にアクセスできる一方で、Foundation 側が将来的にスコープの走査やキャッシュの実装を差し替えても API/ABI を壊さずに済むようになっています。
Set<NSAttributedString.Key> への橋渡し
このプロパティのいちばんの用途は、AttributeScope から Set<NSAttributedString.Key> を組み立てて、既存の Objective-C ベースの API に渡すことです。たとえば、許可する属性キーを Set<NSAttributedString.Key> で受け取るテキストビューに、AttributeScope を入り口にした新しいオーバーロードを生やすケースが想定されています。
struct MyTextView {
// 既存の NSAttributedString ベースの実装
public mutating func setAllowedAttributes(_ keys: Set<NSAttributedString.Key>) {
// ... 既存の Objective-C ベースの実装 ...
}
// AttributeScope を入り口にする新しいオーバーロード
public mutating func setAllowedAttributes(_ scope: some AttributeScope.Type) {
let allowedNSKeys = scope.attributeKeys.map {
NSAttributedString.Key($0.name)
}
self.setAllowedAttributes(Set(allowedNSKeys))
}
}
attributeKeys から得た各属性キーの name を使って NSAttributedString.Key を組み立てることで、Swift 側のスコープと Objective-C 側のキー集合を直接つなげられます。
キーの絞り込み
要素は属性キーの型そのものなので、AttributedStringKey が公開しているプロパティで絞り込むこともできます。たとえば「テキストを追記したときに引き継がれる属性」だけを許可したい場合は、inheritedByAddedText が true のキーだけを取り出せます。
public func setAllowedAttributes(_ scope: some AttributeScope.Type) {
let allowedNSKeys = scope.attributeKeys.filter {
$0.inheritedByAddedText
}.map {
NSAttributedString.Key($0.name)
}
self.setAllowedAttributes(Set(allowedNSKeys))
}
これまで通り、単純なシリアライズや変換であれば既存の Codable ベースの API や NSAttributedString/AttributeContainer の変換 API を使えば十分で、それらは内部でスコープを走査します。attributeKeys は、それらの既存 API では表現しきれない、キー集合そのものを必要とする橋渡しに使うための拡張ポイントとして位置づけられています。