I need to find out whether a character in a string is an emoji.
For example, I have this character:
let string = "๐"
let character = Array(string)[0]
I need to find out if that character is an emoji.
What I stumbled upon is the difference between characters, unicode scalars and glyphs.
For example, the glyph ๐จโ๐จโ๐งโ๐ง consists of 7 unicode scalars:
Another example, the glyph ๐๐ฟ consists of 2 unicode scalars:
Last one, the glyph 1๏ธโฃ contains three unicode characters:
So when rendering the characters, the resulting glyphs really matter.
Swift 5.0 and above makes this process much easier and gets rid of some guesswork we needed to do. Unicode.Scalar
's new Property
type helps is determine what we're dealing with.
However, those properties only make sense when checking the other scalars within the glyph. This is why we'll be adding some convenience methods to the Character class to help us out.
For more detail, I wrote an article explaining how this works.
For Swift 5.0, this leaves you with the following result:
extension Character {
/// A simple emoji is one scalar and presented to the user as an Emoji
var isSimpleEmoji: Bool {
guard let firstScalar = unicodeScalars.first else { return false }
return firstScalar.properties.isEmoji && firstScalar.value > 0x238C
}
/// Checks if the scalars will be merged into an emoji
var isCombinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false }
var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji }
}
extension String {
var isSingleEmoji: Bool { count == 1 && containsEmoji }
var containsEmoji: Bool { contains { $0.isEmoji } }
var containsOnlyEmoji: Bool { !isEmpty && !contains { !$0.isEmoji } }
var emojiString: String { emojis.map { String($0) }.reduce("", +) }
var emojis: [Character] { filter { $0.isEmoji } }
var emojiScalars: [UnicodeScalar] { filter { $0.isEmoji }.flatMap { $0.unicodeScalars } }
}
Which will give you the following results:
"Aฬอฬ".containsEmoji // false
"3".containsEmoji // false
"Aฬอฬโถ๏ธ".unicodeScalars // [65, 795, 858, 790, 9654, 65039]
"Aฬอฬโถ๏ธ".emojiScalars // [9654, 65039]
"3๏ธโฃ".isSingleEmoji // true
"3๏ธโฃ".emojiScalars // [51, 65039, 8419]
"๐๐ฟ".isSingleEmoji // true
"๐๐ผโโ๏ธ".isSingleEmoji // true
"๐น๐ฉ".isSingleEmoji // true
"โฐ".isSingleEmoji // true
"๐ถ".isSingleEmoji // true
"๐จโ๐ฉโ๐งโ๐ง".isSingleEmoji // true
"๐ด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ".isSingleEmoji // true
"๐ด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ".containsOnlyEmoji // true
"๐จโ๐ฉโ๐งโ๐ง".containsOnlyEmoji // true
"Hello ๐จโ๐ฉโ๐งโ๐ง".containsOnlyEmoji // false
"Hello ๐จโ๐ฉโ๐งโ๐ง".containsEmoji // true
"๐ซ Hรฉllo ๐จโ๐ฉโ๐งโ๐ง".emojiString // "๐ซ๐จโ๐ฉโ๐งโ๐ง"
"๐จโ๐ฉโ๐งโ๐ง".count // 1
"๐ซ Hรฉllล ๐จโ๐ฉโ๐งโ๐ง".emojiScalars // [128107, 128104, 8205, 128105, 8205, 128103, 8205, 128103]
"๐ซ Hรฉllล ๐จโ๐ฉโ๐งโ๐ง".emojis // ["๐ซ", "๐จโ๐ฉโ๐งโ๐ง"]
"๐ซ Hรฉllล ๐จโ๐ฉโ๐งโ๐ง".emojis.count // 2
"๐ซ๐จโ๐ฉโ๐งโ๐ง๐จโ๐จโ๐ฆ".isSingleEmoji // false
"๐ซ๐จโ๐ฉโ๐งโ๐ง๐จโ๐จโ๐ฆ".containsOnlyEmoji // true
For older Swift versions, check out this gist containing my old code.