← すべての記事

GenmojiとNSAdaptiveImageGlyph:iOS 18+でのインライン絵文字

GenmojiはApple Intelligenceのカスタム絵文字機能におけるユーザー向けの名称です。ユーザーが説明を入力すると、システムがインラインの絵文字風画像を生成し、Unicode絵文字と並んでテキスト中に表示されます。開発者向けのインターフェースはNSAdaptiveImageGlyphで、属性付きテキスト内のアダプティブなインライン画像を表現するiOS 18+のクラスです1。GenmojiはNSAdaptiveImageGlyphインスタンスのソースの一つであり、属性付きテキストを扱うアプリでは、Genmoji(およびAppleが今後導入する可能性のあるアダプティブイメージグリフコンテンツ)を表示するためにこのクラスへの対応が必要となります。

TL;DR

  • NSAdaptiveImageGlyph(iOS 18+)は、アダプティブな画像と識別用メタデータをラップするデータ型です。システムキーボードからのGenmoji入力は、属性付きテキストに埋め込まれたNSAdaptiveImageGlyphインスタンスとして到着します2
  • supportsAdaptiveImageGlyphUITextInputプロトコルで宣言されています。UITextViewはこのプロトコルに準拠しているため、このプロパティはtextView.supportsAdaptiveImageGlyph = trueとして設定可能です。Appleの説明は明確で、このプロパティがfalseの場合「入力システムはテキスト入力にアダプティブ画像を含めることを許可しません」となります。Genmojiをレンダリングするには、アプリ側で明示的にオプトインする必要があります。
  • アダプティブイメージグリフにはTextKit 2が必須です。TextKit 1のままのアプリではNSAdaptiveImageGlyphが正しくレンダリングされません。iOS 18+を対象とする新規アプリは、デフォルトでTextKit 2を使用すべきです。
  • NSAttributedStringNSAttributedString.Key.adaptiveImageGlyph属性を介してアダプティブイメージグリフを保持します。イニシャライザNSAttributedString(adaptiveImageGlyph:attributes:)は単一のグリフを含む属性付き文字列を構築します3
  • 永続化とラウンドトリップには注意が必要です。プレーンテキストでの保存はGenmojiを完全に削除します。リッチテキスト形式(RTFD、拡張機能付きMarkdown、画像データを埋め込んだHTML)であれば保持されます。

NSAdaptiveImageGlyphが保持するもの

NSAdaptiveImageGlyphは4つの識別プロパティを持つデータラッパーです2

  • imageContent: Data —— 画像データ本体で、contentTypeで宣言された形式で格納されます。
  • contentIdentifier: String —— グリフインスタンスの一意の識別子。重複排除やシステム内部のキャッシュに使用されます。
  • contentDescription: String —— グリフを説明する代替テキスト。アクセシビリティラベルを表示するアプリや、グリフ非対応の受信者にグリフを送信するアプリで使用されます。
  • contentType: UTType —— Appleがアダプティブグリフに使用する画像形式(HEICのバリアント)を公開するクラスレベルの型プロパティです。シリアライズを行うアプリは、形式を意識した処理を行うためにこれを参照します。

データサイズは標準的なGenmojiで通常数十キロバイトです。HEICのアダプティブイメージ機能を使用して、複数のサイズが同一の画像ファイル内にエンコードされており、システムはレンダリングコンテキストに応じて適切なサイズを選択します。

UITextViewでアダプティブイメージグリフを有効にする

オプトインは単一のプロパティで完結します1

import UIKit

let textView = UITextView()
textView.supportsAdaptiveImageGlyph = true
// TextKit 2 is required; modern UITextView usage gets it by default
// (see: developer.apple.com/documentation/uikit/using-textkit-2-to-interact-with-text)

supportsAdaptiveImageGlyph = trueを設定しないと、ユーザーが入力したGenmojiはプレースホルダー文字として表示されます(システムがグリフをレンダリングできないため)。このプロパティを設定すると、レンダリングが有効になり、システムキーボードの「Genmoji」タブも表示されるため、ユーザーはテキストビュー内でカスタムGenmojiを作成できるようになります。

SwiftUIのネイティブTextFieldTextEditorは、現時点ではsupportsAdaptiveImageGlyphモディファイアを公開していません。アダプティブイメージグリフのレンダリングが必要なSwiftUIアプリでは、UIViewRepresentableUITextViewをラップし、内部のビューに対してsupportsAdaptiveImageGlyph = trueを設定します。GlyphMeThatのようなコミュニティ製のラッパーは、このブリッジを既製で提供しています。

TextKit 2が要となる

