Using Emoji Skin-Tone Modifiers in Swift

Since iOS 8.3 was released in April 2015, it has been possible to select the skin tone for a number of Emoji characters. While it is fairly trivial to select these from the iOS keyboard, doing so programmatically is a bit trickier.

Firstly, each skin tone has its own modifier character. If a modifier isn't used, then the default yellow skin tone is used.

let modifiers = [ "🏻", "🏼", "🏽", "🏾", "🏿" ]

The modifier can be applied by concatenating it to the Emoji. In other words, "πŸ‘" + "🏻" = "πŸ‘πŸ»" (note that the other way around will not work in the same way: "🏻" + "πŸ‘" = "πŸ»πŸ‘").

for modifier in modifiers {
    let modified = "πŸ‘" + modifier
    print(modified)
}

// Prints πŸ‘πŸ»πŸ‘πŸΌπŸ‘πŸ½πŸ‘πŸΎπŸ‘πŸΏ

Things get tricker though. For example, measuring the length of a modified versus the unmodified string:

print("πŸ‘".characters.count) // Prints 1
print("πŸ‘πŸΏ".characters.count) // Prints 2

This complicates things if you want to determine the length of a string that contains a modified Emoji. Instead, you need to count composed character sequences using String.enumerateSubstringsInRange().

The following string extension simplifies this:

import UIKit

extension String {
    var emojiVisibleLength: Int {
        var count = 0

        enumerateSubstringsInRange(startIndex ..< endIndex, options: .ByComposedCharacterSequences) {_ in
            count++
        }

        return count
    }
}

Now you can determine the visible length correctly:

print("πŸ‘".emojiVisibleLength) // Prints 1
print("πŸ‘πŸΏ".emojiVisibleLength) // Prints 1

If you want to revert a modified Emoji back to its unmodified version you use the following, which simply keeps the first character in the String:

import UIKit

extension String {
    var emojiUnmodified: String {
        if self.characters.count == 0 {
            return ""
        }
        
        let range = Range<String.Index>(start: self.startIndex, end: self.startIndex.advancedBy(1))
        return self[range]
    }
}

You can use this as follows:

print("πŸ‘πŸΏ".emojiUnmodified) // Prints πŸ‘

This is important, because it doesn't make sense to apply a skin-tone modifier to an emoji that has already been modified:

print("πŸ‘" + "🏾") // Prints πŸ‘πŸΎ
print("πŸ‘πŸΏ" + "🏾") // Prints πŸ‘πŸΏπŸΎ
print("πŸ‘πŸΏ".emojiUnmodified + "🏾") // Prints πŸ‘πŸΎ

What if you want to check if an Emoji can be modified? To achieve this, first list the available modifiers so they can be easily accessed.

Checking if an Emoji can be modified involves appending one of the modifiers and seeing if its visible length changes. If it does, the Emoji cannot be modified.

import UIKit

extension String {
    var emojiSkinToneModifiers: [String] {
        return [ "🏻", "🏼", "🏽", "🏾", "🏿" ]
    }

    var canHaveSkinToneModifier: Bool {
        if self.characters.count == 0 {
            return false
        }

        // Just pick one of the available modifiers, doesn't matter which one
        let modified = self.emojiUnmodified + self.emojiSkinToneModifiers[0]

        // If it can be modified the visible length will remain 1
        return modified.emojiVisibleLength == 1
    }
}

You can use this as follows:

let emojis = [ "πŸ‘", "πŸ‘πŸΏ", "🐸" ]

for emoji in emojis {

    if emoji.canHaveSkinToneModifier {
        print("A")
    }
    else {
        print("B")
    }
}

// Prints AAB

Piecing this string extension together looks as follows:

import UIKit

extension String {
    var emojiSkinToneModifiers: [String] {
        return [ "🏻", "🏼", "🏽", "🏾", "🏿" ]
    }
    
    var emojiVisibleLength: Int {
        var count = 0
        enumerateSubstringsInRange(startIndex..<endIndex, options: .ByComposedCharacterSequences) {_ in count++}
        return count
    }
    
    var emojiUnmodified: String {
        if self.characters.count == 0 {
            return ""
        }
        
        let range = Range<String.Index>(start: self.startIndex, end: self.startIndex.advancedBy(1))
        return self[range]
    }
    
    var canHaveSkinToneModifier: Bool {
        if self.characters.count == 0 {
            return false
        }

        let modified = self.emojiUnmodified + self.emojiSkinToneModifiers[0]
        return modified.emojiVisibleLength == 1
    }
}

Save this code to a file called EmojiStringExtension.swift (or something similar), then you can use it as follows:

let emojis = [ "πŸ‘", "πŸ‘πŸΏ", "🐸" ]

for emoji in emojis {

    if emoji.canHaveSkinToneModifier {
        // Ensure the unmodified Emoji is being used
        let unmodified = emoji.emojiUnmodified

        print(emoji + ": ")

        for modifier in emoji.emojiSkinToneModifiers {
            print(unmodified + modifier)
        }
    }
    else {
        print(emoji)
    }
}

/*
    Output:
        πŸ‘: πŸ‘πŸ»πŸ‘πŸΌπŸ‘πŸ½πŸ‘πŸΎπŸ‘πŸΏ
        πŸ‘πŸΏ: πŸ‘πŸ»πŸ‘πŸΌπŸ‘πŸ½πŸ‘πŸΎπŸ‘πŸΏ
        🐸
*/

To that, I say, πŸ‘πŸ˜€.