← 모든 글

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는 네 가지 식별 속성을 가진 데이터 래퍼입니다2.

  • imageContent: Data. contentType이 선언한 형식의 이미지 데이터 자체입니다.
  • contentIdentifier: String. 글리프 인스턴스의 고유 식별자입니다. 중복 제거 및 시스템의 내부 캐싱에 사용됩니다.
  • contentDescription: String. 글리프를 설명하는 대체 텍스트입니다. 접근성 레이블을 노출하거나 글리프를 미지원 수신자에게 보내는 앱이 이 값을 사용합니다.
  • contentType: UTType. Apple이 적응형 글리프에 사용하는 이미지 형식(HEIC 변형)을 노출하는 클래스 수준 타입 속성입니다. 직렬화하는 앱은 형식 인식 처리를 위해 이를 확인합니다.

데이터는 일반적으로 표준 Genmoji의 경우 수십 KB입니다. 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 모델과 함께 출시된 레거시 텍스트 엔진)은 적응형 이미지 글리프를 올바르게 렌더링하지 못합니다. 글리프가 일반 플레이스홀더로 표시되거나 아예 레이아웃되지 않습니다.

앱은 세 가지 상태에 있습니다.

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 사용 사례를 가진 앱의 경우 마이그레이션은 대부분 자동으로 이루어집니다.

하이브리드 앱. 일부 앱은 일반 편집을 위한 UITextView와 함께 HTML 편집을 위해 WKWebView를 임베드합니다. 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. 웹 친화적입니다. 글리프는 base64 인코딩 데이터 URI를 가진 <img> 태그로 직렬화됩니다. 페이로드가 더 크지만 대부분의 리치 텍스트 지원 수신자에서 작동합니다.

확장 기능을 갖춘 Markdown. 표준 Markdown은 적응형 이미지 글리프 구문이 없지만, 확장 방언(첨부 지원이 있는 CommonMark, Apple 자체의 확장 Markdown)은 이를 전달할 수 있습니다. Markdown 기반 영속화의 경우 방언 요구 사항을 문서화하세요.

네트워크를 통해 텍스트를 보내는 앱(채팅, 이메일, 소셜 미디어)은 결정해야 합니다. 글리프를 종단 간 보존(발신자와 수신자 모두 iOS 18+이고 전송이 리치 텍스트를 지원하는 경우에만 작동), 글리프를 제거하고 폴백 텍스트(contentDescription)로 대체, 또는 글리프를 시스템 이미지로 렌더링하고 그 이미지를 임베드. 올바른 선택은 대상과 플랫폼에 따라 달라집니다.

일반적인 실패

NSAdaptiveImageGlyph 도입 시 세 가지 실패 모드가 반복됩니다.

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+ 앱에 시사하는 바

세 가지 핵심.

  1. 모든 텍스트 입력에 supportsAdaptiveImageGlyph = true를 설정하세요. 사용자가 입력한 텍스트를 받는 앱은 적응형 이미지 글리프에 대해 기본적으로 옵트인되어야 합니다. 이 단일 속성이 Genmoji 렌더링과 Genmoji 손상의 차이를 만듭니다.

  2. 여전히 TextKit 1을 사용 중이라면 TextKit 2로 마이그레이션하세요. TextKit 1은 유지보수 모드에 있습니다. 새로운 iOS 26 시대 기능(적응형 이미지 글리프, Writing Tools의 인라인 재작성, Liquid Glass 텍스트 렌더링)은 모두 TextKit 2를 가정합니다. 마이그레이션 비용은 실재하지만 대안은 폐기 예정인 텍스트 엔진으로 출시하는 것입니다.

  3. 적응형 이미지 글리프를 염두에 두고 영속화 형식을 선택하세요. 네이티브 iOS 저장에는 RTFD, 웹 호환 저장에는 임베드된 이미지를 가진 HTML, 고성능 앱에는 첨부 사이드 채널이 있는 커스텀 바이너리 형식. 사용자가 Genmoji를 입력할 앱에 일반 텍스트는 잘못된 기본값입니다.

전체 Apple 생태계 클러스터: 병행하는 Apple Intelligence 텍스트 표면을 다룬 Writing Tools; 이미지 생성을 다룬 Image Playground; iOS 26 Apple Intelligence 통합을 다룬 App Intents 2.0; 온디바이스 LLM를 다룬 Foundation Models. 허브는 Apple Ecosystem Series에 있습니다. 더 광범위한 iOS-AI 에이전트 컨텍스트는 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 vs .adaptiveImageGlyph)와 다른 렌더링 경로(TextKit 1+TextKit 2 vs TextKit 2 전용)를 사용합니다. Genmoji 스타일 콘텐츠를 타겟팅하는 새 코드는 NSAdaptiveImageGlyph를 사용합니다.

References


  1. Apple Developer Documentation: supportsAdaptiveImageGlyph. UITextView가 준수하는 UITextInput 프로토콜에 선언된 옵트인 속성입니다. 따라서 동일한 속성을 textView.supportsAdaptiveImageGlyph로 접근할 수 있습니다. 

  2. Apple Developer Documentation: NSAdaptiveImageGlyph. 이미지 콘텐츠, 식별자, 설명, 콘텐츠 타입을 감싸는 데이터 타입입니다. 

  3. Apple Developer Documentation: NSAttributedString.Key.adaptiveImageGlyphNSAttributedString(adaptiveImageGlyph:attributes:). 적응형 이미지 글리프를 위한 속성 문자열 통합 표면입니다. 

  4. Apple Developer Documentation: TextKitUsing TextKit 2 to interact with text. 현재 TextKit 2 진입점입니다. 적응형 이미지 글리프 렌더링은 TextKit 2 레이아웃 아키텍처에 의존합니다. 

  5. Apple Developer Documentation: NSAttributedString.DocumentType. 영속화를 통해 적응형 이미지 글리프를 라운드트립하기 위해 지원되는 리치 텍스트 형식(RTFD, HTML 등)입니다. 

관련 게시물

Image Playground API: SwiftUI 시트, 프로그래밍 방식 이미지 생성, 그리고 스타일 제어

Image Playground는 앱에 두 가지 경로를 제공합니다. SwiftUI imagePlaygroundSheet 모디파이어와, 개념으로부터 이미지를 생성하는 프로그래밍 방식의 ImageCreator API입니다…

7 분 소요

Apple의 Translation 프레임워크: 무료, 온디바이스, 그리고 생각보다 날카롭다

Apple의 Translation 프레임워크: translationPresentation과 TranslationSession을 통한 무료 온디바이스 번역, 그리고 데모가 건너뛰는 오프라인 다운로드의 까다로운 지점들.

7 분 소요

Loop Engineering: Loops Win Where Verification Is Cheap

Loop engineering, checked against Boris Cherny's full transcripts: every loop he names has cheap verification. That cons…

19 분 소요