I have setup a NSLayoutManagerDelegate to display emojis for ASCII text on iOS, iPadOS, and Mac Catalyst.
For the first character (e.g., ":"), I set the emoji glyph (e.g., "😀"). For the remainder (e.g., ")"), I use a .null property so it doesn't display.
This almost works, but it's using the default font instead of "Apple Color Emoji", so it appears as a text symbol.
Here is the delegate code. Note that I have verified that the font used for the emoji glyph is the emoji font.
	func layoutManager(_ layoutManager: NSLayoutManager, shouldGenerateGlyphs glyphs: UnsafePointer<CGGlyph>, properties: UnsafePointer<NSLayoutManager.GlyphProperty>, characterIndexes indexes: UnsafePointer<Int>, font: UIFont, forGlyphRange range: NSRange) -> Int {
		let count = range.length
		var glyphs = Array(UnsafeBufferPointer(start: glyphs, count: count))
		var properties = Array(UnsafeBufferPointer(start: properties, count: count))
		var indexes = Array(UnsafeBufferPointer(start: indexes, count: count))
		var fonts = Array(repeating: font, count: count)
		if let textStorage = layoutManager.textStorage {
			for i in 0..<count {
				let range = NSRange(location: range.location + i, length: 1)
				textStorage.enumerateAttribute(Composition.Key.emojiText, in: range, options: .longestEffectiveRangeNotRequired) {(value, range, stop) in
					if let fragment = value as? Composition.Fragment {
						if range.location == fragment.range?.location {
							let string = NSAttributedString(string: fragment.text2, attributes: textStorage.attributes(at: range.location, effectiveRange: nil))
							let line = CTLineCreateWithAttributedString(string)
							let runs = CTLineGetGlyphRuns(line) as! [CTRun]
							if let run = runs.first {
								if let attributes = CTRunGetAttributes(run) as NSDictionary? {
									let font = attributes[kCTFontAttributeName as String] as! UIFont
									if CTRunGetGlyphCount(run) == 1 {
										var glyph = CGGlyph()
										CTRunGetGlyphs(run, CFRange(location: 0, length: 1), &glyph)
										glyphs[i] = glyph
										properties[i] = []
										fonts[i] = font
									}
								}
							}
						} else {
							glyphs[i] = CGGlyph()
							properties[i] = .null
						}
					}
				}
			}
		}
		for i in 0..<count {
			let range = NSRange(location: range.location + i, length: 1)
			layoutManager.setGlyphs(&glyphs[i], properties: &properties[i], characterIndexes: &indexes[i], font: fonts[i], forGlyphRange: range)
		}
		return count
	}
Is there something else I should do to set the font?