3

Following this post I tried to update the code from Swift 2.0 to Swift 5.0 to check which emojis have skin tones available or not and other variations already present. My updated code in detail:

extension String {
var emojiSkinToneModifiers: [String] {
    return [ "", "", "", "", "" ]
}

var emojiVisibleLength: Int {
    var count = 0
    enumerateSubstrings(in: startIndex..<endIndex, options: .byComposedCharacterSequences) { (_, _, _, _) in
        count = count + 1
    }
    return count
}

var emojiUnmodified: String {
    if self.count == 0 {
        return ""
    }
    let range = String(self[..<self.index(self.startIndex, offsetBy: 1)])
    return range
}

var canHaveSkinToneModifier: Bool {
    if self.count == 0 {
        return false
    }
    let modified = self.emojiUnmodified + self.emojiSkinToneModifiers[0]
    return modified.emojiVisibleLength == 1
}
}

And use this with an array:

let emojis = [ "", "", "" ]
for emoji in emojis {
   if emoji.canHaveSkinToneModifier {
        let unmodified = emoji.emojiUnmodified
        print(emoji)
        for modifier in emoji.emojiSkinToneModifiers {
            print(unmodified + modifier)
        }
    } else {
      print(emoji)
    }
 }

The output: assigns variations to emojis that do not have them or that already is instead of:

I suppose enumerateSubstringsInRange is incorrect and self.characters.count now became self.count easy and correct to count one emoji (composed) compared to before Swift 4 but maybe not useful in this case. What am I not seeing wrong? Thanks

Joannes
  • 2,569
  • 3
  • 17
  • 39
  • I do not have an answer at the moment, but note that as of Swift 4, extended grapheme clusters count as a single character, so that the result of `s.emojiVisibleLength` is the same as `s.count` (compare https://stackoverflow.com/q/39104152/1187415). – Martin R Aug 21 '19 at 13:31
  • Oops, what I meant is that *Emoji sequences* are counted as a single character in Swift 4 (and later). – Martin R Aug 21 '19 at 14:35
  • @MartinR what I had assumed too... A solution is welcome – Joannes Aug 21 '19 at 15:01

1 Answers1

3

A "hack" would be to compare the visual representation of a correct emoji (like "") and a wanna-be emoji (like "").

I've modified your code here and there to make it work:

extension String {
    static let emojiSkinToneModifiers: [String] =  ["", "", "", "", ""]

    var emojiVisibleLength: Int {
        var count = 0
        let nsstr = self as NSString
        let range = NSRange(location: 0, length: nsstr.length)
        nsstr.enumerateSubstrings(in: range,
                            options: .byComposedCharacterSequences)
        { (_, _, _, _) in

            count = count + 1
        }
        return count
    }

    var emojiUnmodified: String {
        if isEmpty {
            return self
        }
        let string = String(self.unicodeScalars.first!)
        return string
    }

    private static let emojiReferenceSize: CGSize = {
        let size = CGSize(width : CGFloat.greatestFiniteMagnitude,
                          height: CGFloat.greatestFiniteMagnitude)
        let rect = ("" as NSString).boundingRect(with: size,
                                                     options: .usesLineFragmentOrigin,
                                                     context: nil)
        return rect.size
    }()

    var canHaveSkinToneModifier: Bool {
        if isEmpty {
            return false
        }

        let modified = self.emojiUnmodified + String.emojiSkinToneModifiers[0]

        let size = (modified as NSString)
            .boundingRect(with: CGSize(width : CGFloat.greatestFiniteMagnitude,
                                       height: .greatestFiniteMagnitude),
                          options: .usesLineFragmentOrigin,
                          context: nil).size

        return size == String.emojiReferenceSize
    }
}

Let's try it out:

let emojis = [ "", "", "" ]
for emoji in emojis {
    if emoji.canHaveSkinToneModifier {
        let unmodified = emoji.emojiUnmodified
        print(unmodified)
        for modifier in String.emojiSkinToneModifiers {
            print(unmodified + modifier)
        }
    } else {
        print(emoji)
    }
    print("\n")
}

And voila!














ielyamani
  • 17,807
  • 10
  • 55
  • 90
  • Thanks, it could be a idea for and , but it doesn't make much sense to apply a skin-tone modifier to an emoji that has already been modified like . So for un output like , , ? Please check my first link in the question – Joannes Aug 21 '19 at 18:21
  • From swift 4 the count is "global". A solution would be a hack for a "true" count like up to swift 3. – Joannes Aug 21 '19 at 18:22
  • @Joannes In the link, at the bottom of the article, skin tones are applied to : `: `. So, do you want to apply skin tones to emoji that could have skin modifiers, but don't have one right now? like ``, ``...? – ielyamani Aug 21 '19 at 18:38
  • Thanks, today this is a solution – Joannes Aug 21 '19 at 19:55