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 NSAttributedString
enabling 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
: AData
type that contains the data of the image;contentIdentifier
: A uniqueString
that identifies the image;contentDescription
: AString
storing 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 TextEditor
or 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
supportsAdaptiveImageGlypgh
property 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 NSAttributedString
rather 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
NSAttributedString
text variable that will store the input. - Assign the binding
NSAttributedString
text to theattributedText
property of your customUITextView
. - Enable the Genmoji creation with
supportsAdaptiveImageGlyph
on 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
importsGraphics
property 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
textStorage
property 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
NSAttributedString
using theinit(data:options:documentAttributes:)
out from the rtfData. - Set the returning
NSAttributedString
on 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 ofNSAttributedString
intoData
;deserializeText(data:)
responsible for the de-serialization ofData
intoNSAttributedString
.
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.