NSAdaptiveImageGlyphにはTextKit 2のレイアウトアーキテクチャが必須です4。TextKit 1(オリジナルのNSTextStorage/NSLayoutManager/NSTextContainerモデルで提供されたレガシーなテキストエンジン)はアダプティブイメージグリフを正しくレンダリングできません。グリフは汎用のプレースホルダーとして表示されるか、レイアウト自体が失敗します。

アプリは次の3つの状態のいずれかにあります。

iOS 18+の新規アプリ。 デフォルトでTextKit 2を使用します。Interface Builderまたはinit(frame:textContainer:)で初期化されたUITextViewは、iOS 16+ではデフォルトでTextKit 2を使用します4。新しいコードでは無償でこの恩恵を受けられます。

TextKit 1を使い続けるレガシーアプリ。 マイグレーションが必要となります。NSLayoutManagerをサブクラス化したり、レイアウト関連のデリゲートメソッドをオーバーライドしたり、古いNSTextStorageを直接使用したりしているアプリにとって、TextKit 2へのマイグレーションは決して簡単ではありません。AppleのTextKit 2のドキュメントが現代的なレイアウトアーキテクチャを解説しています。シンプルなUITextViewの使い方をしているアプリであれば、マイグレーションはほぼ自動です。

ハイブリッドアプリ。 一部のアプリはHTML編集用にWKWebViewを、プレーン編集用にUITextViewを併用しています。WKWebViewは独自のレンダリングパス(TextKitを介さない)でアダプティブイメージグリフを処理するため、ハイブリッドアプリではGenmojiをサポートする画面と削除してしまう画面が混在する可能性があります。この挙動はドキュメント化しましょう。一方のエディタはカスタム絵文字をサポートし、もう一方は削除する —— ユーザーはそれに必ず気づきます。

NSAttributedStringとの統合

アダプティブイメージグリフはNSAttributedString.Key.adaptiveImageGlyph属性を介して属性付き文字列を流れます3

import UIKit

// Construct an attributed string containing a single adaptive image glyph
let glyph: NSAdaptiveImageGlyph = ...
let attrString = NSAttributedString(
    adaptiveImageGlyph: glyph,
    attributes: [
        .font: UIFont.systemFont(ofSize: 17)
    ]
)

// Concatenate with surrounding text
let composed = NSMutableAttributedString(string: "Look at this ")
composed.append(attrString)
composed.append(NSAttributedString(string: " I just made!"))

このパターンは構成可能です ——テキストの中にグリフがあり、さらにそれが別のテキストの中にある、という構造です。レイアウト(周囲のテキストのフォントに合わせたグリフのアダプティブサイジングを含む)はシステムが自動的に処理します。

読み取りでは、NSAttributedString.adaptiveImageGlyph属性を反復処理することで、グリフが出現する位置でNSAdaptiveImageGlyphインスタンスを取得できます。

attributedString.enumerateAttribute(
    .adaptiveImageGlyph,
    in: NSRange(location: 0, length: attributedString.length)
) { value, range, _ in
    if let glyph = value as? NSAdaptiveImageGlyph {
        // process glyph + range
    }
}

テキストをフィルタリング、変換、永続化するアプリは、この列挙を用いてグリフを見つけ、それをどう扱うかを決定します。

永続化とシリアライズ

プレーンテキストでの保存(String、UTF-8ファイル)はアダプティブイメージグリフを保持しません。属性付きテキスト内でグリフを表すUnicodeプレースホルダー文字はU+FFFC(オブジェクト置換文字)または何も無しとしてシリアライズされ、実際のグリフデータは失われます。

ラウンドトリップ可能な永続化のためには、リッチテキスト形式が必要です5

RTFD。 Appleのリッチテキスト+添付ファイル形式です。アダプティブイメージグリフをラウンドトリップできます。Notes、Mail(リッチコンテンツ送信時)、TextEditで使用されています。形式は冗長(添付ファイル付きのディレクトリバンドル)ですが、可逆です。

画像を埋め込んだHTML。 Webフレンドリーです。グリフはbase64エンコードされたデータURIを持つ<img>タグとしてシリアライズされます。ペイロードは大きくなりますが、ほとんどのリッチテキスト対応の受信者で機能します。

拡張機能付きMarkdown。 標準のMarkdownにはアダプティブイメージグリフ用の構文はありませんが、拡張方言(添付ファイル対応のCommonMark、Apple独自の拡張Markdown)であればグリフを保持できます。Markdownベースの永続化を採用する場合は、その方言要件をドキュメント化しましょう。

