[iOS] Let's generate original emoji (Gen characters) using on-device LLMs. Explanation of try! Swift Tokyo 2026 talk

[iOS] Let's generate original emoji (Gen characters) using on-device LLMs. Explanation of try! Swift Tokyo 2026 talk

2026.04.21

This page has been translated by machine translation. View original

I presented at the largest Swift conference in Japan, try! Swift Tokyo 2026, where developers gather from around the world.

This year, the venue was once again at Tachikawa Stage Garden, and apparently the day before, Morning Musume.'26 had their concert at the same venue. I never thought I'd stand on the same stage as such great idols.

Presentation Content

I presented on the theme How to Live with Genmoji.
It was a presentation about image generation using Apple Intelligence (mainly Genmoji).

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

Since it was a short LT presentation, I could only give a rough overview.
So in this article, I'll explain some additional details while sharing my presentation slides.

Environment

  • Xcode 26.4
  • iOS 26.4

Apple Intelligence

Apple Intelligence is Apple's AI built into devices.

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

Apple Intelligence Compatible Devices

iPhone

Device Chip
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

Device Chip
iPad mini (7th generation) A17 Pro
iPad Air M1 or later
iPad Pro M1 or later

Mac / Others

Device Chip
MacBook Air / Pro M1 or later
iMac / Mac mini M1 or later
Mac Studio M1 Max or later
MacBook Neo A18 Pro
Apple Vision Pro M2 or later

What is Genmoji

Genmoji is a feature of Apple Intelligence announced at WWDC24 that allows you to create personalized emoji.

For example, you can create Genmoji like this from the text Smile and Nosebleed.

Living with Genmoji.004

Genmoji is in Beta

Even though it's been 2 years since its introduction, it's still in Beta, so please understand that it may produce unintended results.

Living with Genmoji.005

Where users can create Genmoji

If you have an Apple Intelligence compatible device, you can create Genmoji through:

  • The Genmoji button in the emoji keyboard
  • Apple's Image Playground app
    • By selecting the Genmoji option in styles

Living with Genmoji.007

How developers can integrate image generation into their apps

Apple provides the Image Playground framework, which makes it easy to incorporate on-device image generation into apps.
https://developer.apple.com/documentation/imageplayground

There are mainly two approaches:

  • ImagePlaygroundSheet
    • UIKit, AppKit: ImagePlaygroundViewControleer
  • ImageCreator

ImagePlaygroundSheet

ImagePlaygroundSheet is a feature that displays a system sheet with the same functionality as Apple's Image Playground app.

Living with Genmoji.010

The image generation styles you can select on this sheet are the following four:

  • Animation
  • Illustration
  • Sketch
  • ChatGPT

Interestingly, the Genmoji option that exists in the Image Playground app was not available...

ImageCreator

ImageCreator is a class that allows you to programmatically generate images by determining the image generation style from given text or images.

For more details, please read the following blog post.

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

The basic image generation flow is as follows:

func generateImage() async throws {
    // Initialize ImageCreator
    let creator = try await ImageCreator()
    // Concepts to include in the image
    let concepts: [ImagePlaygroundConcept] = [.text("enjoy programing")]
    // Style option for the generated image
    let style: ImagePlaygroundStyle = .animation
    for try await result in creator.images(
        for: concepts,
        style: style,
        limit: 1
    ) {
        // Generated image
        let generateImage = result.cgImage
    }
}

ImagePlaygroundConcept

You can provide concepts to include in the generated image using text, PKDrawing, or images.

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

  • text(String)
    • Creates a concept from a short text
  • extracted(from: String, title: String?)
    • Creates a concept from a long text string and title
      • This title is used as a keyword to summarize the long text
  • drawing(PKDrawing)
    • Creates a concept from a PencilKit drawing
  • image(CGImage)
    • Creates a concept from a specified image
  • image(URL)
    • Creates a concept from an image at the specified URL

You can pass an array of ImagePlaygroundConcept to creator.images, allowing for expressions like:

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

