25

I'm building an iOS App, and Emojis play a big part in it.

In iOS 10.2, new emojis were released.

I'm pretty sure that if someone has iOS 8, for example, they wouldn't actually be able to see these emojis. Is there a way to detect this? I'm trying to dynamically build a list of all the Emojis that are supported on the user's iOS version, but I'm having a bit of trouble.

David
  • 7,028
  • 10
  • 48
  • 95
  • Are you trying to determine if an emjoi is available for the user in your app or the person you're sending a message to? I wonder if there's a string representation for emojis – quantumpotato Dec 25 '16 at 05:56
  • Well, it is sort of two-fold. One the one hand, I want to generate a list of all emojis a user can use on their own iOS version, but if they send it to someone, that person might not be able to see the emoji. In which case, I want a fallback. – David Dec 25 '16 at 06:04
  • Why you don't use `@available(iOS 10.2, *)` before generating available emojis to the user ? – Wajih Dec 25 '16 at 06:44
  • @wajeeh I could do something like that for generating emojis for iOS 10.2, but that doesn't tell me anything about if an Emoji is supported in iOS 9, for example. In fact, that would be great if I got a list of Emojis for iOS 9, 9.1, etc. - iOS 10.2. I'm looking for lists like that currently. – David Dec 25 '16 at 06:50
  • Ah ok, check this link http://en.stack.aiseen.org/questions/35687407/get-surrogate-pairs-from-an-emoji , Also after I made some tests I found that unsupported emoji will always fall back to a specific character (black question mark), So maybe you can benefit from this. this answer use same method: http://stackoverflow.com/a/39606543/5559741 – Wajih Dec 25 '16 at 08:34
  • 3
    Here's a link with added emojis on iOS 10.2: http://emojipedia.org/apple/ios-10.2/new/ You can scroll to the bottom and pick another iOS version changelog. Not sure how correct that website is. This is not what you asked for, but it might help. – Nikola Miljković Dec 29 '16 at 10:17
  • @SomeHelpingDude that looks really cool. If I could consolidate some of the versions into like an array of unicodes in the order Apple shows them in the Emoji Keyboard, this could work out. I'd need to work out the kinks of different skin tone emojis, but that shouldn't be too difficult. – David Dec 29 '16 at 20:11
  • The _update_ is an entirely different question. Please post a new request. – SwiftArchitect Dec 30 '16 at 20:45

2 Answers2

28

Clarification: an Emoji is merely a character in the Unicode Character space, so the present solution works for all characters, not just Emoji.

Synopsis

To know if a Unicode character (including an Emoji) is available on a given device or OS, run the unicodeAvailable() method below.

It works by comparing a given character image against a known undefined Unicode character U+1FFF.

unicodeAvailable(), a Character extension

private static let refUnicodeSize: CGFloat = 8
private static let refUnicodePng =
    Character("\u{1fff}").png(ofSize: Character.refUnicodeSize)

func unicodeAvailable() -> Bool {
    if let refUnicodePng = Character.refUnicodePng,
        let myPng = self.png(ofSize: Character.refUnicodeSize) {
        return refUnicodePng != myPng
    }
    return false
}

Discussion

  1. All characters will be rendered as a png at the same size (8) as defined once in

    static let refUnicodeSize: CGFloat = 8

  2. The undefined character U+1FFF image is calculated once in

    static let refUnicodePng = Character("\u{1fff}").png(ofSize: Character.refUnicodeSize)

  3. A helper method optionally creates a png from a Character

    func png(ofSize fontSize: CGFloat) -> Data?

1. Example: Test against 3 emoji

let codes:[Character] = ["\u{2764}","\u{1f600}","\u{1F544}"] // ❤️, , undefined
for unicode in codes {
    print("\(unicode) : \(unicode.unicodeAvailable())")
}

3 characters

2. Example: Test a range of Unicode characters

func unicodeRange(from: Int, to: Int) {
    for unicodeNumeric in from...to {
        if let scalar = UnicodeScalar(unicodeNumeric) {
            let unicode = Character(scalar)
            let avail = unicode.unicodeAvailable()
            let hex = String(format: "0x%x", unicodeNumeric)
            print("\(unicode) \(hex) is \(avail ? "" : "not ")available")
        }
    }
}

a few hundred characters


Helper function: Character to png

func png(ofSize fontSize: CGFloat) -> Data? {
    let attributes = [NSAttributedStringKey.font:
                          UIFont.systemFont(ofSize: fontSize)]
    let charStr = "\(self)" as NSString
    let size = charStr.size(withAttributes: attributes)

    UIGraphicsBeginImageContext(size)
    charStr.draw(at: CGPoint(x: 0,y :0), withAttributes: attributes)

    var png:Data? = nil
    if let charImage = UIGraphicsGetImageFromCurrentImageContext() {
        png = UIImagePNGRepresentation(charImage)
    }

    UIGraphicsEndImageContext()
    return png
}

► Find this solution on GitHub and a detailed article on Swift Recipes.

Community
  • 1
  • 1
SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
  • 3
    It is merely one of the many characters not yet implemented (or defined). It thus prints a [?] question mark, as do all the invalid characters. All invalid unicode characters share the same bitmap. – SwiftArchitect Dec 30 '16 at 09:09
  • 1
    FYI, this fails for ‍♂️ (man doing cartwheel, U+1F938 U+200D U+2642 U+FE0F) because the PNG will render as [?]♂ rather than [?]. I worked around this by only rendering the first codepoint (obtained using enumerateSubstringsInRange:options:usingBlock:) – Ford Oct 10 '17 at 02:02
  • @Ford, find generalized solution on GitHub. As per Unit Test, no need to render individual Codepoints – SwiftArchitect Jan 03 '18 at 21:28
  • @SwiftArchitect, This approach doesnt work with Country flags. e.g. "NZ" encoded as a unicode countrycode is fine but "EU" (some software insists the European Union is a country) returns the letters E and U each surrounded by a box. I can filter the country codes that I know will cause problems but I still don't know if macOS has actually created a flag glyph for the remainder. – granada29 Jul 13 '18 at 06:30
  • This approach works for EU. Build a unicode `Character` with codes **U+1F1EA U+1F1EA** as in `\u{1F1EA}\u{1F1EA}`, you will get , and the Unit Test [See GitHub project](https://github.com/SwiftArchitect/SO-41318999) will pass. – SwiftArchitect Jul 15 '18 at 04:33
  • This is a great solution @SwiftArchitect, thanks for sharing! I have tried to convert it to AppKit for a macOS application but wasn’t able to find a proper equivalent for `UIGraphicsBeginImageContext`. Would you mind sharing a macOS solution as well? – ixany May 26 '20 at 13:58
0

Just for future reference, after discovering my app had 13.2 emojis not supported in 12.x versions, I used the answer from here: How can I determine if a specific emoji character can be rendered by an iOS device? which worked really well for me.

lorg
  • 1,160
  • 1
  • 10
  • 27