63

I'm looking for a quick way to turn something like:

let germany = "DE" 

into

let flag = "\u{1f1e9}\u{1f1ea}"

ie, what's the mapping of D to 1f1e9 and E to 1f1ea I was looking at .utf8 for the string, but this returns an integer.

FWIW my general goal is to be able to take an arbitrary country code and get the corresponding emoji flag.

EDIT: I'm also fine with just holding a table that does this mapping if its available somewhere. I googled around but didn't find it.

9 Answers9

113

Here's a general formula for turning a two-letter country code into its emoji flag:

func flag(country:String) -> String {
    let base = 127397
    var usv = String.UnicodeScalarView()
    for i in country.utf16 {
        usv.append(UnicodeScalar(base + Int(i)))
    }
    return String(usv)
}

let s = flag("DE")

EDIT Ooops, no need to pass through the nested String.UnicodeScalarView struct. It turns out that String has an append method for precisely this purpose. So:

func flag(country:String) -> String { 
    let base : UInt32 = 127397
    var s = ""
    for v in country.unicodeScalars {
        s.append(UnicodeScalar(base + v.value))
    }
    return s
}

EDIT Oooops again, in Swift 3 they took away the ability to append a UnicodeScalar to a String, and they made the UnicodeScalar initializer failable (Xcode 8 seed 6), so now it looks like this:

func flag(country:String) -> String {
    let base : UInt32 = 127397
    var s = ""
    for v in country.unicodeScalars {
        s.unicodeScalars.append(UnicodeScalar(base + v.value)!)
    }
    return String(s)
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Oh, one more thing: your question title is poor. Try to revise it so that people solving this same problem (turn a country name into a flag) can find it. Thanks. – matt May 22 '15 at 18:13
  • 2
    Obscure magic constants ? :) – What about something like `let regionalA = "".unicodeScalars ; let letterA = "A".unicodeScalars ; let base = regionalA[regionalA.startIndex].value - letterA[letterA.startIndex].value` ? (Can probably be simplified.) – Martin R May 22 '15 at 18:38
  • @MartinR If we reveal how I arrived at the obscure magic constant, it doesn't look so magic any more! Actually all I did was subtract the ASCII value of "D" from the first codepoint of the German flag string that the OP started with. – matt May 22 '15 at 18:55
  • @matt bah, I had a way to get it but Swift's typing is really annoying. ie this is fine Int(UnicodeScalar("D").value) but if you pass in a variable that represents the letter, then it won't accept it. Great job Matt, much appreciated. –  May 22 '15 at 19:11
  • "this is fine `Int(UnicodeScalar("D").value)` but if you pass in a variable that represents the letter, then it won't accept it." Correct; I explain that here: http://stackoverflow.com/a/30333950/341994 – matt May 22 '15 at 19:18
  • Turns out there's a better way; edited my answer to demonstrate. – matt Jun 19 '15 at 17:59
  • 8
    I absolutely LOVE that the Emoji guys actually thought about adding the flags to the character set based on an indexing/ordering of the actual ISO country codes! – Stijn Mar 31 '16 at 13:53
  • 2
    See https://en.wikipedia.org/wiki/Regional_Indicator_Symbol The flags start at code point 0x1F1E6. The offset for "A" is 65. 0x1F1E6 - 65 = 127397 – markiv Apr 27 '16 at 11:54
  • @markiv maybe a stupid question but how do come to the 65 offset? – user1007522 Jul 29 '16 at 06:34
  • @matt I created an extension with all the ISO-2 Alpha country codes to return a flag from a string based on your answer. Thank you! https://github.com/AdrianBinDC/FlagExtension – Adrian Nov 30 '16 at 16:27
  • 1
    If it's based on my answer, @Adrian, your code should _mention_ my answer. Just include a link and acknowledge the debt. – matt Nov 30 '16 at 16:28
  • @ludovic-landry don't do that please – matt Aug 31 '17 at 06:09
  • @matt Why keep something that is irrelevant in the answer - and remove something that can be useful? :( – Ludovic Landry Sep 02 '17 at 10:20
  • 2
    @LudovicLandry why change history? The answer developed along with Swift, over many years. Don't turn it into a lie about the past. If you don't like it, down vote it, or give a better answer of your own, but don't put words into my mouth. – matt Sep 02 '17 at 12:08
  • 1
    @matt I see, I guess I mixed wikipedia with stackoverflow ways of dealing with this. – Ludovic Landry Sep 02 '17 at 20:11
  • I want to write a England flag. According to this page, the code needs to be XE. But unfortunately I can not view it. Where can I find the English Flag Country code? https://emojipedia.org/flag-for-england/ – Zebedee Aug 18 '18 at 18:56
  • @Zebedee That has nothing to do with this question-answer. Please ask a new separate question. – matt Aug 18 '18 at 19:04
  • How can I actually find out if there is no emoji flag for the 2-letter code I'm searching for??? – blackjacx Nov 23 '21 at 19:03
  • I am getting some wired stuff on view when trying to put this into Text(flag(Locale.current.identifier)) in swiftui. Any idea? Wired stuff are here: https://ibb.co/1n4sdRp – pregmatch Jun 17 '23 at 15:56
  • 1
    @pregmatch If you have a new question, use the Ask Question button at the top right. – matt Jun 17 '23 at 15:57
28

If anyone looking for solution in ObjectiveC here is convenient category:

@interface NSLocale (RREmoji)

+ (NSString *)emojiFlagForISOCountryCode:(NSString *)countryCode;

@end


@implementation NSLocale (RREmoji)


+ (NSString *)emojiFlagForISOCountryCode:(NSString *)countryCode {
    NSAssert(countryCode.length == 2, @"Expecting ISO country code");

    int base = 127462 -65;

    wchar_t bytes[2] = {
        base +[countryCode characterAtIndex:0],
        base +[countryCode characterAtIndex:1]
    };

    return [[NSString alloc] initWithBytes:bytes
                                    length:countryCode.length *sizeof(wchar_t)
                                  encoding:NSUTF32LittleEndianStringEncoding];
}


@end

test:

for ( NSString *countryCode in [NSLocale ISOCountryCodes] ) {
    NSLog(@"%@ - %@", [NSLocale emojiFlagForISOCountryCode:countryCode], countryCode);
}

output: - AD - AE - AF - AG - AI ...

RolandasR
  • 3,030
  • 2
  • 25
  • 26
20

I know this is not exactly what was asked, but maybe it will help someone:

let flags: [String: String] = [
  "AD": "", "AE": "", "AF": "", "AG": "", "AI": "", "AL": "", "AM": "", "AO": "", "AQ": "", "AR": "", "AS": "", "AT": "", "AU": "", "AW": "", "AX": "", "AZ": "", "BA": "", "BB": "", "BD": "", "BE": "", "BF": "", "BG": "", "BH": "", "BI": "", "BJ": "", "BL": "", "BM": "", "BN": "", "BO": "", "BQ": "", "BR": "", "BS": "", "BT": "", "BV": "", "BW": "", "BY": "", "BZ": "", "CA": "", "CC": "", "CD": "", "CF": "", "CG": "", "CH": "", "CI": "", "CK": "", "CL": "", "CM": "", "CN": "", "CO": "", "CR": "", "CU": "", "CV": "", "CW": "", "CX": "", "CY": "", "CZ": "", "DE": "", "DJ": "", "DK": "", "DM": "", "DO": "", "DZ": "", "EC": "", "EE": "", "EG": "", "EH": "", "ER": "", "ES": "", "ET": "", "FI": "", "FJ": "", "FK": "", "FM": "", "FO": "", "FR": "", "GA": "", "GB": "", "GD": "", "GE": "", "GF": "", "GG": "", "GH": "", "GI": "", "GL": "", "GM": "", "GN": "", "GP": "", "GQ": "", "GR": "", "GS": "", "GT": "", "GU": "", "GW": "", "GY": "", "HK": "", "HM": "", "HN": "", "HR": "", "HT": "", "HU": "", "ID": "", "IE": "", "IL": "", "IM": "", "IN": "", "IO": "", "IQ": "", "IR": "", "IS": "", "IT": "", "JE": "", "JM": "", "JO": "", "JP": "", "KE": "", "KG": "", "KH": "", "KI": "", "KM": "", "KN": "", "KP": "", "KR": "", "KW": "", "KY": "", "KZ": "", "LA": "", "LB": "", "LC": "", "LI": "", "LK": "", "LR": "", "LS": "", "LT": "", "LU": "", "LV": "", "LY": "", "MA": "", "MC": "", "MD": "", "ME": "", "MF": "", "MG": "", "MH": "", "MK": "", "ML": "", "MM": "", "MN": "", "MO": "", "MP": "", "MQ": "", "MR": "", "MS": "", "MT": "", "MU": "", "MV": "", "MW": "", "MX": "", "MY": "", "MZ": "", "NA": "", "NC": "", "NE": "", "NF": "", "NG": "", "NI": "", "NL": "", "NO": "", "NP": "", "NR": "", "NU": "", "NZ": "", "OM": "", "PA": "", "PE": "", "PF": "", "PG": "", "PH": "", "PK": "", "PL": "", "PM": "", "PN": "", "PR": "", "PS": "", "PT": "", "PW": "", "PY": "", "QA": "", "RE": "", "RO": "", "RS": "", "RU": "", "RW": "", "SA": "", "SB": "", "SC": "", "SD": "", "SE": "", "SG": "", "SH": "", "SI": "", "SJ": "", "SK": "", "SL": "", "SM": "", "SN": "", "SO": "", "SR": "", "SS": "", "ST": "", "SV": "", "SX": "", "SY": "", "SZ": "", "TC": "", "TD": "", "TF": "", "TG": "", "TH": "", "TJ": "", "TK": "", "TL": "", "TM": "", "TN": "", "TO": "", "TR": "", "TT": "", "TV": "", "TW": "", "TZ": "", "UA": "", "UG": "", "UM": "", "US": "", "UY": "", "UZ": "", "VA": "", "VC": "", "VE": "", "VG": "", "VI": "", "VN": "", "VU": "", "WF": "", "WS": "", "YE": "", "YT": "", "ZA": "", "ZM": "", "ZW": ""
]
budiDino
  • 13,044
  • 8
  • 95
  • 91
  • 1
    I'd be curious how a simple lookup dictionary like this performs vs. all the string operations the other answers contain. – Clifton Labrum Aug 24 '21 at 20:44
14

Two optimizations of matt's answer.

  • No need to pass through the nested String in Swift 4
  • To avoid pass lower case string, I added uppercased()

Here is the code.

func flag(from country:String) -> String {
    let base : UInt32 = 127397
    var s = ""
    for v in country.uppercased().unicodeScalars {
        s.unicodeScalars.append(UnicodeScalar(base + v.value)!)
    }
    return s
}
Lumialxk
  • 6,239
  • 6
  • 24
  • 47
14

Another function for turning a two-letter country code into its emoji flag using Swift 5.

internal func getFlag(from countryCode: String) -> String {
    countryCode
        .unicodeScalars
        .map({ 127397 + $0.value })
        .compactMap(UnicodeScalar.init)
        .map(String.init)
        .joined()
}
Community
  • 1
  • 1
habibiboss
  • 321
  • 4
  • 7
7

I use this implementation

func countryFlag(_ countryCode: String) -> String {
    let flagBase = UnicodeScalar("").value - UnicodeScalar("A").value

    let flag = countryCode
        .uppercased()
        .unicodeScalars
        .compactMap({ UnicodeScalar(flagBase + $0.value)?.description })
        .joined()
    return flag
}

Now we can test this function with this approach in the Playground

let isoCodes = Locale.isoRegionCodes
for isoCode in isoCodes {
    print(countryFlag(isoCode))
}

and we get

Alexander Khitev
  • 6,417
  • 13
  • 59
  • 115
4

To give more insight into matt answer

Swift 2 version

public static func flag(countryCode: String) -> Character {
    let base = UnicodeScalar("").value - UnicodeScalar("A").value

    let string = countryCode.uppercaseString.unicodeScalars.reduce("") {
      var string = $0
      string.append(UnicodeScalar(base + $1.value))
      return string
    }

    return Character(string)
  }

Swift 3 version, taken from https://github.com/onmyway133/Smile/blob/master/Sources/Smile.swift#L52

public func emoji(countryCode: String) -> Character {
  let base = UnicodeScalar("").value - UnicodeScalar("A").value

  var string = ""
  countryCode.uppercased().unicodeScalars.forEach {
    if let scala = UnicodeScalar(base + $0.value) {
      string.append(String(describing: scala))
    }
  }

  return Character(string)
}
onmyway133
  • 45,645
  • 31
  • 257
  • 263
2

You can use this chained function:

"de" // The short code
  .uppercased()
  .unicodeScalars
  .compactMap { UnicodeScalar(127397 + $0.value) }
  .reduce(into: "") { $0.unicodeScalars.append($1) }
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
1

For a more functional approach, using no mutable variables, use this:

private func flag(country: String) -> String {
    let base: UInt32 = 127397
    return country.unicodeScalars
        .flatMap({ UnicodeScalar(base + $0.value) })
        |> String.UnicodeScalarView.init
        |> String.init
}

Where the |> operator is the function application operator, working like a "pipe" for a more natural reading order: We take the scalars, map them into new scalars, turn that into a view, and that into a string.

It's defined like so:

infix operator |> : MultiplicationPrecedence
func |> <T, U>(left: T, right: (T) -> U) -> U {
    return right(left)
}

Without custom operators, we can still do without mutable state, like so:

private func flag(country: String) -> String {
    let base: UInt32 = 127397
    return String(String.UnicodeScalarView(
        country.unicodeScalars.flatMap({ UnicodeScalar(base + $0.value) })
    ))
}

But IMHO, this reads a little "backwards", since the natural flow of operations read neither out-in, nor in-out, but a little bit of both.

Svein Halvor Halvorsen
  • 2,474
  • 1
  • 16
  • 14