4

Swift Tricks: Emoji Flags

 3 years ago
source link: https://www.timekl.com/blog/2017/08/31/swift-tricks-emoji-flags/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Swift Tricks: Emoji Flags

Toying around with some code on the bus this morning, I came across an interesting fact about region flag emoji. Among the thousands of emoji that the Unicode standard defines, 270 of them represent region flags, each corresponding to a two-character region code: “us” means 🇺🇸, for example.

My curiosity was this: is there a way to programmatically generate the flag emoji 🇺🇸 from the string "us"? I was afraid that, like many other emoji, the flags would each have a unique name — something like REGIONAL FLAG UNITED STATES — and would require a lookup table to translate between the basic string "us" and the resulting emoji. Not so!

It turns out each flag is defined as a two-character sequence of Regional Indicator Symbols, each of which corresponds to a letter A-Z. When rendered by a sufficiently smart text engine, a valid pair of these symbols turns into the flag for the region named by the two-character code.

This meant that turning a two-character ASCII-alphabetic string into a flag emoji was an algorithmic affair, rather than one requiring a large lookup table. Roughly speaking, we’d want to:

  • Take the input string, lowercase it, and make sure its two characters fall between 'a' and 'z'
  • Translate each character from the ASCII lowercase alphabet range (0x610x7A) up to the Regional Indicator Symbol range (0x1F1E60x1F1FF)
  • Return the two-character string containing both translated indicator symbols

This seemed easy enough to just try out, with a couple extra precautions about input thrown in. First I started with the translation bit:

func isLowercaseASCIIScalar(_ scalar: Unicode.Scalar) -> Bool {
    return scalar.value >= 0x61 && scalar.value <= 0x7A
}

func regionalIndicatorSymbol(for scalar: Unicode.Scalar) -> Unicode.Scalar {
    precondition(isLowercaseASCIIScalar(scalar))
    
    // 0x1F1E6 marks the start of the Regional Indicator Symbol range and corresponds to 'A'
    // 0x61 marks the start of the lowercase ASCII alphabet: 'a'
    return Unicode.Scalar(scalar.value + (0x1F1E6 - 0x61))!
}

With the per-character work out of the way, we can provide a convenience function on String to perform a few extra checks and do the wholesale translation:

func emojiFlag(for countryCode: String) -> String! {
    let lowercasedCode = countryCode.lowercased()
    guard lowercasedCode.characters.count == 2 else { return nil }
    guard lowercasedCode.unicodeScalars.reduce(true, { accum, scalar in accum && isLowercaseASCIIScalar(scalar) }) else { return nil }
    
    let indicatorSymbols = lowercasedCode.unicodeScalars.map({ regionalIndicatorSymbol(for: $0) })
    return String(indicatorSymbols.map({ Character($0) }))
}

Just like that, we’ve got a programmatic translation from "us" to 🇺🇸! Let’s try it out with a few extra cases:

emojiFlag(for: "de") // 🇩🇪
emojiFlag(for: "is") // 🇮🇸

We can even get crazy, and ask for the flag equivalent of every ISO region code that Apple includes on Locale:

Locale.isoRegionCodes.map({ emojiFlag(for: $0)! })

At the time of writing, this returns a 257-element array of flags. Funnily enough, Wikipedia claims there are only 256 “regular regions,” but two “macroregion sequences” — I wonder which of these Apple is including?

You can download all this code in playground form and test it out yourself. Enjoy your newfound multiregionalism!


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK