Fix category names to match Apple categories (#5)
This commit is contained in:
committed by
GitHub
parent
5766292d31
commit
b57c28995b
@@ -50,6 +50,12 @@
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "download ."
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -24,7 +24,7 @@ https://github.com/niklasamslgruber/EmojiKit
|
||||
|
||||
### Usage
|
||||
|
||||
The SPM package provides an easy-to-use `EmojiManager` that parses the stored emoji files and returns them as an Array of `EmojiCategory`. Each category is the official unicode category and includes all emojis that are assigned to this category. In total these are 10 categories while the `.component` category can be ignored in most cases.
|
||||
The SPM package provides an easy-to-use `EmojiManager` that parses the stored emoji files and returns them as an Array of `EmojiCategory`. Each category is the category that Apple uses on any Apple platform and includes all emojis that are assigned to this category. In total these are 8 categories.
|
||||
|
||||
#### Get all supported emojis
|
||||
```swift
|
||||
|
||||
56
Sources/EmojiKit/AppleEmojiCategory.swift
Normal file
56
Sources/EmojiKit/AppleEmojiCategory.swift
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// AppleEmojiCategory.swift
|
||||
//
|
||||
//
|
||||
// Created by Niklas Amslgruber on 19.02.24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class AppleEmojiCategory: Codable {
|
||||
|
||||
public enum Name: String, CaseIterable, Codable {
|
||||
case flags = "Flags"
|
||||
case activity = "Activity"
|
||||
case objects = "Objects"
|
||||
case travelAndPlaces = "Travel & Places"
|
||||
case symbols = "Symbols"
|
||||
case animalsAndNature = "Animals & Nature"
|
||||
case foodAndDrink = "Food & Drink"
|
||||
case smileysAndPeople = "Smileys & People"
|
||||
|
||||
public static var orderedCases: [Name] {
|
||||
return allCases.sorted(by: { $0.order < $1.order })
|
||||
}
|
||||
|
||||
public var order: Int {
|
||||
switch self {
|
||||
case .flags:
|
||||
return 8
|
||||
case .activity:
|
||||
return 4
|
||||
case .objects:
|
||||
return 6
|
||||
case .travelAndPlaces:
|
||||
return 5
|
||||
case .symbols:
|
||||
return 7
|
||||
case .animalsAndNature:
|
||||
return 2
|
||||
case .foodAndDrink:
|
||||
return 3
|
||||
case .smileysAndPeople:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public let name: Name
|
||||
public var values: [String]
|
||||
|
||||
public init(name: Name, values: [String]) {
|
||||
self.name = name
|
||||
self.values = values
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,8 +7,10 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias EmojiCategory = AppleEmojiCategory
|
||||
|
||||
public enum EmojiManager {
|
||||
|
||||
|
||||
public enum Version: Double {
|
||||
case v13_1 = 13.1
|
||||
case v14 = 14
|
||||
@@ -31,7 +33,7 @@ public enum EmojiManager {
|
||||
return "15.1"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static func getSupportedVersion() -> Version {
|
||||
if #available(iOS 17.4, *) {
|
||||
return .v15_1
|
||||
@@ -48,34 +50,55 @@ public enum EmojiManager {
|
||||
/// 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)
|
||||
/// - showAllVariations: Some emojis inlcude skin type variations which increases the number of emojis drastically. (default: only the yellow neutral emojis are returned)
|
||||
/// - showAllVariations: Some emojis include skin type variations which increases the number of emojis drastically. (default: only the yellow neutral emojis are returned)
|
||||
/// - url: Specify the location of the `emoji_v<version_number>.json` files if needed (default: bundle resource path)
|
||||
/// - Returns: Array of categories with all emojis that are assigned to each category
|
||||
public static func getAvailableEmojis(version: Version = .getSupportedVersion(), showAllVariations: Bool = false, at url: URL? = nil) -> [EmojiCategory] {
|
||||
let fileUrl = url ?? Bundle.module.url(forResource: version.fileName, withExtension: "json")
|
||||
if let url = fileUrl, let content = try? Data(contentsOf: url), let result = try? JSONDecoder().decode([EmojiCategory].self, from: content) {
|
||||
var filteredEmojis: [EmojiCategory] = []
|
||||
if let url = fileUrl, let content = try? Data(contentsOf: url), let result = try? JSONDecoder().decode([UnicodeEmojiCategory].self, from: content) {
|
||||
var filteredEmojis: [UnicodeEmojiCategory] = []
|
||||
var appleCategories: [AppleEmojiCategory] = []
|
||||
for category in result {
|
||||
let supportedEmojis = category.values.filter({
|
||||
showAllVariations ? true : isNeutralEmoji(for: $0)
|
||||
})
|
||||
filteredEmojis.append(EmojiCategory(name: category.name, values: supportedEmojis))
|
||||
let unicodeCategory = UnicodeEmojiCategory(name: category.name, values: supportedEmojis)
|
||||
filteredEmojis.append(unicodeCategory)
|
||||
|
||||
if shouldMergeCategory(category), let index = appleCategories.firstIndex(where: { $0.name == .smileysAndPeople }) {
|
||||
if category.name == .smileysAndEmotions {
|
||||
let oldValues = appleCategories[index].values
|
||||
appleCategories[index].values = supportedEmojis
|
||||
appleCategories[index].values.append(contentsOf: oldValues)
|
||||
} else {
|
||||
appleCategories[index].values.append(contentsOf: supportedEmojis)
|
||||
}
|
||||
} else {
|
||||
guard let appleCategory = unicodeCategory.appleCategory else {
|
||||
continue
|
||||
}
|
||||
appleCategories.append(AppleEmojiCategory(name: appleCategory, values: supportedEmojis))
|
||||
}
|
||||
}
|
||||
return filteredEmojis
|
||||
return appleCategories.sorted(by: { $0.name.order < $1.name.order })
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
private static func shouldMergeCategory(_ category: UnicodeEmojiCategory) -> Bool {
|
||||
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"]
|
||||
|
||||
|
||||
for color in colors where unicodes.contains(color) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private static func getUnicodes(emoji: String) -> [String] {
|
||||
let unicodeScalars = emoji.unicodeScalars
|
||||
let unicodes = unicodeScalars.map { $0.value }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// EmojiCategory.swift
|
||||
// UnicodeEmojiCategory.swift
|
||||
//
|
||||
//
|
||||
// Created by Niklas Amslgruber on 10.06.23.
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public class EmojiCategory: Codable {
|
||||
public class UnicodeEmojiCategory: Codable {
|
||||
|
||||
public enum Name: String, CaseIterable, Codable {
|
||||
case flags = "Flags"
|
||||
@@ -21,47 +21,39 @@ public class EmojiCategory: Codable {
|
||||
case foodAndDrink = "Food & Drink"
|
||||
case smileysAndEmotions = "Smileys & Emotion"
|
||||
|
||||
public static var orderedCases: [EmojiCategory.Name] {
|
||||
return EmojiCategory.Name.allCases.sorted(by: { $0.order < $1.order })
|
||||
}
|
||||
|
||||
// The component category does not include relevant emojis
|
||||
public static var relevantCases: [EmojiCategory.Name] {
|
||||
return EmojiCategory.Name.orderedCases.filter({ $0 != .components })
|
||||
}
|
||||
|
||||
// Order that Apple uses in their emoji picker
|
||||
public var order: Int {
|
||||
var appleName: AppleEmojiCategory.Name? {
|
||||
switch self {
|
||||
case .flags:
|
||||
return 10
|
||||
return .flags
|
||||
case .activities:
|
||||
return 5
|
||||
return .activity
|
||||
case .components:
|
||||
return 8
|
||||
return nil
|
||||
case .objects:
|
||||
return 7
|
||||
return .objects
|
||||
case .travelAndPlaces:
|
||||
return 6
|
||||
return .travelAndPlaces
|
||||
case .symbols:
|
||||
return 9
|
||||
return .symbols
|
||||
case .peopleAndBody:
|
||||
return 2
|
||||
return .smileysAndPeople
|
||||
case .animalsAndNature:
|
||||
return 3
|
||||
return .animalsAndNature
|
||||
case .foodAndDrink:
|
||||
return 4
|
||||
return .foodAndDrink
|
||||
case .smileysAndEmotions:
|
||||
return 1
|
||||
return .smileysAndPeople
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public let name: Name
|
||||
public let values: [String]
|
||||
public let appleCategory: AppleEmojiCategory.Name?
|
||||
public var values: [String]
|
||||
|
||||
public init(name: Name, values: [String]) {
|
||||
self.name = name
|
||||
self.appleCategory = name.appleName
|
||||
self.values = values
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,9 @@ struct EmojiDownloader: ParsableCommand, AsyncParsableCommand {
|
||||
let parser = UnicodeParser()
|
||||
|
||||
do {
|
||||
let emojisByCategory: [EmojiCategory] = try await parser.parseEmojiList(for: emojiListURL)
|
||||
let emojisByCategory: [UnicodeEmojiCategory] = try await parser.parseEmojiList(for: emojiListURL)
|
||||
|
||||
let emojiCounts: [EmojiCategory.Name: Int] = parser.parseCountHTML(for: emojiCountsURL)
|
||||
let emojiCounts: [UnicodeEmojiCategory.Name: Int] = parser.parseCountHTML(for: emojiCountsURL)
|
||||
|
||||
for category in emojisByCategory {
|
||||
assert(emojiCounts[category.name] == category.values.count)
|
||||
@@ -94,7 +94,7 @@ struct EmojiDownloader: ParsableCommand, AsyncParsableCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private func save(data: [EmojiCategory], for: EmojiManager.Version) {
|
||||
private func save(data: [UnicodeEmojiCategory], for: EmojiManager.Version) {
|
||||
let directory = getPath()
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
|
||||
@@ -18,10 +18,10 @@ class UnicodeParser {
|
||||
case minimallyQualified = "minimally-qualified"
|
||||
}
|
||||
|
||||
func parseEmojiList(for fileUrl: URL) async throws -> [EmojiCategory] {
|
||||
func parseEmojiList(for fileUrl: URL) async throws -> [UnicodeEmojiCategory] {
|
||||
let handle = try FileHandle(forReadingFrom: fileUrl)
|
||||
var currentGroup: EmojiCategory.Name = .activities
|
||||
var emojisByGroup: [EmojiCategory.Name: [String]] = [:]
|
||||
var currentGroup: UnicodeEmojiCategory.Name = .activities
|
||||
var emojisByGroup: [UnicodeEmojiCategory.Name: [String]] = [:]
|
||||
|
||||
for try await line in handle.bytes.lines {
|
||||
|
||||
@@ -34,7 +34,7 @@ class UnicodeParser {
|
||||
if isLine(line, ofType: .group) {
|
||||
let name = line.split(separator: ":")
|
||||
let categoryName = name.last?.trim() ?? ""
|
||||
guard let category = EmojiCategory.Name(rawValue: categoryName) else {
|
||||
guard let category = UnicodeEmojiCategory.Name(rawValue: categoryName) else {
|
||||
continue
|
||||
}
|
||||
currentGroup = category
|
||||
@@ -79,15 +79,15 @@ class UnicodeParser {
|
||||
}
|
||||
try handle.close()
|
||||
|
||||
var result: [EmojiCategory] = []
|
||||
var result: [UnicodeEmojiCategory] = []
|
||||
|
||||
for category in EmojiCategory.Name.allCases {
|
||||
result.append(EmojiCategory(name: category, values: emojisByGroup[category] ?? []))
|
||||
for category in UnicodeEmojiCategory.Name.allCases {
|
||||
result.append(UnicodeEmojiCategory(name: category, values: emojisByGroup[category] ?? []))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func parseCountHTML(for url: URL) -> [EmojiCategory.Name: Int] {
|
||||
func parseCountHTML(for url: URL) -> [UnicodeEmojiCategory.Name: Int] {
|
||||
do {
|
||||
let html = try String(contentsOf: url)
|
||||
let doc: Document = try SwiftSoup.parse(html)
|
||||
@@ -104,14 +104,14 @@ class UnicodeParser {
|
||||
return [:]
|
||||
}
|
||||
|
||||
var categoryNames: [EmojiCategory.Name] = []
|
||||
var categoryNames: [UnicodeEmojiCategory.Name] = []
|
||||
var countNumbers: [Int] = []
|
||||
|
||||
for categoryElement in categoryEntries {
|
||||
if categoryElement == categoryEntries.first || categoryElement == categoryEntries.last {
|
||||
continue
|
||||
}
|
||||
guard let text = try? categoryElement.text(), let category = EmojiCategory.Name(rawValue: text) else {
|
||||
guard let text = try? categoryElement.text(), let category = UnicodeEmojiCategory.Name(rawValue: text) else {
|
||||
continue
|
||||
}
|
||||
categoryNames.append(category)
|
||||
@@ -127,7 +127,7 @@ class UnicodeParser {
|
||||
countNumbers.append(number)
|
||||
}
|
||||
|
||||
var result: [EmojiCategory.Name: Int] = [:]
|
||||
var result: [UnicodeEmojiCategory.Name: Int] = [:]
|
||||
|
||||
for (index, categoryName) in categoryNames.enumerated() {
|
||||
result[categoryName] = countNumbers[index]
|
||||
|
||||
Reference in New Issue
Block a user