Enabling Genmoji in your app
Learn how to make your app able to input custom emoji.
With iOS 18.0, iPadOS 18.0 and macOS 15.0, Apple introduced the adaptiveImageGlyph property to the NSAttributedStringenabling the class to store Genmoji, an emoji generated from textual prompts that, unlike emojis, are image glyphs and not Unicode.
The adaptiveImageGlyph property is of the NSAdaptiveImageGlyph type, a data object for an emoji-like image that can appear in attributed text, and has the following properties:
imageContent: ADatatype that contains the data of the image;contentIdentifier: A uniqueStringthat identifies the image;contentDescription: AStringstoring the alternative text, the image content description;contentType: AUTType, the image data format to use for this image type.
Every time a new custom emoji is created, TextKit creates an instance of NSAdaptiveImageGlyph storing Genmoji information in it, and enabling the ability to change its size and resolution - square aspect ratio based - according to the metadata; this later is also responsible for giving directions on how to correctly do it based on the text font and the font attributes.
Thanks to this capability to adapt to the text surrounding them, Genmoji are particularly appropriate for blog posts, titles and messages, which also means that they won't fit other cases like being used in identifiers, phone numbers or email addresses.
They can be used all alone or in combination with text, formatted, copied and pasted and used as stickers.
They are supported by rich text view and by the following system serializing frameworks - that have been updated, to natively support NSAdaptiveImageGlyph, with the introduction of NSAttributedString(adaptiveImageGlyph:, attributes:):
- RTFD
- UIActivity / UIPasteboard
- NSPasteboard
- HTML - NSAttributeString
Enabling the generation of Genmoji on the keyboard
The Genmoji generation icon button doesn’t show up automatically on your keyboard when you implement a TextEditoror a TextField .

To enable it, the property supportsAdaptiveImageGlypgh of the UITextView must be set to true.
// 1. Custom Text View
struct CustomTextEditor: UIViewRepresentable {
@Binding var text: String
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.text = text
// 2. Enabling editability
textView.isEditable = true
textView.font = UIFont.systemFont(ofSize: 40)
// 3. Enabling the genmoji creation
textView.supportsAdaptiveImageGlyph = true
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
if uiView.text != text {
uiView.text = text
}
}
}- Create a custom
UITextView. - Make it editable.
- Set the
supportsAdaptiveImageGlypghproperty to true.
And now create an instance of your CustomTextEditor in your main view.
import SwiftUI
struct ContentView: View {
@State var text: String = ""
var body: some View {
VStack {
CustomTextEditor(text: $text)
}
.padding()
}
}
The same result can be achieved when using NSAttributedStringrather thanString.
struct CustomTextEditor: UIViewRepresentable {
// 1. The binding NSAttributedString text
@Binding var text: NSAttributedString?
func makeUIView(context: Context) -> UITextView {
...
// 2. Assign the binding NSAttributedString text
textView.attributedText = text
// 3. Enabling the genmoji creation
textView.supportsAdaptiveImageGlyph = true
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
if uiView.attributedText != text {
uiView.attributedText = text
}
}
}- Declare the binding
NSAttributedStringtext variable that will store the input. - Assign the binding
NSAttributedStringtext to theattributedTextproperty of your customUITextView. - Enable the Genmoji creation with
supportsAdaptiveImageGlyphon true.
Create an instance of your CustomTextEditor in your main view.
import SwiftUI
struct ContentView: View {
@State var text: NSAttributedString? = nil
var body: some View {
VStack {
CustomTextEditor(text: $text)
}
.padding()
}
}Enabling the copy-paste of Genmoji from the edit menu
To be also able to copy-paste the Genmoji from the edit menu of your custom UITextView, be sure to set the allowsEditingTextAttributes property to true.
func makeUIView(context: Context) -> UITextView {
...
// Enable its editability
textView.allowsEditingTextAttributes = true
...
}It also enables the display of the Genmoji button icon and feature.
In macOS, NSTextView.importsGraphics allows you to achieve the same result.
struct CustomTextEditor: NSViewRepresentable {
func makeNSView(context: Context) -> NSTextView {
// 1. Instance of NSTextView
let textView = NSTextView()
...
// 2. Enable graphics
textView.importsGraphics = true
return textView
}
}- Create an instance of
NSTextView. - Set the
importsGraphicsproperty on true to enable the copy-paste of Genmoji.
Serializing and de-serializing Genmoji for reading and displaying
It may happen in certain situations that you need to display the content of a text containing the Genmoji. To achieve this kind of result, the attributed string must be read and serialized as rich text formatted data to be ready to be shared.
This approach will ensure that all the data about the Genmoji will be preserved to avoid losing important information when recreating your NSAdaptiveImageGlyph when you are ready to display it again.
// 1. Access the attributed string
let textContents = textView.textStorage
// 2. Serialize as data
let rtfData = try textContents.data(
from: NSRange(location: 0, length: textContents.length),
documentAttributes: [.documentType: NSAttributedString.DocumentType.rtfd]
)
- Store the attributed string from the
textStorageproperty of your customUITextView; - Serialize it using the method
data(from:documentAttributes:), that turns a text stream corresponding to the characters and attributes within the specified range into a data object.
To display this data object, reverse the process.
// 1. De-serialized rtfData
let attributedString = try NSAttributedString(data: rtfData, documentAttributes: nil)
// 2. Set it on text view
textView.textStorage.setAttributedString(attributedString)- Create and store an instance of
NSAttributedStringusing theinit(data:options:documentAttributes:)out from the rtfData. - Set the returning
NSAttributedStringon the customUITextView.
Let’s see how to start taking advantage of this approach by integrating it in a SwiftUI view.
Create a custom UITextView that will be responsible to display the NSAttributedString coming from the deserialization of the rtfData.
import SwiftUI
// Custom View
struct CustomTextView: UIViewRepresentable {
@Binding var text: NSAttributedString?
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
...
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
// Update the text view's content if the binding changes
if let updatedText = text {
uiView.attributedText = updatedText
}
}
}Create a custom UITextView that will allow the user to input text and generate custom emoji.
// Custom Text Editor
struct CustomTextEditor: UIViewRepresentable {
@Binding var text: NSAttributedString?
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.isEditable = true
textView.font = UIFont.systemFont(ofSize: 16)
textView.allowsEditingTextAttributes = true
textView.supportsAdaptiveImageGlyph = true
textView.delegate = context.coordinator
// Initialize with the current text if available
if let initialText = text {
textView.attributedText = initialText
}
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
if uiView.attributedText != text {
uiView.attributedText = text
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UITextViewDelegate {
var parent: CustomTextEditor
init(_ parent: CustomTextEditor) {
self.parent = parent
}
func textViewDidChange(_ textView: UITextView) {
// Update the binding
if let currentText = textView.attributedText {
parent.text = currentText
}
}
}
}Be sure to implement Coordinator and assign it as the UITextView.delegate to handle text changes and sync them with the SwiftUI state.
struct ContentView: View {
var body: some View {
...
}
// 1. Serialize text from an NSTextStorage into RTFD data
func serializeText(text: NSAttributedString?) -> Data? {
guard let text = text else { return nil }
do {
let rtfData = try text.data(from: NSRange(location: 0, length: text.length), documentAttributes: [.documentType: NSAttributedString.DocumentType.rtfd])
return rtfData
} catch {
print("Error serializing text: \(error)")
return nil
}
}
// 2. Deserialize RTFD data back into an NSAttributedString
func deserializeText(data: Data) -> NSAttributedString? {
do {
let attributedString = try NSAttributedString(data: data, documentAttributes: nil)
return attributedString
} catch {
print("Error deserializing text: \(error)")
return nil
}
}
}Create two different functions:
serializeText(text:)responsible for the serialization ofNSAttributedStringintoData;deserializeText(data:)responsible for the de-serialization ofDataintoNSAttributedString.
import SwiftUI
struct ContentView: View {
@State var textInput: NSAttributedString? = NSAttributedString(string: "Type here")
@State var textToDisplay: NSAttributedString? = nil
var body: some View {
VStack {
// Custom Text Editor
CustomTextEditor(text: $textInput)
// Custom Text View to display the serialized/deserialized content
if let displayedText = textToDisplay {
CustomTextView(text: $textToDisplay)
} else {
Text("No text to display")
.foregroundColor(.gray)
}
}
.onChange(of: textInput) { oldText, newText in
if let rtfData = serializeText(newText) {
textToDisplay = deserializeText(rtfData)
}
}
.padding()
}
func serializeText(text: NSAttributedString?) -> Data? {...}
func deserializeText(data: Data) -> NSAttributedString? {...}
}
In this view, whenever the text input by the user has changed, the new value will be serialized into a Data object and de-serialized into an NSAttributedString to be displayed in a customUITextView.
The introduction of the adaptiveImageGlyph property and NSAdaptiveImageGlyph in iOS 18.0, iPadOS 18.0, and macOS 15.0 allows to handle rich text with custom, adaptive emojis like Genmoji. Their seamless adaptability to surrounding text attributes, combined with their flexibility to serialize and deserialize across platforms, makes them a powerful tool for creating engaging user content.