【iOS】 オンデバイスLLMを活用し、オリジナル絵文字(ジェン文字)を生成しよう。try! Swift Tokyo 2026のトーク解説

【iOS】 オンデバイスLLMを活用し、オリジナル絵文字(ジェン文字)を生成しよう。try! Swift Tokyo 2026のトーク解説

2026.04.21

世界中から開発者が集まる日本最大級のSwiftのカンファレンスtry! Swift Tokyo 2026で登壇してきました。

今年の会場は去年に引き続き、立川ステージガーデンで開催され、前日は、モーニング娘。’26さんが同会場でコンサートしていたらしく、まさか偉大なアイドルさんと同じ舞台の上に立てるとは思いませんでした。

登壇内容

Genmojiと私たちはどう生きるのかというテーマで登壇させていただきました。
Apple Intelligenceを活用した画像生成(主にジェン文字)についての発表でした。

https://www.youtube.com/watch?v=UJFi6Ote3js

LTという短い時間での発表で、ざっくりした全体像でしか紹介できませんでした。
なので、この記事では発表スライドを挟みながら、少しだけ詳細も説明していきます。

環境

  • Xcode 26.4
  • iOS 26.4

Apple Intelligence

Apple Intelligenceはデバイスに組み込まれたAppleのAIです。

https://www.apple.com/jp/apple-intelligence/

Apple Intelligence対応端末一覧

iPhone

端末 チップ
iPhone 15 Pro / 15 Pro Max A17 Pro
iPhone 16 / 16 Plus A18
iPhone 16e A18
iPhone 16 Pro / 16 Pro Max A18 Pro
iPhone 17 / 17e A19
iPhone Air A19 Pro
iPhone 17 Pro / 17 Pro Max A19 Pro

iPad

端末 チップ
iPad mini (第7世代) A17 Pro
iPad Air M1以降
iPad Pro M1以降

Mac / その他

端末 チップ
MacBook Air / Pro M1以降
iMac / Mac mini M1以降
Mac Studio M1 Max以降
MacBook Neo A18 Pro
Apple Vision Pro M2以降

ジェン文字とは

WWDC24で発表されたApple Intelligenceの機能で、パーソナライズされた絵文字を作成できる機能です。

例えば、SmileNosebleedというテキストから次のようなジェン文字が作成できます。

Living with Genmoji.004

ジェン文字はBeta版

登場から2年経ちましたが、いまだにBeta版に位置しており、意図としない結果が出力されてしまうことを理解した上でご利用いただければと思います。

Living with Genmoji.005

ユーザーがジェン文字を作成できる場所

Apple Intelligence対応機種であれば、以下の経路でジェン文字が作成できます。

  • 絵文字キーボードのジェン文字ボタン
  • AppleのImage Playgroundアプリ
    • スタイルのオプションでジェン文字を選択することで作成可能

Living with Genmoji.007

開発者がアプリに画像生成機能を組み込む方法

AppleからImage Playgroundフレームワークが提供されており、これを活用することでオンデバイスでの画像生成をアプリに簡単に組み込むことができます。
https://developer.apple.com/documentation/imageplayground

主に、2つのアプローチがあります。

  • ImagePlaygroundSheet
    • UIKit, AppKit: ImagePlaygroundViewControleer
  • ImageCreator

ImagePlaygroundSheet

ImagePlaygroundSheetは、前述したAppleのImage Playgroundアプリと同じような機能をもったシステムシートを表示できる機能です。

Living with Genmoji.010

このシート上で選択できる画像生成のスタイルは以下の4つです。

  • アニメーション
  • イラスト
  • スケッチ
  • ChatGPT

Image Playgroundアプリでは存在していたはずのジェン文字の選択肢はありませんでした、、

ImageCreator

ImageCreatorは、与えたテキストや画像などから画像生成スタイルを決めて、プログラマティックに画像生成できるクラスです。

以下のブログに詳細がまとまっているのでぜひ読んでみてください。

https://dev.classmethod.jp/articles/ios18-4-imagecreator-dynamic-image-generation/

基本的な画像生成の流れは以下になります。

func generateImage() async throws {
    // ImageCreatorの初期化
    let creator = try await ImageCreator()
    // 画像に含めたいコンセプト
    let concepts: [ImagePlaygroundConcept] = [.text("enjoy programing")]
    // 生成される画像の見た目のオプション
    let style: ImagePlaygroundStyle = .animation
    for try await result in creator.images(
        for: concepts,
        style: style,
        limit: 1
    ) {
        // 生成された画像
        let generateImage = result.cgImage
    }
}

ImagePlaygroundConcept

生成する画像に含めたいコンセプトをテキストやPKDrawingや画像などで示してあげることができます。

https://developer.apple.com/documentation/imageplayground/imageplaygroundconcept

  • text(String)
    • 短いテキストからコンセプトを作成
  • extracted(from: String, title: String?)
    • 長文の文字列とタイトルからコンセプトを作成
      • このtitleが長文の文字列を要約するためのキーワードとして使用されます
  • drawing(PKDrawing)
    • PencilKitの描画からコンセプトを作成
  • image(CGImage)
    • 指定した画像からコンセプトを作成
  • image(URL)
    • 指定したURLの画像からコンセプトを作成

