diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/EmojiSourceKit.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/EmojiSourceKit.xcscheme index 463a67e..c411603 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/EmojiSourceKit.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/EmojiSourceKit.xcscheme @@ -50,6 +50,12 @@ ReferencedContainer = "container:"> + + + + 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.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 } diff --git a/Sources/EmojiKit/EmojiCategory.swift b/Sources/EmojiKit/UnicodeEmojiCategory.swift similarity index 56% rename from Sources/EmojiKit/EmojiCategory.swift rename to Sources/EmojiKit/UnicodeEmojiCategory.swift index e068644..5c28693 100644 --- a/Sources/EmojiKit/EmojiCategory.swift +++ b/Sources/EmojiKit/UnicodeEmojiCategory.swift @@ -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 } } diff --git a/Sources/EmojiSourceKit/EmojiDownloader.swift b/Sources/EmojiSourceKit/EmojiDownloader.swift index 6eae9ff..5c9dc58 100644 --- a/Sources/EmojiSourceKit/EmojiDownloader.swift +++ b/Sources/EmojiSourceKit/EmojiDownloader.swift @@ -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() diff --git a/Sources/EmojiSourceKit/UnicodeParser.swift b/Sources/EmojiSourceKit/UnicodeParser.swift index f885f13..39bfbbb 100644 --- a/Sources/EmojiSourceKit/UnicodeParser.swift +++ b/Sources/EmojiSourceKit/UnicodeParser.swift @@ -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]