ネットワーク経由でテキストを送信するアプリ(チャット、メール、ソーシャルメディア)は、次のいずれかを選ぶ必要があります。グリフをエンドツーエンドで保持する(送信者と受信者の双方がiOS 18+で、トランスポートがリッチテキストをサポートしている場合のみ機能)、グリフを削除してフォールバックテキスト(contentDescription)に置き換える、またはグリフをシステム画像としてレンダリングしてその画像を埋め込む、という選択肢です。適切な選択は対象オーディエンスとプラットフォームに依存します。

よくある失敗

NSAdaptiveImageGlyphの採用では、3つの失敗パターンが繰り返し見られます。

supportsAdaptiveImageGlyph = trueを設定し忘れる。 最も多いバグです。テキストビューはUnicode絵文字を問題なくレンダリングするのに、Genmojiはプレースホルダー文字として表示されてしまいます。修正方法:すべてのUITextView(UIKit)、またはNSTextView(AppKit)のようなNSTextInputClient準拠オブジェクトに対して、このプロパティをtrueに設定します。SwiftUIにはネイティブのモディファイアが存在しないため、UIViewRepresentable(macOSではNSViewRepresentable)でUITextViewをラップし、内部のビューに対してプロパティを設定してください。

プレーンテキストでの永続化がグリフを削除する。 テキストビューのコンテンツをtext(プレーンなString)として保存するとGenmojiが破棄されます。ユーザーがカスタム絵文字を入力し、テキストビューで確認し、ドキュメントを保存して再オープンすると —— 絵文字は消えています。修正方法:アダプティブイメージグリフをサポートするリッチテキスト形式(RTFD、HTML、添付ファイルのサイドチャネルを持つカスタム形式)でattributedTextとして永続化してください。

ネットワーク送信でグリフが静かに消失する。 送信メッセージをプレーンテキストとしてシリアライズするメッセージングアプリは、送信時にGenmojiを削除してしまいます。受信者にはプレースホルダー文字や空白が表示されます。修正方法:リッチコンテンツを送信する(受信者がそれをサポートしていることを確認)、もしくはプレーンテキスト受信者向けにはcontentDescriptionを代用し、画像データは別途添付として含める、のいずれかを選びます。

このパターンがiOS 18+アプリにとって意味すること

3つの要点を挙げます。

  1. すべてのテキスト入力でsupportsAdaptiveImageGlyph = trueを設定する。 ユーザー入力テキストを受け付けるアプリは、アダプティブイメージグリフへのオプトインをデフォルトとすべきです。たった1つのプロパティが、Genmojiが正しくレンダリングされるか、それとも壊れるかの分かれ目となります。

  2. TextKit 1のままならTextKit 2へ移行する。 TextKit 1はメンテナンスモードに入っています。iOS 26世代の新機能(アダプティブイメージグリフ、Writing Toolsのインラインリライト、Liquid Glassのテキストレンダリング)はすべてTextKit 2を前提としています。マイグレーションコストは現実問題として無視できませんが、代替案は非推奨のテキストエンジンで出荷し続けることです。

  3. 永続化形式はアダプティブイメージグリフを念頭に選ぶ。 ネイティブiOSストレージにはRTFD、Web互換ストレージには画像を埋め込んだHTML、ハイパフォーマンスアプリには添付サイドチャネル付きのカスタムバイナリ形式が適しています。ユーザーがGenmojiを入力するようなアプリにとって、プレーンテキストは適切なデフォルトではありません。

Apple Ecosystemクラスタの全体像:Writing ToolsはApple Intelligenceの並行するテキストインターフェース、Image Playgroundは画像生成、App Intents 2.0はiOS 26のApple Intelligence統合、Foundation ModelsはオンデバイスLLMを扱います。ハブはApple Ecosystem Seriesにあります。AIエージェントとiOSの広範なコンテキストについては、iOS Agent Development guideをご覧ください。

FAQ

UITextViewを使うだけでGenmojiが「無料で」使えるのですか?

そうとも限りません。UITextView.supportsAdaptiveImageGlyphのデフォルトはfalseです。アプリはこのプロパティをtrueに設定して明示的にオプトインする必要があります。一度有効にすると、ユーザーにシステムキーボードのGenmojiタブが表示され、ペーストされたGenmojiも正しくレンダリングされます。オプトインしない場合、別の場所で入力されてテキストビューにペーストされたGenmojiはプレースホルダー文字として表示されます。

Genmojiをテストするには、Apple Intelligenceを有効化する必要がありますか?

完全なGenmoji作成には必要です。ユーザー向けのGenmoji作成フローには、Apple Intelligence対応ハードウェア(iPhone 15 Pro以降、Mシリーズ Mac)でiOS 18+とApple Intelligenceの有効化が必要です。NSAdaptiveImageGlyphのレンダリングを開発テストするだけであれば、サンプル画像データを使ってテスト用のグリフインスタンスをプログラムで構築し、iOS 18+の任意のデバイスやシミュレータでテキストビューのレンダリングを検証できます。