creator.imagesに渡すImagePlaygroundConceptは配列で渡せれるのため、以下のような記述も可能です。

for try await result in creator.images(
    for: [.text("enjoy programing"),
          .image(capturedImage)],
    style: style,
    limit: 1
) {

個人的な感想ですが、imageを使用した場合、生成される画像はimageの主張がかなり強い気がしています。

ImagePlaygroundStyle

生成される画像の見た目のオプションを選ぶことができます。

https://developer.apple.com/documentation/imageplayground/imageplaygroundstyle

  • animation
    • アニメーションスタイル
  • illustration
    • 2D カートゥーンスタイル
  • sketch
    • 手描きスケッチスタイル
  • externalProvider
    • 外部プロバイダーが提供するスタイル
  • all: [ImagePlaygroundStyle]
    • あらゆるスタイルで画像を生成できるオプション

残念ながら、このドキュメント上にもgenmojiのオプションはありませんでした、、

ImageCreator.availableStyles

ImageCreatorのプロパティにavailableStylesがあります。

https://developer.apple.com/documentation/imageplayground/imagecreator/availablestyles

この値を出力して確認してみると、emojiというジェン文字のためのスタイルを確認できます。

Living with Genmoji.028

以下のようなコードで、プログラマティックにジェン文字を生成できるようになります。

func generateGenmoji() async throws {
    let creator = try await ImageCreator()

    guard let emojiStyle = creator.availableStyles.first(where: { $0.id == "emoji" })
    else { fatalError("My App is dead") }

    for try await result in creator.images(
        for: [.image(capturedImage)],
        style: emojiStyle,
        limit: 1
    ) {
        let generateImage = result.cgImage
    }
}

左で撮影した写真が右のジェン文字となって出力されました。

Living with Genmoji.032

conceptRequirePersonIdentity

これはジェン文字生成に限った話ではないのですが、ImageCreatorで文字列を渡して画像生成を行っていると、たびたびconceptRequirePersonIdentityエラーが発生して、生成を失敗することがありました。

このエラーが発生していた時のコンセプトとして渡していた文字列を見てみると、

Living with Genmoji.034

Ia man🏃など人間に関連すると表現を使うコンセプトで多く発生していることが分かりました。

conceptRequirePersonIdentityのエラーの説明を確認してみると、

https://developer.apple.com/documentation/imageplayground/imagecreator/error/conceptsrequirepersonidentity

追加で人間の顔画像を渡す必要がありそうです。

リクエストを完了するには、人物の顔が写っているソース画像を追加する必要があることを示すエラーです。

解決策

  • Iなどの人間に関連する表現を取り除く
    • 該当の文字列を消す
    • 別の表現に置き換える
  • ImagePlaygroundConceptに人物画像を付け加える

人物画像をコンセプトに付け加えるのが一番単純ですが、すべてのユーザーが顔写真を設定したいわけではないです。そこで人間に関連する表現を取り除く方針で解決案を深掘りました。

Foundation Models

ここでFoundation Models フレームワークの出番です。

https://developer.apple.com/documentation/FoundationModels

このフレームワークを使用することで、Appleが提供するオンデバイスLLMへアクセスできます。指示で役割を決め、プロンプトを入力すると、実行結果を受け取ることができます。

今回は入力値から人間に関連する表現を取り除くため、別の表現に置き換えるを試します。

以下は、日記の内容を絵文字に置き換える指示を与えた一例です。

LanguageModelSessionに指示を与えて、入力値をプロンプトとしてrespond(to:)の引数に渡して実行しています。

func removeHumanSubject(from text: String) async throws -> String {
    let session = LanguageModelSession(instructions: """
            You are a text rewriter. Given a journal entry,
            rewrite it as a short emoji description \
            that captures the mood and theme,
            but remove all references to people,
            humans, faces, or person identity.
            Focus on objects, animals, nature, food,
            activities, and emotions.
            Reply with only the rewritten description, nothing else.
            """)
    let response = try await session.respond(to: text)
    return response.content
}

また、このrespond(to:)メソッドは、generatingを引数を設定することでレスポンスとして生成される値を制御することができます。

https://developer.apple.com/documentation/foundationmodels/languagemodelsession/respond(to:generating:includeschemainprompt:options:)

generatingに渡す値はGenerableマクロを付けたstruct、もしくenumあれば、その形式で結果を出力させることができます。

https://developer.apple.com/documentation/foundationmodels/generable

func convertToEmojis(from text: String) async throws -> [String] {
    // ...省略
    let response = try await session.respond(
        to: text,
        generating: ExpressiveEmojis.self
    )
    return response.content.emojis
}

@Generable
struct ExpressiveEmojis {
    @Guide(description: "A list of emojis without any text", .count(6))
    let emojis: [String]
}

また、今回の例のようにGuideマクロをつけることで生成される値に影響を与えることができます。今回はテキストなしの絵文字の配列で個数は6個といったガイドを付けています。

https://developer.apple.com/documentation/foundationmodels/guide(description:_:)

ただし、これは出力される値を完全に強制できるものではなく、少なからず出力値に影響を与えることができるくらいの温度感でいた方が良さそうです。

このFoundation Modelsを活用した方法を用いることで、以下のようなパイプラインが実現可能です。

  1. User Input: ユーザー入力を受け取る
  2. Foundation Models: ユーザー入力値を画像生成しやすいような表現に変換する
  3. ImageCreator: 受け取った画像生成用の文字列から画像を生成する

Foundation Modelsを用いたパイプラインで試した一例ですが、下記のような結果になりました。

  1. User Input: Feeling so nervous about my upcoming talk at try! Swift Tokyo
  2. Foundation Models: 😬📣🐦💻🌍📺に変換
  3. ImageCreator: 以下の画像に変換

genmoji

ちなみに、ジェン文字生成の場合にImageCreatorが受け取ったコンセプトが😬📣🐦💻🌍📺のような絵文字の場合に生成されるジェン文字は絵文字同士を合体させるような挙動になることがわかりました。

おまけ

これまでの説明で、どんな文字列からもジェン文字が生成することができることが分かりました。
もっとジェン文字の知名度を向上させるには、ジェン文字が人々の生活に溶け込む必要があります。

そこで思いついたひとつのアイデアを紹介します。

ユーザーの日々のできごとを取得する

Journaling Suggestionsフレームワークを使用することで、Apple純正のジャーナルアプリで表示されるようなJournalingSuggestionsPickerという機能を使用することができます。

https://developer.apple.com/documentation/journalingsuggestions/journalingsuggestionspicker

このPickerには、訪れた場所や交流した人物、写真、聴いている曲など、ユーザーの人生で起こった個人的な出来事が表示されます。
PickerからはJournalingSuggestion.ItemContentという形で受け取ることができます。

Living with Genmoji.049

以下の例では、JournalingSuggestionPickerからワークアウトに関する情報を受け取った場合のジェン文字生成フローです。

JournalingSuggestion.Workout.Detailsからはワークアウトの詳細の情報を取得できます。

https://developer.apple.com/documentation/journalingsuggestions/journalingsuggestion/workout/details-swift.struct

struct SampleView: View {
    @State private var genmojiImage: CGImage?

    var body: some View {
        VStack {
            // result of Genmoji
            if let genmojiImage {
                Image(uiImage: UIImage(cgImage: genmojiImage))
            }

            // Button for JournalingSuggestionPicker
            JournalingSuggestionsPicker {
                Text("Show JournalingSuggestions")
            } onCompletion: { @MainActor suggestion in
                do {
                    let snapshot = await WorkoutSnapshot(suggestion: suggestion)
                    for name in snapshot.workoutNames {
                        let convertedEmojis = try await convertEmojis(from: "\(name) 頑張ったぞ")
                        let concept = convertedEmojis.joined()
                        try await generateGenmoji(from: concept)
                    }
                } catch {
                    print(error)
                }
            }
        }
    }

    func convertEmojis(from text: String) async throws -> [String] {
        let session = LanguageModelSession()
        let response = try await session.respond(
            to: text,
            generating: ExpressiveEmojis.self
        )
        return response.content.emojis
    }

    func generateGenmoji(from concept: String) async throws {
        let creator = try await ImageCreator()
        guard let emojiStyle = creator.availableStyles.first(where: { $0.id == "emoji" })
        else { fatalError() }
        let resultImages = creator.images(for: [.text(concept)], style: emojiStyle, limit: 1)
        for try await image in resultImages {
            // 生成されたGenmoji
            self.genmojiImage = image.cgImage
        }
    }
}

@Generable
struct ExpressiveEmojis {
    @Guide(description: "A list of emojis without any text", .count(3))
    let emojis: [String]
}

struct WorkoutSnapshot {
    let workoutNames: [String]

    init(suggestion: sending JournalingSuggestion) async {
        let workouts = await suggestion.content(forType: JournalingSuggestion.Workout.self)
        self.workoutNames = workouts.compactMap {
            $0.details?.localizedName ?? "No title"
        }
    }
}

まとめ

引き続き、ジェン文字をたくさんの人に使っていただけるように精進していきたいと思います。

Living with Genmoji.051

おわりに

今年もtry! Swift Tokyo ではたくさんの人と交流し、刺激的な日々を過ごせました。
海外のエンジニアとの交流が日本でできる希少な機会です。

改めて、主催、運営、スピーカー、スポンサー、参加された皆さま、素敵な空間をありがとうございました!

https://x.com/tryswiftconf/status/2044089870885552258?s=20

https://x.com/tryswiftconf/status/1909950917585383838?s=20

この記事をシェアする

関連記事