Add beta version

This commit is contained in:
Niklas Amslgruber
2023-06-14 17:36:36 +02:00
parent 17b95eff68
commit 0d40f55737
15 changed files with 747 additions and 88 deletions

95
.gitignore vendored
View File

@@ -1,90 +1,9 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# Accio dependency management
Dependencies/
.accio/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EmojiKit"
BuildableName = "EmojiKit"
BlueprintName = "EmojiKit"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
viewDebuggingEnabled = "No">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EmojiKit"
BuildableName = "EmojiKit"
BlueprintName = "EmojiKit"
ReferencedContainer = "container:">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "download --path ."
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EmojiKit"
BuildableName = "EmojiKit"
BlueprintName = "EmojiKit"
ReferencedContainer = "container:">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EmojiKitLibrary"
BuildableName = "EmojiKitLibrary"
BlueprintName = "EmojiKitLibrary"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EmojiKitLibrary"
BuildableName = "EmojiKitLibrary"
BlueprintName = "EmojiKitLibrary"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

23
Package.resolved Normal file
View File

@@ -0,0 +1,23 @@
{
"pins" : [
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a",
"version" : "1.2.2"
}
},
{
"identity" : "swiftsoup",
"kind" : "remoteSourceControl",
"location" : "https://github.com/scinfu/SwiftSoup.git",
"state" : {
"revision" : "0e96a20ffd37a515c5c963952d4335c89bed50a6",
"version" : "2.6.0"
}
}
],
"version" : 2
}

33
Package.swift Normal file
View File

@@ -0,0 +1,33 @@
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "EmojiKit",
platforms: [.macOS(.v13), .iOS(.v16)],
products: [
.executable(
name: "EmojiKit",
targets: ["EmojiKit"]
),
// .library(
// name: "EmojiKitLibrary",
// targets: ["EmojiKitLibrary"]
// )
],
dependencies: [
.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"),
],
targets: [
.executableTarget(
name: "EmojiKit",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SwiftSoup", package: "SwiftSoup"),
// .target(name: "EmojiKitLibrary")
]),
// .target(name: "EmojiKitLibrary")
]
)

37
README.md Normal file
View File