iOS 17のユーザーにGenmojiを送信するとどうなりますか?

グリフを保持するリッチテキストトランスポートがない場合、受信者にはcontentDescription(代替テキスト)またはプレースホルダー文字が表示されます。最新のメッセージングフレームワーク(AppleのMessagesアプリ、最近のメールクライアント)はフォールバックを自動的に処理しますが、独自プロトコルでは明示的な処理が必要です。

NSAdaptiveImageGlyphインスタンスをプログラムで作成できますか?

可能です。パブリックなイニシャライザはinit(imageContent: Data)で、事前にエンコードされたHEICアダプティブ画像データを受け取ります。contentIdentifiercontentDescriptionはエンコードされたペイロードから読み取られるインスタンスプロパティです。contentTypeはAppleがアダプティブグリフに使用する形式(HEICのバリアント)を表すクラスレベルのUTTypeであり、インスタンスごとには持ちません。カスタムのアダプティブイメージグリフを作成するアプリは、グリフごとの識別子と説明を埋め込んだHEICペイロードを準備し、そのデータからグリフを構築します。WWDC 2024 セッション10220「Bring expression to your app with Genmoji」が完全な作成フローを解説しています。

Writing Toolsとはどのように相互作用しますか?

Writing Tools(Writing Tools APIで解説)はリライト出力でアダプティブイメージグリフを保持します。ユーザーがGenmojiを含むテキストを選択してWriting Toolsにリライトを依頼すると、意味的に適切な位置でGenmojiが保持されたリライトが返されます。UIWritingToolsCoordinatorを介してWriting Toolsに参加するアプリは、独自のテキストストレージを通じてNSAdaptiveImageGlyphインスタンスを正しくラウンドトリップさせる必要があります。

NSAdaptiveImageGlyphNSTextAttachmentの違いは何ですか?

NSTextAttachmentは属性付きテキスト内のインライン非テキストコンテンツ(画像、ファイル、カスタム描画)に対応する、より古く広範な添付システムです。NSAdaptiveImageGlyphは、周囲のフォント特性に適応する絵文字風のインライン画像に特化したiOS 18の特殊型です。両者とも属性付き文字列の属性を介して付加されますが、異なるキー(.attachment.adaptiveImageGlyph)と異なるレンダリングパス(TextKit 1+TextKit 2 と TextKit 2 のみ)を使用します。Genmojiスタイルのコンテンツを対象とする新しいコードではNSAdaptiveImageGlyphを使用してください。

参考文献


  1. Apple Developer Documentation:supportsAdaptiveImageGlyphUITextInputプロトコルで宣言され、UITextViewが準拠するオプトインプロパティ。同じプロパティがtextView.supportsAdaptiveImageGlyphとしてアクセス可能となります。 

  2. Apple Developer Documentation:NSAdaptiveImageGlyph。画像コンテンツ、識別子、説明、コンテンツタイプをラップするデータ型です。 

  3. Apple Developer Documentation:NSAttributedString.Key.adaptiveImageGlyphおよびNSAttributedString(adaptiveImageGlyph:attributes:)。アダプティブイメージグリフの属性付き文字列統合インターフェースです。 

  4. Apple Developer Documentation:TextKitおよびUsing TextKit 2 to interact with text。現行のTextKit 2エントリポイント。アダプティブイメージグリフのレンダリングはTextKit 2のレイアウトアーキテクチャに依存します。 

  5. Apple Developer Documentation:NSAttributedString.DocumentType。アダプティブイメージグリフを永続化を介してラウンドトリップするためにサポートされるリッチテキスト形式(RTFD、HTMLなど)。 

関連記事

Image Playground API:SwiftUIシート、プログラムによるImage Creator、そしてスタイル制御

Image Playgroundはアプリに2つの経路を提供します。SwiftUIのimagePlaygroundSheetモディファイアと、コンセプトから画像を生成するプログラム的なImageCreator APIです。

2 分で読める

AppleのTranslationフレームワーク:無料、オンデバイス、そして見た目以上に切れ味鋭い

AppleのTranslationフレームワーク——translationPresentationとTranslationSessionによる無料のオンデバイス翻訳、そしてデモが省略するオフラインダウンロードの落とし穴まで。

1 分で読める

ループ・エンジニアリング――検証が安いところでループは勝つ

ループ・エンジニアリングを、Boris Cherny の全文書き起こしと照合する。彼が名前を挙げるループはどれも検証が安い。その制約こそが、何を自動化すべきかを決める。

4 分で読める