In my personal experience, when using image, the generated image tends to be strongly influenced by the input image.

ImagePlaygroundStyle

You can choose appearance options for the generated image.

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

  • animation
    • Animation style
  • illustration
    • 2D cartoon style
  • sketch
    • Hand-drawn sketch style
  • externalProvider
    • Style provided by an external provider
  • all: [ImagePlaygroundStyle]
    • Option to generate images in any style

Unfortunately, there was no genmoji option in this documentation either...

ImageCreator.availableStyles

There's a property called availableStyles in ImageCreator.

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

When checking this value, we can confirm an emoji style for Genmoji.

Living with Genmoji.028

You can programmatically generate Genmoji with code like this:

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
    }
}

The photo taken on the left was output as the Genmoji on the right.

Living with Genmoji.032

conceptRequirePersonIdentity

This isn't limited to Genmoji generation, but when generating images with text strings in ImageCreator, I often encountered conceptRequirePersonIdentity errors.

Looking at the string concepts that were causing this error:

Living with Genmoji.034

I found it occurs frequently with human-related expressions like I, a man, or 🏃.

Checking the explanation of the conceptRequirePersonIdentity error:

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

It seems we need to provide an additional human face image.

An error indicating that a source image of a person's face must be added to complete the request.

Solutions

  • Remove human-related expressions
    • Delete the relevant text
    • Replace with alternative expressions
  • Add a person's image to ImagePlaygroundConcept

Adding a person's image to the concept is the simplest solution, but not all users want to set a face photo. So I explored solutions focusing on removing human-related expressions.

Foundation Models

This is where the Foundation Models framework comes in.

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

Using this framework, you can access Apple's on-device LLM. You can define a role through instructions, input a prompt, and receive results.

Now let's try replacing human-related expressions with alternative expressions.

Here's an example of giving the instruction to convert diary content into emoji:

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
}

Additionally, the respond(to:) method can control the generated values by setting the generating argument.

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

If you pass a struct or enum with the Generable macro to generating, the result will be output in that format.

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

func convertToEmojis(from text: String) async throws -> [String] {
    // ...omitted
    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]
}

You can also influence the generated values by adding the Guide macro. In this example, we've added a guide specifying an array of emojis without text with a count of 6.

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

However, this doesn't completely control the output values; it's better to think of it as something that can influence the output to some extent.

Using Foundation Models, we can implement the following pipeline:

  1. User Input: Receive user input
  2. Foundation Models: Convert user input to expressions suitable for image generation
  3. ImageCreator: Generate images from the converted image generation string

Example

Here's an example result using the Foundation Models pipeline:

  1. User Input: Feeling so nervous about my upcoming talk at try! Swift Tokyo
  2. Foundation Models: Converted to 😬📣🐦💻🌍📺
  3. ImageCreator: Converted to the following image

genmoji

By the way, I found that when ImageCreator receives concepts like 😬📣🐦💻🌍📺 (emojis) for Genmoji generation, it tends to create Genmoji that combine these emojis.

Bonus

Now that we understand how to generate Genmoji from any string, to increase Genmoji's popularity, we need to integrate it into people's daily lives.

Here's one idea I came up with:

Getting users' daily events

Using the Journaling Suggestions framework, you can use the JournalingSuggestionsPicker feature, similar to what's shown in Apple's native Journal app.

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

This Picker displays personal events from the user's life, such as places visited, people interacted with, photos taken, and music listened to. You can receive this data as JournalingSuggestion.ItemContent.

Living with Genmoji.049

Example

Here's an example Genmoji generation flow when receiving workout information from JournalingSuggestionPicker:

You can get workout details from 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 {
            // Generated 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"
        }
    }
}

Summary

I'll continue striving to make Genmoji accessible to more people.

Living with Genmoji.051

Closing

This year at try! Swift Tokyo, I again had the opportunity to interact with many people and spend stimulating days.
It's a rare opportunity in Japan to connect with international engineers.

Once again, thank you to the organizers, staff, speakers, sponsors, and all participants for creating such a wonderful space!

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

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

Share this article