@@ -0,0 +1,37 @@
# EmojiKit
A lightweight script that queries all available emoji releases from [Unicode.org](unicode.org) and makes them easily available in any product for the Apple ecosystem.
## Usage
The main idea behind this script is that there is no full list of supported emojis that can be easily used in Swift. This script fetches the full list of emojis from [Unicode.org](unicode.org), parses them and returns a structured `.json` file where all emojis are sorted in their official category. The `.json` files can be easily read afterwards for further usage in any product.
1. Clone this repository and `cd` into the repository
2. Run `swift run -c release EmojiKit` to build the package
3. Move the product into `/usr/local/bin` to make it available systemwide with `cp .build/release/EmojiKit /usr/bin/emojiKit`
4. Call the script from anywhere with `emojiKit download <path> -v <version>
> Alternatively you can simply run `sh build.sh` to do steps 2. and 3. in one go.
### Arguments:
* **path**: Specify the directory where the `emoji_v<version>.json` file should be stored. Only provide the directory like `/Desktop` or `.` for the current directory, not the whole file path.
* **version (--version, -v)**: New emojis are released yearly (in general). Currently only version `14` and `15` (latest) are supported. But you can easily extend the script by adding new cases to the `EmojiUnicodeVersion` enum.
## Usage with Xcode
> Swift Package Manager and CocoaPods support is planned for the future. Currently, you need to do some manual steps to use the emojis with Xcode and Swift.
1. In your Xcode project, add a file called `emoji_v<version>` to your assets, Make sure that they are included under *Build Phases - Copy Bundle Resources* (if not add them manually there). For each version (currently `v14` and `v15`) you need to add a separate file into Xcode.
2. Run the script as describe above and specify the directory of the files you added in Xcode as the `<path>` argument.
3. The script will query the emojis and saves them to your files in Xcode.
4. Afterwards you're ready to use them in Xcode by either reading the data yourself or using the `EmojiManager.swift` included in this repository.
5. Simply copy the `EmojiManager.swift` and `EmojiCategory.swift` files into your Xcode project.
6. Afterwards you can load the emojis by calling `EmojiManager.getAvailableEmojis()`. The manager automatically fetches the correct unicode version depending on the device's iOS version to only show supported ones *(you need to run the script for all supported versions first to make the automatic loading work)*.
> **Note:** Version 14 includes all emojis that are supported on devices from `iOS 15.4` to `<iOS 16.4`. Devices with an iOS Version `>iOS 16.5` require version 15.
## Error Handling
* If you see emojis on your device that are displayed as a `?` you're most likely running your App on a device below `iOS 15.4`. Anything below `iOS 15.4` is unfortunately not supported. If you still want to use it simply add a new case in the `EmojiUnicodeVersion`, e.g. `v13 = 13` to make it work (no guarantees though).
* If the `EmojiManager` doesn't return any emojis, there are two possible reasons. First, you didn't run the script for the version you're trying to load, so your `.json` files are empty or non-existent. Simply run the script for all supported version and it should work. If not make sure that the `.json` files are included in the *Build Phases - Copy Bundle Resources* for each target that wants to use the `EmojiManager`.

View File

@@ -0,0 +1,85 @@
//
// DataProcessor.swift
//
//
// Created by Niklas Amslgruber on 12.06.23.
//
import Foundation
public enum DataProcessor {
public enum EmojiUnicodeVersion: Int {
case v14 = 14
case v15 = 15
public var fileName: String {
return "emojis_v\(rawValue).json"
}
public static func getSupportedVersionForCurrentIOSVersion() -> EmojiUnicodeVersion {
if #available(iOS 16.4, *) {
return .v15
} else {
return .v14
}
}
}
public enum SkinType {
case neutral
case light
case mediumLight
case medium
case mediumDark
case dark
var unicode: String {
switch self {
case .neutral:
return ""
case .light:
return "1F3FB"
case .mediumLight:
return "1F3FC"
case .medium:
return "1F3FD"
case .mediumDark:
return "1F3FE"
case .dark:
return "1F3FF"
}
}
}
public static func getEmojis(at url: URL, for version: EmojiUnicodeVersion, skinTypes: [SkinType] = [.neutral]) -> [EmojiCategory] {
guard let content = try? Data(contentsOf: url), let result = try? JSONDecoder().decode([EmojiCategory].self, from: content) else {
return []
}
var filteredEmojis: [EmojiCategory] = []
for wrapper in result {
let supportedEmojis = wrapper.values.filter({
isMatchingSkinType(of: $0, for: skinTypes)
})
filteredEmojis.append(EmojiCategory(name: wrapper.name, values: supportedEmojis))
}
return filteredEmojis
}
private static func isMatchingSkinType(of emoji: String, for skinTypes: [SkinType]) -> Bool {
let unicodes = getUnicodes(emoji: emoji)
for skinType in skinTypes where unicodes.contains(skinType.unicode) {
return true
}
return false
}
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) }
}
}

View File

@@ -0,0 +1,32 @@
//
// EmojiCategory.swift
//
//
// Created by Niklas Amslgruber on 10.06.23.
//
import Foundation
public class EmojiCategory: Codable {
public enum Name: String, CaseIterable, Codable {
case flags = "Flags"
case activities = "Activities"
case components = "Component"
case objects = "Objects"
case travelAndPlaces = "Travel & Places"
case symbols = "Symbols"
case peopleAndBody = "People & Body"
case animalsAndNature = "Animals & Nature"
case foodAndDrink = "Food & Drink"
case smileysAndEmotions = "Smileys & Emotion"
}
public let name: Name
public let values: [String]
public init(name: Name, values: [String]) {
self.name = name
self.values = values
}
}

View File

@@ -0,0 +1,111 @@
//
// File.swift
//
//
// Created by Niklas Amslgruber on 10.06.23.
//
import Foundation
import ArgumentParser
import EmojiKitLibrary
struct EmojiDownloader: ParsableCommand, AsyncParsableCommand {
static let configuration: CommandConfiguration = CommandConfiguration(
commandName: "download",
abstract: "Downloads a list of all available emojis and their counts from unicode.rog for the respective unicode version"
)
@Argument var path: String
@Option(name: .shortAndLong) var version: DataProcessor.EmojiUnicodeVersion = .v15
func run() async throws {
print("⚙️", "Starting to download all emojis for version \(version.rawValue) from unicode.org...\n")
guard let emojiListURL = await getTemporaryURLForEmojiList(version: version), let emojiCountsURL = await getTemporaryURLForEmojiCounts(version: version) else {
print("⚠️", "Could not get content from unicode.org. Either the emoji list or the emoji count file is not available.\n")
return
}
print("🎉", "Successfully retrieved temporary URLs for version \(version.rawValue).\n")
print("⚙️", "Starting to parse content...\n")
let parser = UnicodeParser()
do {
let emojisByCategory: [EmojiCategory] = try await parser.parseEmojiList(for: emojiListURL)
let emojiCounts: [EmojiCategory.Name: Int] = parser.parseCountHTML(for: emojiCountsURL)
for category in emojisByCategory {
assert(emojiCounts[category.name] == category.values.count)
}
print("🎉", "Successfully parsed emojis and matched counts to the count file.\n")
print("⚙️", "Saving emojis to file emojis_v\(version.rawValue).json...\n")
save(data: emojisByCategory, for: version)
print("🎉", "Successfully saved emojis to file.\n")
} catch {
print("⚠️", "Could not parse emoji lists or emoji counts. Process failed with: \(error).\n")
}
}
func getTemporaryURLForEmojiList(version: DataProcessor.EmojiUnicodeVersion) async -> URL? {
return await load(urlString: "https://unicode.org/Public/emoji/\(version.rawValue).0/emoji-test.txt")
}
func getTemporaryURLForEmojiCounts(version: DataProcessor.EmojiUnicodeVersion) async -> URL? {
return await load(urlString: "https://www.unicode.org/emoji/charts-\(version.rawValue).0/emoji-counts.html")
}
private func load(urlString: String) async -> URL? {
guard let url = URL(string: urlString) else {
return nil
}
let session = URLSession(configuration: .default)
do {
let (tmpFileURL, response) = try await session.download(from: url)
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode == 200 else {
print("⚠️", "Failed with a non 200 HTTP status")
return nil
}
return tmpFileURL
} catch {
print("⚠️", error)
return nil
}
}
private func save(data: [EmojiCategory], for: DataProcessor.EmojiUnicodeVersion) {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let result = try? encoder.encode(data) else {
print("⚠️", "Couldn't encode emoji categories.")
return
}
var filePath = URL(filePath: path)
filePath.append(path: version.fileName)
let jsonString = String(data: result, encoding: .utf8)
if FileManager.default.fileExists(atPath: filePath.absoluteString) == false {
FileManager.default.createFile(atPath: filePath.absoluteString, contents: nil)
}
do {
try jsonString?.write(to: filePath, atomically: true, encoding: .utf8)
} catch {
print("⚠️", error)
}
}
}
extension DataProcessor.EmojiUnicodeVersion: ExpressibleByArgument {}

View File

@@ -0,0 +1,58 @@
//
// EmojiManager.swift
// Travely
//
// Created by Niklas Amslgruber on 13.06.23.
//
import Foundation
enum EmojiManager {
enum Version: Int {
case v14 = 14
case v15 = 15
var fileName: String {
return "emojis_v\(rawValue)"
}
static func getSupportedVersion() -> Version {
if #available(iOS 16.4, *) {
return .v15
} else {
return .v14
}
}
}
static func getAvailableEmojis(version: Version = .getSupportedVersion()) -> [EmojiCategory] {
if let url = Bundle.main.url(forResource: version.fileName, withExtension: "json"), let content = try? Data(contentsOf: url), let result = try? JSONDecoder().decode([EmojiCategory].self, from: content) {
var filteredEmojis: [EmojiCategory] = []
for category in result {
let supportedEmojis = category.values.filter({
isNeutralEmoji(for: $0)
})
filteredEmojis.append(EmojiCategory(name: category.name, values: supportedEmojis))
}
return filteredEmojis
}
return []
}
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 }
return unicodes.map { String($0, radix: 16, uppercase: true) }
}
}

View File

@@ -0,0 +1,21 @@
//
// EmojiScripts.swift
//
//
// Created by Niklas Amslgruber on 12.06.23.
//
import Foundation
import ArgumentParser
@main
struct EmojiScripts: ParsableCommand, AsyncParsableCommand {
static let configuration: CommandConfiguration = CommandConfiguration(
commandName: "emojis",
abstract: "Manage Emojis from Unicode",
subcommands: [
EmojiDownloader.self,
]
)
}

View File

@@ -0,0 +1,33 @@
//
// StringHelper.swift
//
//
// Created by Niklas Amslgruber on 10.06.23.
//
import Foundation
extension String {
func trim() -> String {
return self.trimmingCharacters(in: .whitespacesAndNewlines)
}
func asEmoji() -> String? {
guard let unicodeNumber = Int(self, radix: 16), let unicode = Unicode.Scalar(unicodeNumber) else {
return nil
}
return String(unicode)
}
}
extension String.SubSequence {
func trim() -> String {
return String(self).trim()
}
func asEmoji() -> String? {
return String(self).asEmoji()
}
}

View File

@@ -0,0 +1,147 @@
//
// UnicodeParser.swift
//
//
// Created by Niklas Amslgruber on 12.06.23.
//
import Foundation
import SwiftSoup
import EmojiKitLibrary
class UnicodeParser {
enum Tags: String {
case comment = "#"
case group = "# group:"
case unqualified
case minimallyQualified = "minimally-qualified"
}
func parseEmojiList(for fileUrl: URL) async throws -> [EmojiCategory] {
let handle = try FileHandle(forReadingFrom: fileUrl)
var currentGroup: EmojiCategory.Name = .activities
var emojisByGroup: [EmojiCategory.Name: [String]] = [:]
for try await line in handle.bytes.lines {
/// Skip comments, but keep groups
if isLine(line, ofType: .comment), isLine(line, ofType: .group) == false {
continue
}
/// Get current group
if isLine(line, ofType: .group) {
let name = line.split(separator: ":")
let categoryName = name.last?.trim() ?? ""
guard let category = EmojiCategory.Name(rawValue: categoryName) else {
continue
}
currentGroup = category
emojisByGroup[category] = []
}
/// Split line into list of entries
let lineComponents = line.split(separator: ";")
/// Get hex-string from compenents
guard let hexString = lineComponents.map({ $0.trim() }).first else {
continue
}
/// Check if category exists
guard lineComponents.count > 1 else {
continue
}
let category = lineComponents[1].trim()
/// Remove `unqualified` or `minimally-qualified` entries
guard (isLine(category, ofType: .unqualified) || isLine(category, ofType: .minimallyQualified)) == false else {
continue
}
let hexComponents = hexString.split(separator: " ")
/// Check for multi-hex emojis
if hexComponents.count > 1 {
let multiHexEmoji = hexComponents.compactMap({ $0.asEmoji() }).joined()
if multiHexEmoji.isEmpty == false {
emojisByGroup[currentGroup]?.append(multiHexEmoji)
}
} else {
if let unicode = hexString.asEmoji(), unicode.isEmpty == false {
emojisByGroup[currentGroup]?.append(unicode)
}
}
}
try handle.close()
var result: [EmojiCategory] = []
for category in EmojiCategory.Name.allCases {
result.append(EmojiCategory(name: category, values: emojisByGroup[category] ?? []))
}
return result
}
func parseCountHTML(for url: URL) -> [EmojiCategory.Name: Int] {
do {
let html = try String(contentsOf: url)
let doc: Document = try SwiftSoup.parse(html)
guard let table = try doc.select("table").first() else {
return [:]
}
let rows: Elements = try table.select("tbody tr")
let categories = rows.first
let totals = rows.last
guard let categories, let totals, let categoryEntries = try? categories.select("th"), let countEntries = try? totals.select("th") else {
return [:]
}
var categoryNames: [EmojiCategory.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 {
continue
}
categoryNames.append(category)
}
for countElement in countEntries {
if countElement == countEntries.first || countElement == countEntries.last {
continue
}
guard let text = try? countElement.text(), let number = Int(text) else {
continue
}
countNumbers.append(number)
}
var result: [EmojiCategory.Name: Int] = [:]
for (index, categoryName) in categoryNames.enumerated() {
result[categoryName] = countNumbers[index]
}
return result
} catch {
print("Error parsing HTML: \(error)")
}
return [:]
}
private func isLine(_ line: String, ofType type: Tags) -> Bool {
return line.starts(with: type.rawValue)
}
}

2
build.sh Normal file
View File

@@ -0,0 +1,2 @@
swift run -c release EmojiKit
sudo cp .build/release/EmojiKit /usr/local/bin/emojiKit