Compare commits
3 Commits
emoji-keyw
...
0.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
dbda721fba
|
|||
|
719d405244
|
|||
|
1f1b96a73f
|
6
LICENSE
6
LICENSE
@@ -1,7 +1,13 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Terry Yiu
|
||||
|
||||
Fork of https://github.com/niklasamslgruber/EmojiKit
|
||||
Copyright (c) 2023 Niklas Amslgruber
|
||||
|
||||
Apple emoji category name localizations forked from https://github.com/izyumkin/MCEmojiPicker
|
||||
Copyright (c) 2022 Ivan Izyumkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"state" : {
|
||||
"revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb",
|
||||
"version" : "1.1.0"
|
||||
"revision" : "ee97538f5b81ae89698fd95938896dec5217b148",
|
||||
"version" : "1.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ let package = Package(
|
||||
)
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMajor(from: "1.1.0")),
|
||||
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMajor(from: "1.1.1")),
|
||||
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"),
|
||||
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.6.0"),
|
||||
],
|
||||
|
||||
@@ -19,7 +19,7 @@ In most cases it is enough to just use the `EmojiKit` for your app. The `EmojiSo
|
||||
##### SwiftPM
|
||||
|
||||
```
|
||||
https://github.com/niklasamslgruber/EmojiKit
|
||||
https://github.com/tyiu/EmojiKit
|
||||
```
|
||||
|
||||
### Usage
|
||||
@@ -89,3 +89,8 @@ Currently only to Unicode releases are supported (Version 14 and 15). If you wan
|
||||
|
||||
In that case make sure that you added the `emojis_vX.json` file to your Xcode project. The file name must match the version you're trying to fetch emojis for, e.g. for version 12 the file name must be `emojis_v12.json`. Additionally make sure that your JSON file is added under `Build Phase - Copy Bundle Resources` for each target where you want to use the `EmojiManager`.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
This EmojiKit Swift package was forked from [niklasamslgruber/EmojiKit](https://github.com/niklasamslgruber/EmojiKit).
|
||||
|
||||
Some of the localization files for Apple emoji category names were copied from [izyumkin/MCEmojiPicker](https://github.com/izyumkin/MCEmojiPicker).
|
||||
|
||||
@@ -16,6 +16,7 @@ public class AppleEmojiCategory: Codable, Hashable {
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(name)
|
||||
hasher.combine(emojis)
|
||||
hasher.combine(variations)
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +67,12 @@ public class AppleEmojiCategory: Codable, Hashable {
|
||||
|
||||
public let name: Name
|
||||
public var emojis: OrderedDictionary<String, Emoji>
|
||||
public var variations: [String: [Emoji]]
|
||||
|
||||
public init(name: Name, emojis: OrderedDictionary<String, Emoji>) {
|
||||
public init(name: Name, emojis: OrderedDictionary<String, Emoji>, variations: [String: [Emoji]]) {
|
||||
self.name = name
|
||||
self.emojis = emojis
|
||||
self.variations = variations
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OrderedCollections
|
||||
|
||||
public typealias EmojiCategory = AppleEmojiCategory
|
||||
|
||||
@@ -47,6 +48,32 @@ public enum EmojiManager {
|
||||
}
|
||||
}
|
||||
|
||||
// When skin tone modifiers are stripped from some emojis,
|
||||
// they don't have the same Unicode scalar values as the neutral version.
|
||||
// We need to maintain a manual mapping so that the lists of variations are accurate.
|
||||
private static let emojiSpecialMapping: [[UInt32]: UInt32] = [
|
||||
[0x1FAF1, 0x200D, 0x1FAF2]: 0x1F91D, // 🤝 handshake
|
||||
[0x1F469, 0x200D, 0x1F91D, 0x200D, 0x1F469]: 0x1F46D, // 👭 women holding hands
|
||||
[0x1F469, 0x200D, 0x1F91D, 0x200D, 0x1F468]: 0x1F46B, // 👫 woman and man holding hands
|
||||
[0x1F468, 0x200D, 0x1F91D, 0x200D, 0x1F468]: 0x1F46C, // 👬 men holding hands
|
||||
[0x1F9D1, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F9D1]: 0x1F48F, // 💏 kiss: person, person
|
||||
[0x1F9D1, 0x200D, 0x2764, 0x200D, 0x1F9D1]: 0x1F491, // 💑 couple with heart: person, person
|
||||
]
|
||||
|
||||
private static func uint32ToEmoji(_ value: UInt32) -> String? {
|
||||
// Create a Unicode scalar from the UInt32 value
|
||||
guard let scalar = UnicodeScalar(value) else {
|
||||
print("Invalid Unicode scalar value")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Character from the Unicode scalar
|
||||
let character = Character(scalar)
|
||||
|
||||
// Convert the Character to a String and return it
|
||||
return String(character)
|
||||
}
|
||||
|
||||
/// Returns all emojis for a specific version
|
||||
/// - Parameters:
|
||||
/// - version: The specific version you want to fetch (default: the highest supported version for a device's iOS version)
|
||||
@@ -59,25 +86,49 @@ public enum EmojiManager {
|
||||
var filteredEmojis: [UnicodeEmojiCategory] = []
|
||||
var appleCategories: [AppleEmojiCategory] = []
|
||||
for category in result {
|
||||
let supportedEmojis = category.emojis.filter({
|
||||
showAllVariations ? true : isNeutralEmoji(for: $0.key)
|
||||
})
|
||||
var variations = [String: [Emoji]]()
|
||||
var supportedEmojis = OrderedDictionary<String, Emoji>()
|
||||
category.emojis.forEach {
|
||||
if isNeutralEmoji(for: $0.key) {
|
||||
supportedEmojis[$0.key] = $0.value
|
||||
} else if showAllVariations {
|
||||
var unqualifiedNeutralEmoji = unqualifiedNeutralEmoji(for: $0.key)
|
||||
|
||||
let unicodeScalars = unqualifiedNeutralEmoji.unicodeScalars.map { $0.value }
|
||||
if let actualUnqualifiedNeutralScalar = emojiSpecialMapping[unicodeScalars],
|
||||
let actualUnqualifiedNeutralEmoji = uint32ToEmoji(actualUnqualifiedNeutralScalar) {
|
||||
unqualifiedNeutralEmoji = String(actualUnqualifiedNeutralEmoji)
|
||||
}
|
||||
|
||||
if let variationsForEmoji = variations[unqualifiedNeutralEmoji] {
|
||||
variations[unqualifiedNeutralEmoji] = variationsForEmoji + [$0.value]
|
||||
} else {
|
||||
variations[unqualifiedNeutralEmoji] = [$0.value]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let unicodeCategory = UnicodeEmojiCategory(name: category.name, emojis: supportedEmojis)
|
||||
filteredEmojis.append(unicodeCategory)
|
||||
|
||||
if shouldMergeCategory(category), let index = appleCategories.firstIndex(where: { $0.name == .smileysAndPeople }) {
|
||||
if category.name == .smileysAndEmotions {
|
||||
let oldValues = appleCategories[index].emojis
|
||||
let oldEmojis = appleCategories[index].emojis
|
||||
appleCategories[index].emojis = supportedEmojis
|
||||
appleCategories[index].emojis.merge(oldValues) { (current, _) in current }
|
||||
appleCategories[index].emojis.merge(oldEmojis) { (current, _) in current }
|
||||
|
||||
let oldVariations = appleCategories[index].variations
|
||||
appleCategories[index].variations = variations
|
||||
appleCategories[index].variations.merge(oldVariations) { (current, _) in current }
|
||||
} else {
|
||||
appleCategories[index].emojis.merge(supportedEmojis) { (current, _) in current }
|
||||
appleCategories[index].variations.merge(variations) { (current, _) in current }
|
||||
}
|
||||
} else {
|
||||
guard let appleCategory = unicodeCategory.appleCategory else {
|
||||
continue
|
||||
}
|
||||
appleCategories.append(AppleEmojiCategory(name: appleCategory, emojis: supportedEmojis))
|
||||
appleCategories.append(AppleEmojiCategory(name: appleCategory, emojis: supportedEmojis, variations: variations))
|
||||
}
|
||||
}
|
||||
return appleCategories.sorted(by: { $0.name.order < $1.name.order })
|
||||
@@ -89,19 +140,32 @@ public enum EmojiManager {
|
||||
return category.name == .smileysAndEmotions || category.name == .peopleAndBody
|
||||
}
|
||||
|
||||
private static func isNeutralEmoji(for emoji: String) -> Bool {
|
||||
let unicodes = getUnicodes(emoji: emoji)
|
||||
let colors = ["1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF"]
|
||||
private static let skinToneRange: ClosedRange<UInt32> = 0x1F3FB...0x1F3FF
|
||||
|
||||
for color in colors where unicodes.contains(color) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
public static func isNeutralEmoji(for emojiValue: String) -> Bool {
|
||||
return emojiValue.unicodeScalars.allSatisfy { !skinToneRange.contains($0.value) }
|
||||
}
|
||||
|
||||
private static func getUnicodes(emoji: String) -> [String] {
|
||||
let unicodeScalars = emoji.unicodeScalars
|
||||
let unicodes = unicodeScalars.map { $0.value }
|
||||
return unicodes.map { String($0, radix: 16, uppercase: true) }
|
||||
public static func isSkinToneModifier(scalar: Unicode.Scalar) -> Bool {
|
||||
return skinToneRange.contains(scalar.value)
|
||||
}
|
||||
|
||||
public static func neutralEmoji(for emojiValue: String) -> String {
|
||||
let filteredScalars = emojiValue.unicodeScalars.filter { !isSkinToneModifier(scalar: $0) }
|
||||
return String(String.UnicodeScalarView(filteredScalars))
|
||||
}
|
||||
|
||||
public static func unqualifiedNeutralEmoji(for emoji: String) -> String {
|
||||
let variationSelector: Character = "\u{FE0F}"
|
||||
var unqualifiedEmoji = ""
|
||||
|
||||
for scalar in neutralEmoji(for: emoji).unicodeScalars {
|
||||
let character = Character(scalar)
|
||||
if character != variationSelector {
|
||||
unqualifiedEmoji.append(character)
|
||||
}
|
||||
}
|
||||
|
||||
return unqualifiedEmoji
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user