├── .swift-version
├── Example
└── EmojicaExample
│ ├── Files
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── logo.imageset
│ │ │ ├── logo.png
│ │ │ ├── logo@2x.png
│ │ │ ├── logo@3x.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── blank.png
│ │ │ ├── Icon-20.png
│ │ │ ├── Icon-76.png
│ │ │ ├── Icon-20@2x.png
│ │ │ ├── Icon-20@3x.png
│ │ │ ├── Icon-60@2x.png
│ │ │ ├── Icon-60@3x.png
│ │ │ ├── Icon-76@2x.png
│ │ │ ├── Icon-Small.png
│ │ │ ├── Icon-20@2x-1.png
│ │ │ ├── Icon-83,5@2x.png
│ │ │ ├── Icon-Small-40.png
│ │ │ ├── Icon-Small@2x.png
│ │ │ ├── Icon-Small@3x.png
│ │ │ ├── Icon-Small-40@2x.png
│ │ │ ├── Icon-Small-40@3x.png
│ │ │ ├── Icon-Small@2x-1.png
│ │ │ ├── Icon-Small-40@2x-1.png
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── AppDelegate.swift
│ └── ViewController.swift
│ └── EmojicaExample.xcodeproj
│ ├── project.xcworkspace
│ └── contents.xcworkspacedata
│ └── project.pbxproj
├── Emojica.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── Emojica.xcworkspace
└── contents.xcworkspacedata
├── Emojica.podspec
├── EmojicaTests
├── Info.plist
└── EmojicaTests.swift
├── Source
├── Info.plist
├── Emojica.h
├── Utility.swift
├── Extensions.swift
├── Emojica.swift
└── Unicode.swift
├── rename.sh
├── .gitignore
├── README.md
└── LICENSE
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.0
2 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/logo.imageset/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/logo.imageset/logo.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/logo.imageset/logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/logo.imageset/logo@2x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/logo.imageset/logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/logo.imageset/logo@3x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/blank.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-20@2x-1.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-83,5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-83,5@2x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/emojica/master/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png
--------------------------------------------------------------------------------
/Emojica.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/EmojicaExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Emojica.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "logo@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "logo@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Emojica.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "Emojica"
4 | s.version = "0.9.4"
5 | s.summary = "A Swift framework for custom emoji conversion."
6 |
7 | s.homepage = "https://github.com/xoudini/emojica"
8 | s.screenshots = "https://raw.githubusercontent.com/xoudini/emojica/images/demo.gif"
9 |
10 | s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" }
11 | s.author = { "Dan Lindholm" => "main@xoudini.com" }
12 | s.social_media_url = "http://twitter.com/xoudini"
13 | s.platform = :ios, "10.1"
14 |
15 | s.source = { :git => "https://github.com/xoudini/emojica.git", :tag => s.version.to_s }
16 | s.source_files = "Source/*.{h,swift}"
17 |
18 | end
19 |
--------------------------------------------------------------------------------
/EmojicaTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Source/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Source/Emojica.h:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2017 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // Emojica.h
21 | //
22 |
23 | #import
24 |
25 | //! Project version number for Emojica.
26 | FOUNDATION_EXPORT double EmojicaVersionNumber;
27 |
28 | //! Project version string for Emojica.
29 | FOUNDATION_EXPORT const unsigned char EmojicaVersionString[];
30 |
31 | // In this header, you should import all the public headers of your framework using statements like #import
32 |
33 |
34 |
--------------------------------------------------------------------------------
/rename.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # ------------------------------------------------------------------------
5 | #
6 | # Copyright 2016 Dan Lindholm
7 | #
8 | # Licensed under the Apache License, Version 2.0 (the "License");
9 | # you may not use this file except in compliance with the License.
10 | # You may obtain a copy of the License at
11 | #
12 | # http:#www.apache.org/licenses/LICENSE-2.0
13 | #
14 | # Unless required by applicable law or agreed to in writing, software
15 | # distributed under the License is distributed on an "AS IS" BASIS,
16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | # See the License for the specific language governing permissions and
18 | # limitations under the License.
19 | #
20 | # ------------------------------------------------------------------------
21 | #
22 | # rename.sh
23 | #
24 | # Usage:
25 | # 1. Copy into folder containing images.
26 | # 2. Navigate into the directory in Terminal.
27 | # 3. Run script:
28 | # sh rename.sh
29 | #
30 |
31 | DIR="$( cd "$(dirname "$0")"; pwd )";
32 | COUNT=0;
33 | FOUND=0;
34 |
35 | echo "Renaming images in $DIR";
36 |
37 | for f in *.png; do
38 | COUNT=$(( COUNT + 1 ));
39 |
40 | name=$(echo "$f" | sed 's/-200d//g' | sed 's/-fe0f//g' | sed 's/_200d//g' | sed 's/_fe0f//g' | sed 's/emoji_u//g');
41 |
42 | if [ "$name" != "$f" ]; then
43 | FOUND=$(( FOUND + 1 ));
44 | echo "\tRenaming $f";
45 | mv "$f" "$name";
46 | fi;
47 | done;
48 |
49 | echo "-----------------------------";
50 | echo "Renamed $FOUND of $COUNT images.";
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/Preview.html
64 | fastlane/screenshots
65 | fastlane/test_output
66 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Emojica
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 | UIInterfaceOrientationPortraitUpsideDown
39 |
40 | UISupportedInterfaceOrientations~ipad
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationPortraitUpsideDown
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/EmojicaTests/EmojicaTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2017 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // EmojicaTests.swift
21 | //
22 |
23 | import XCTest
24 |
25 | class EmojicaTests: XCTestCase {
26 |
27 | override func setUp() {
28 | super.setUp()
29 | // Put setup code here. This method is called before the invocation of each test method in the class.
30 | }
31 |
32 | override func tearDown() {
33 | // Put teardown code here. This method is called after the invocation of each test method in the class.
34 | super.tearDown()
35 | }
36 |
37 | func testExample() {
38 | // This is an example of a functional test case.
39 | // Use XCTAssert and related functions to verify your tests produce the correct results.
40 | }
41 |
42 | func testPerformanceExample() {
43 | // This is an example of a performance test case.
44 | self.measure {
45 | // Put the code you want to measure the time of here.
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Source/Utility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2017 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // Utility.swift
21 | //
22 |
23 | import Foundation
24 |
25 | /// Simple, generic stack implementation.
26 | internal class Stack {
27 | private(set) var array: [T]
28 |
29 | init(_ array: [T] = []) {
30 | self.array = array
31 | }
32 |
33 | var isEmpty: Bool {
34 | return self.array.isEmpty
35 | }
36 |
37 | var size: Int {
38 | return self.array.count
39 | }
40 |
41 | func push(_ object: T) {
42 | self.array.append(object)
43 | }
44 |
45 | @discardableResult
46 | func pop() -> T? {
47 | guard !self.isEmpty else { return nil }
48 | return self.array.removeLast()
49 | }
50 |
51 | var head: T? {
52 | return self.array.first
53 | }
54 |
55 | var top: T? {
56 | return self.array.last
57 | }
58 |
59 | func clear() {
60 | self.array.removeAll()
61 | }
62 | }
63 |
64 | /// Object used in the conversion procedure as a container for a single grapheme cluster.
65 | internal class Replacement {
66 | let character: Character
67 | let unicodeScalars: [UnicodeScalar]
68 | let range: NSRange
69 |
70 | init(character: Character, unicodeScalars: [UnicodeScalar], range: NSRange) {
71 | self.character = character
72 | self.unicodeScalars = unicodeScalars
73 | self.range = range
74 | }
75 | }
76 |
77 | /// Subclass of `NSTextAttachment` for storing the original unicode scalars in the attachment.
78 | internal class EmojicaAttachment: NSTextAttachment {
79 | private var unicodeScalars: [UnicodeScalar] = []
80 |
81 | /// A string representation of the unicode scalars.
82 | var representation: String {
83 | return self.unicodeScalars.string
84 | }
85 |
86 | func insert(unicodeScalar: UnicodeScalar) {
87 | self.unicodeScalars.append(unicodeScalar)
88 | }
89 |
90 | func insert(unicodeScalars: [UnicodeScalar]) {
91 | self.unicodeScalars.append(contentsOf: unicodeScalars)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-Small@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-Small@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-Small-40@2x-1.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-Small-40@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-60@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-60@3x.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "Icon-20.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-20@2x-1.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-Small.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-Small@2x-1.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-Small-40.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-Small-40@2x.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-76.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-76@2x.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "size" : "83.5x83.5",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-83,5@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "1024x1024",
107 | "idiom" : "ios-marketing",
108 | "filename" : "blank.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | }
116 | }
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2017 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // AppDelegate.swift
21 | //
22 |
23 | import UIKit
24 |
25 | @UIApplicationMain
26 | class AppDelegate: UIResponder, UIApplicationDelegate {
27 |
28 | var window: UIWindow?
29 |
30 | static var noImages = false
31 |
32 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
33 | // Override point for customization after application launch.
34 |
35 | let c = 0x1f600 + arc4random_uniform(80)
36 | if UIImage(named: String(c, radix: 16)) == nil { AppDelegate.noImages = true }
37 |
38 | return true
39 | }
40 |
41 | func applicationWillResignActive(_ application: UIApplication) {
42 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
43 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
44 | }
45 |
46 | func applicationDidEnterBackground(_ application: UIApplication) {
47 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
48 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
49 | }
50 |
51 | func applicationWillEnterForeground(_ application: UIApplication) {
52 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
53 | }
54 |
55 | func applicationDidBecomeActive(_ application: UIApplication) {
56 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
57 | }
58 |
59 | func applicationWillTerminate(_ application: UIApplication) {
60 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
61 | }
62 |
63 |
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/Source/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2016 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // Extensions.swift
21 | //
22 |
23 | import Foundation
24 |
25 | extension UIImage {
26 | public func extractEmojiAt(coords: CGRect) -> UIImage {
27 | return UIImage(cgImage: self.cgImage!.cropping(to: coords)!)
28 | }
29 | }
30 |
31 | extension UITextView {
32 | public var emojicaText: NSAttributedString {
33 | get { return self.attributedText }
34 | set { self.attributedText = newValue }
35 | }
36 | }
37 |
38 | extension UILabel {
39 | public var emojicaText: NSAttributedString? {
40 | get { return self.attributedText }
41 | set { self.attributedText = newValue }
42 | }
43 | }
44 |
45 | extension UITextInput {
46 |
47 | /// Returns the cursor offset from the end of the document.
48 | internal func getCursor() -> Int? {
49 | guard let range = self.selectedTextRange else { return nil }
50 | return self.offset(from: self.endOfDocument, to: range.end)
51 | }
52 |
53 | /// Sets the cursor to the end position offset by the given argument.
54 | /// - parameter offset: The value to offset the cursor with.
55 | internal func setCursor(to offset: Int) {
56 | guard let position = self.position(from: self.endOfDocument, offset: offset) else { return }
57 | self.selectedTextRange = self.textRange(from: position, to: position)
58 | }
59 |
60 | }
61 |
62 | extension String {
63 | internal init(unicodeScalars: S) where S.Iterator.Element == UnicodeScalar {
64 | var string = String()
65 | string.unicodeScalars.append(contentsOf: unicodeScalars)
66 | self = string
67 | }
68 | }
69 |
70 | extension Sequence where Iterator.Element == UnicodeScalar {
71 | var string: String {
72 | return String(String.UnicodeScalarView(self))
73 | }
74 | }
75 |
76 | extension NSTextAttachment {
77 | /// Resizes the attachment to work well with the font.
78 | internal func resize(to size: CGFloat, with font: UIFont?) {
79 | let font = font ?? UIFont.systemFont(ofSize: size)
80 | let height = font.ascender - font.descender
81 | let side = (size + height) / 2
82 | let margin = (height - side) / 2
83 | self.bounds = CGRect(x: 0, y: font.descender + margin, width: side, height: side).integral
84 | }
85 | }
86 |
87 | extension UnicodeScalar {
88 |
89 | var isObjectReplacementCharacter: Bool { return 0xfffc == self.value }
90 | var isReplacementCharacter: Bool { return 0xfffd == self.value }
91 | var isZeroWidthJoiner: Bool { return 0x200d == self.value }
92 | var isVariationSelector: Bool { return 0xfe0e...0xfe0f ~= self.value }
93 | var isVariationSelector15: Bool { return 0xfe0e == self.value }
94 | var isVariationSelector16: Bool { return 0xfe0f == self.value }
95 | var isRegionalIndicatorSymbol: Bool { return 0x1f1e6...0x1f1ff ~= self.value }
96 | var isModifierSymbol: Bool { return 0x1f3fb...0x1f3ff ~= self.value }
97 | var isKeycapSymbol: Bool { return 0x20e3 == self.value }
98 | var isKeycapBase: Bool { return Unicode.keycapBaseCharacters.contains(self.value) }
99 | }
100 |
101 | fileprivate typealias Block = Unicode.Block
102 |
103 | extension UnicodeScalar {
104 | internal var isEmoji: Bool {
105 | guard
106 | !self.isObjectReplacementCharacter,
107 | !self.isReplacementCharacter
108 | else { return false }
109 |
110 | let codePoint = self.value
111 |
112 | switch codePoint {
113 | case Block.miscellaneousSymbols.range:
114 | let block = Block.miscellaneousSymbols
115 | return !block.nonEmoji.contains(codePoint)
116 |
117 | case Block.dingbats.range:
118 | let block = Block.dingbats
119 | return !block.nonEmoji.contains(codePoint)
120 |
121 | case Block.miscellaneousSymbolsAndPictographs.range:
122 | let block = Block.miscellaneousSymbolsAndPictographs
123 | return !block.nonEmoji.contains(codePoint)
124 |
125 | case Block.emoticons.range:
126 | return true
127 |
128 | case Block.transportAndMapSymbols.range:
129 | let block = Block.transportAndMapSymbols
130 | return !block.nonEmoji.contains(codePoint) && !block.unassigned.contains(codePoint)
131 |
132 | case Block.supplementalSymbolsAndPictographs.range:
133 | let block = Block.supplementalSymbolsAndPictographs
134 | return !block.nonEmoji.contains(codePoint) && !block.unassigned.contains(codePoint)
135 |
136 | case let value where Unicode.additionalCharacters.contains(value):
137 | return true
138 |
139 | default:
140 | return false
141 | }
142 | }
143 | }
144 |
145 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2017 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // ViewController.swift
21 | //
22 |
23 | import UIKit
24 | import Emojica
25 |
26 | class ViewController: UIViewController, UITextViewDelegate {
27 |
28 | // MARK: - Initialization
29 | let emojica = Emojica()
30 |
31 | // MARK: - Modifiable values
32 | lazy var font = UIFont.systemFont(ofSize: 28, weight: UIFont.Weight.thin)
33 | lazy var images: Emojica.ImageSet = .default
34 |
35 | // MARK: - Usage of convert
36 | func convert() {
37 | textView.attributedText = emojica.convert(string: textView.emojicaText)
38 | }
39 |
40 | // MARK: - Usage of revert
41 | func revert() {
42 | textView.text = emojica.revert(attributedString: textView.emojicaText)
43 | }
44 |
45 | // MARK: - Usage of textViewDidChange(_:)
46 | func textViewDidChange(_ textView: UITextView) {
47 | guard state.isOn else { return }
48 | emojica.textViewDidChange(textView)
49 | }
50 |
51 | // MARK: - Ignore
52 | @IBOutlet weak var textView: UITextView!
53 | @IBOutlet weak var bottom: NSLayoutConstraint!
54 |
55 | @IBOutlet weak var imageSet: UIBarButtonItem!
56 | @IBAction func imageSetPressed(_ sender: UIBarButtonItem) { sheet() }
57 |
58 | @IBOutlet weak var state: UISwitch!
59 | @IBAction func stateChanged(_ sender: UISwitch) {
60 | if state.isOn {
61 | if AppDelegate.noImages {
62 | alert()
63 | state.setOn(false, animated: true)
64 | return
65 | }
66 | }
67 | state.isOn ? convert() : revert()
68 | }
69 |
70 |
71 | }
72 |
73 | // MARK: - Ignore
74 | extension ViewController {
75 |
76 | override func viewDidLoad() {
77 | super.viewDidLoad()
78 | self.setup()
79 | }
80 |
81 | func setup() {
82 | navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
83 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
84 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillChangeFrame(_:)), name: .UIKeyboardWillChangeFrame, object: nil)
85 | self.set(imageSet: images)
86 | textView.delegate = self
87 | textView.text = "Write here \u{1f609}"
88 | textView.font = font
89 | emojica.font = font
90 | emojica.revertible = true
91 | }
92 |
93 | func set(imageSet: Emojica.ImageSet) {
94 | emojica.imageSet = imageSet
95 | self.imageSet.title = emojica.imageSet.rawValue
96 | }
97 |
98 | @objc func keyboardWillHide(_ notification: NSNotification) {
99 | if let info = notification.userInfo {
100 | let animationTime = info[UIKeyboardAnimationDurationUserInfoKey] as! TimeInterval
101 | bottom.constant = 0
102 | UIView.animate(withDuration: animationTime, animations: { self.view.layoutIfNeeded() })
103 | }
104 | }
105 |
106 | @objc func keyboardWillChangeFrame(_ notification: NSNotification) {
107 | if let info = notification.userInfo {
108 | let animationTime = info[UIKeyboardAnimationDurationUserInfoKey] as! TimeInterval
109 | let endFrame = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
110 | bottom.constant = endFrame.height
111 | UIView.animate(withDuration: animationTime, animations: { self.view.layoutIfNeeded() })
112 | }
113 | }
114 |
115 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
116 | textView.resignFirstResponder()
117 | }
118 | }
119 |
120 | extension ViewController {
121 | override func viewDidAppear(_ animated: Bool) {
122 | super.viewDidAppear(animated)
123 | if AppDelegate.noImages {
124 | alert()
125 | }
126 | }
127 |
128 | func alert() {
129 | let alert = UIAlertController(title: "No Images", message: "You need to import an image set for this to work.", preferredStyle: .alert)
130 | alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler: nil))
131 | present(alert, animated: true, completion: nil)
132 | }
133 |
134 | func sheet() {
135 | let sheet = UIAlertController.init(title: nil, message: "Select Image Set", preferredStyle: .actionSheet)
136 | let imageSets = [Emojica.ImageSet.default, Emojica.ImageSet.twemoji, Emojica.ImageSet.emojione, Emojica.ImageSet.noto]
137 | for imageSet in imageSets {
138 | let action = UIAlertAction(title: imageSet.rawValue, style: .default) { _ in
139 | self.set(imageSet: imageSet)
140 | if self.state.isOn {
141 | let reverted = self.emojica.revert(attributedString: self.textView.emojicaText)
142 | self.textView.emojicaText = self.emojica.convert(string: reverted)
143 | }
144 | }
145 | sheet.addAction(action)
146 | }
147 | sheet.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler: nil))
148 | present(sheet, animated: true, completion: nil)
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | =====
3 |
4 |
5 | Emojica – a Swift framework for using custom emoji in strings.
6 |
7 |
8 | 
9 |
10 | ## What does it do?
11 |
12 | Emojica allows you to replace the standard emoji in your iOS apps with [custom emoji](#compatible-image-sets).
13 | Works on `UILabel` and `UITextView`.
14 |
15 | Just follow the instructions below, import your custom image set, and you're ready to go.
16 |
17 |
18 |
19 | ## Features
20 |
21 | - [x] Compatible with __all__ iOS 11 emoji
22 | - [x] Works with any image set1
23 | - [x] Safe to use even with incomplete image sets2
24 | - [x] Convert input directly on [`textViewDidChange(_:)`](#directly-converting-text-input)
25 | - [x] Revert converted strings to their original representation
26 |
27 |
28 | 1. The naming should follow a similar pattern as the compatible image sets.
29 |
30 |
31 |
32 | 2. The original emoji are used as fallback.
33 |
34 |
35 |
36 |
37 | ## Requirements
38 |
39 | + Xcode 8.3
40 | + iOS 10.0+
41 | * _Lower versions haven't been tested properly, although the framework may run without issues on a lower version._
42 | + Swift 4
43 | * _Using the framework in an Objective-C project may require some modifications to the source.
44 | Support for Objective-C will possibly be added in the near future._
45 |
46 |
47 |
48 | ## Installation
49 |
50 | ### CocoaPods
51 |
52 | 1. Add the pod to your `Podfile`:
53 |
54 | ```ruby
55 | target '...' do
56 | pod 'Emojica'
57 | end
58 | ```
59 | 2. Navigate into your project directory and install/update:
60 |
61 | ```sh
62 | $ cd /Path/To/Your/Project/ && pod install
63 | ```
64 |
65 | ### Manual installation
66 |
67 | 1. Clone the repository, and drag `Emojica.xcodeproj` into your project hierarchy in Xcode.
68 | 2. Select your project, then select your application's target under __Targets__.
69 | 3. Under the __General__ tab, click the __+__ under __Embedded Binaries__.
70 | 4. Select `Emojica.frameworkiOS` and finish by pressing __Add__.
71 |
72 | > If Xcode gives you a `No such module 'Emojica'` compiler error at your `import` statement, just
73 | build your application (or the framework) once. Also, each time you Clean (⇧⌘K) the project Xcode
74 | will give you the same error, and the solution is the same.
75 |
76 |
77 |
78 | ## Usage
79 |
80 | ```swift
81 | import Emojica
82 | ```
83 |
84 | ### Instantiation
85 |
86 | ```swift
87 | let emojica = Emojica()
88 |
89 | // Creates an instance with a font.
90 | let emojica = Emojica(font: UIFont.systemFont(ofSize: 17.0))
91 | ```
92 |
93 | ### Configure instance
94 |
95 | * __Set font__:
96 |
97 | ```swift
98 | emojica.font = UIFont.systemFont(ofSize: 17.0)
99 | ```
100 |
101 | If no font is set, the system font is used.
102 |
103 |
104 | * __Set size__:
105 |
106 | ```swift
107 | emojica.pointSize = 17.0
108 | ```
109 |
110 | If you're satisfied with the default font, you can just set the size.
111 | The value for `pointSize` is 17.0 by default.
112 |
113 |
114 | * __Set minimum code point width__:
115 |
116 | > __NOTE__: Use this only when using a custom image set that [isn't handled by Emojica](#compatible-image-sets).
117 |
118 | ```swift
119 | emojica.minimumCodePointWidth = 4
120 | ```
121 |
122 | A value between 0 and 8 that sets the minimum width for code point strings in order to correctly
123 | find the images for the custom emoji. The character `0` is used for padding.
124 |
125 | To find a suitable value, find the image for e.g. © (`U+00A9 COPYRIGHT SIGN`), and use the length
126 | of that image's name – `a9.png` has a width of 2, `00a9.png` has a width of 4, etc.
127 |
128 |
129 | * __Set separator__:
130 |
131 | > __NOTE__: Use this only when using a custom image set that [isn't handled by Emojica](#compatible-image-sets).
132 |
133 | ```swift
134 | emojica.separator = "~"
135 | ```
136 |
137 | The separator used in the image names of combined code points.
138 |
139 |
140 | * __Set image set used in the project__:
141 |
142 | ```swift
143 | emojica.imageSet = .default
144 | ```
145 |
146 | Automatically configures settings specific to the image set.
147 |
148 |
149 | * __Disable modifier symbols__:
150 |
151 | ```swift
152 | emojica.useModifiers = false
153 | ```
154 |
155 | Strips out all [modifier symbols](http://unicode.org/reports/tr51/#Emoji_Modifiers_Table) from
156 | complete modifier sequences.
157 |
158 |
159 | * __Enable emoji to be reverted__:
160 |
161 | > __NOTE__: Keep the instance non-revertible if the original strings aren't needed after conversion.
162 |
163 | ```swift
164 | emojica.revertible = true
165 | ```
166 |
167 | Enables strings with custom emoji to be reverted to original state.
168 |
169 |
170 | ### Convert string
171 |
172 | ```swift
173 | let sample: String = "Sample text 😎 "
174 |
175 | let converted: NSAttributedString = emojica.convert(string: sample)
176 | ```
177 |
178 | ### Revert string
179 |
180 | > __NOTE__: The instance must have `revertible` set to `true`.
181 |
182 | ```swift
183 | let reverted: String = emojica.revert(attributedString: converted)
184 | ```
185 |
186 | ### Using converted strings
187 |
188 | ```swift
189 | let textView = UITextView()
190 |
191 | ...
192 |
193 | let flag: String = "🇫🇮 "
194 |
195 | textView.attributedText = emojica.convert(string: flag)
196 | ```
197 |
198 | ### Directly converting text input
199 |
200 | You can directly convert text input by implementing the `UITextViewDelegate` method `textViewDidChange(_:)`
201 | and passing the changed `UITextView` to the Emojica method by the same name:
202 |
203 | ```swift
204 | func textViewDidChange(_ textView: UITextView) {
205 | emojica.textViewDidChange(textView)
206 | }
207 | ```
208 |
209 |
210 |
211 | ## Compatible Image Sets
212 |
213 | > The below image sets are tested, but other image sets may work just as well. If you have an image set that
214 | should be added to Emojica, please create an [__Issue__](https://github.com/xoudini/emojica/issues).
215 |
216 | | Set | Version | Notes |
217 | | ------------- | --------- | ----------------------------------- |
218 | | [Twemoji] | 2.2 | _[Prepare](#preparations)_ |
219 | | [EmojiOne] | 2.2.7 | _Missing code points_1 |
220 | | [Noto Emoji] | 1.05 | _[Prepare](#preparations)_ |
221 |
222 | [Twemoji]: https://github.com/twitter/twemoji
223 | [EmojiOne]: https://github.com/Ranks/emojione
224 | [Noto Emoji]: https://github.com/googlei18n/noto-emoji
225 |
226 |
227 | 1. U+2640, U+2642 and U+2695 and sequences containing these characters are unsupported.
228 |
229 |
230 |
231 |
232 | ## Example Project
233 |
234 | The example `EmojicaExample.xcodeproj` is set up but __does not contain images__. To test the project,
235 | add your emoji images to the `Images` group and __Run__.
236 |
237 |
238 |
239 | ## Preparations
240 |
241 | > __WARNING__: Running the script __will__ overwrite the image names, so __do not run the script over a unique image set!__
242 |
243 |
244 | Some image sets may have to be slightly modified before usage. Check the table in
245 | [Compatible Image Sets](#compatible-image-sets) if you're using a set marked _Prepare_, and if you are,
246 | follow these instructions:
247 |
248 | #### 1. Copy/move the contained file `rename.sh` into the folder containing your image set.
249 | #### 2. Open your preferred terminal.
250 | #### 3. Navigate into the directory:
251 | ```sh
252 | $ cd /Path/To/Your/ImageSet/
253 | ```
254 | #### 4. Run the script:
255 | ```sh
256 | $ sh rename.sh
257 | ```
258 |
259 |
260 |
261 | ## Contact
262 |
263 | You can find me on Twitter at [@xoudini](https://twitter.com/xoudini),
264 | or send me electronic mail at [main@xoudini.com](mailto:main@xoudini.com).
265 |
266 | Feedback and questions are welcome, create an [__Issue__](https://github.com/xoudini/emojica/issues)
267 | for bugs, problems and feature requests.
268 |
269 | If you end up using Emojica in one of your projects, hit me up. I'd love to check it out!
270 |
271 |
272 |
273 | ## License
274 |
275 | Emojica is released under the **Apache License 2.0**.
276 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/Files/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Source/Emojica.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2016 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // Emojica.swift
21 | //
22 |
23 | import Foundation
24 |
25 | /// A class to convert the standard emoji representation into something more
26 | /// customised. Images not provided.
27 | public final class Emojica {
28 |
29 | public init() {}
30 |
31 | /// Initializes an instance that uses the given font.
32 | /// - parameter font: The font to be used in converted attributed strings.
33 | public init(font: UIFont) {
34 | self.font = font
35 | }
36 |
37 | // Private declarations.
38 | private var _font: UIFont?
39 | private var _pointSize: CGFloat = 17.0
40 | private var _minimumCodePointWidth: UInt = 0
41 | private var _separator: String = "-"
42 | private var _imageSet: ImageSet = .default
43 | private var _spriteSheetCache: [String: UIImage] = [:]
44 |
45 | /// The font to be used for standard text.
46 | /// - note: If no font is set, the system font is used.
47 | public var font: UIFont? {
48 | get { return _font }
49 | set {
50 | _font = newValue
51 | _pointSize = newValue?.pointSize ?? pointSize
52 | }
53 | }
54 |
55 | /// The point size to be used for text and emoji.
56 | /// - note: 17.0 by default.
57 | public var pointSize: CGFloat {
58 | get { return _pointSize }
59 | set {
60 | _pointSize = newValue
61 | _font = font?.withSize(newValue)
62 | }
63 | }
64 |
65 | /// The minimum width used in the names of the imported images. The character `0` is used for padding.
66 | /// - note: This value must between 0 and 8.
67 | public var minimumCodePointWidth: UInt {
68 | get { return _minimumCodePointWidth }
69 | set {
70 | _minimumCodePointWidth = newValue < 8 ? newValue : 8
71 | }
72 | }
73 |
74 | /// The separator used in the names of the imported images.
75 | public var separator: String {
76 | get { return _separator }
77 | set {
78 | _separator = newValue
79 | }
80 | }
81 |
82 | /// The image set used in the project.
83 | public var imageSet: ImageSet {
84 | get { return _imageSet }
85 | set {
86 | switch newValue {
87 | case .default:
88 | self.minimumCodePointWidth = 0
89 | self.separator = "-"
90 | case .twemoji:
91 | self.minimumCodePointWidth = 2
92 | self.separator = "-"
93 | case .emojione:
94 | self.minimumCodePointWidth = 4
95 | self.separator = "-"
96 | case .noto:
97 | self.minimumCodePointWidth = 4
98 | self.separator = "_"
99 | }
100 | self._imageSet = newValue
101 | }
102 | }
103 |
104 | /// Setting this to `false` _used to_ strip out all modifier symbols (🏻, 🏼, 🏽, 🏾 and 🏿).
105 | /// - note: This will be removed in a future version.
106 | @available(*, deprecated: 1.0)
107 | public var useModifiers: Bool = true
108 |
109 | /// Keep the instance non-revertible if the original strings aren't needed after conversion.
110 | public var revertible: Bool = false
111 | }
112 |
113 | extension Emojica {
114 | public enum ImageSet: String {
115 | /// Default parameters.
116 | case `default` = "Custom"
117 | /// Twemoji 2.2 compatibility.
118 | case twemoji = "Twemoji"
119 | /// Emoji One compatibility.
120 | case emojione = "Emoji One"
121 | /// Noto Emoji compatibility.
122 | case noto = "Noto Emoji"
123 | }
124 | }
125 |
126 | extension Emojica {
127 |
128 | /// Replaces the standard emoji with custom emoji from the provided image set.
129 | /// - parameter string: The string to convert.
130 | /// - returns: An `NSAttributedString` with all the emoji replaced with custom images.
131 | public func convert(string: String) -> NSAttributedString {
132 | let attributedString = NSAttributedString(string: string)
133 | return self.convert(string: attributedString)
134 | }
135 |
136 | /// Replaces the standard emoji with custom emoji from the provided spritesheet and coordinate
137 | /// - parameter string: The string to convert.
138 | /// - parameter spriteSheet: The name of the image containing your emoji.
139 | /// - parameter coords: The coordinate of your emoji in the spritesheet.
140 | /// - returns: An `NSAttributedString` with all the emoji replaced with custom images.
141 | public func convert(string: String, spriteSheet: String, coords: CGRect) -> NSAttributedString {
142 | let attributedString = NSAttributedString(string: string)
143 | return self.convert(string: attributedString, spriteSheet: spriteSheet, coords: coords)
144 | }
145 |
146 | /// Replaces the standard emoji with custom emoji from the provided image set.
147 | /// - parameter string: The string to convert.
148 | /// - parameter spriteSheet: The name of the image containing your emoji. If any.
149 | /// - parameter coords: The coordinate of your emoji in the spritesheet. If any.
150 | /// - returns: An `NSAttributedString` with all the emoji replaced with custom images.
151 | public func convert(string attributedString: NSAttributedString, spriteSheet: String? = nil, coords: CGRect? = nil) -> NSAttributedString {
152 | let result = NSMutableAttributedString(attributedString: attributedString)
153 |
154 | if let sheet = spriteSheet, let frame = coords {
155 | return replaceString(from: result.string, spriteSheet: sheet, coords: frame) ?? result
156 | } else {
157 | let stack = generateReplacementStack(from: attributedString)
158 |
159 | // Replacing characters in reverse order, to avoid out-of-range issues.
160 | while let replacement = stack.pop() {
161 | if let replacementString = replacementString(from: replacement) {
162 | result.replaceCharacters(in: replacement.range, with: replacementString)
163 | } else {
164 | result.replaceCharacters(in: replacement.range, with: fallbackString(from: replacement))
165 | }
166 | }
167 |
168 | // Fix font and size of final string.
169 | let resultRange = result.mutableString.range(of: result.string)
170 | let font = self.font ?? UIFont.systemFont(ofSize: self.pointSize)
171 | result.addAttribute(NSAttributedStringKey.font, value: font, range: resultRange)
172 |
173 | return result
174 | }
175 | }
176 | }
177 |
178 | extension Emojica {
179 |
180 | /// Reverts an attributedString converted by Emojica to a string with standard emoji.
181 | /// - note: The instance must have `revertible` set to `true`.
182 | /// - parameter attributedString: The attributed string to convert back to normal.
183 | public func revert(attributedString: NSAttributedString) -> String {
184 | let storage = NSTextStorage(attributedString: attributedString)
185 | let range = NSRange(location: 0, length: storage.length)
186 |
187 | // Return the string as is if the instance isn't revertible.
188 | guard self.revertible else { return storage.string }
189 |
190 | storage.enumerateAttribute(NSAttributedStringKey.attachment, in: range, options: []) { (value, range, _) -> Void in
191 | if let attachment = value as? EmojicaAttachment {
192 | storage.replaceCharacters(in: range, with: attachment.representation)
193 | }
194 | }
195 |
196 | return storage.string
197 | }
198 | }
199 |
200 | extension Emojica {
201 |
202 | /// Replaces the emoji properly when called from `textViewDidChange(:)`.
203 | /// - parameter textView: The text view containing the changes.
204 | public func textViewDidChange(_ textView: UITextView) {
205 | let offset = textView.getCursor() ?? 0
206 | textView.emojicaText = self.convert(string: textView.emojicaText)
207 | textView.setCursor(to: offset)
208 | }
209 | }
210 |
211 | extension Emojica {
212 |
213 | /// Iterates through every unicode scalar in the string and generates a stack of
214 | /// replacement objects.
215 | /// - parameter attributedString: The string to parse.
216 | fileprivate func generateReplacementStack(from attributedString: NSAttributedString) -> Stack {
217 | let replacementStack: Stack = Stack()
218 |
219 | var range: NSRange = NSRange(location: 0, length: 0)
220 |
221 | for character in attributedString.string {
222 | let unicodeScalarStack: Stack = Stack()
223 |
224 | for unicodeScalar in character.unicodeScalars {
225 | range.length += UTF16.width(unicodeScalar)
226 |
227 | if unicodeScalarStack.isEmpty {
228 |
229 | // Quit iterating if stack is empty and current scalar isn't emoji.
230 | guard unicodeScalar.isEmoji else { break }
231 | unicodeScalarStack.push(unicodeScalar)
232 |
233 | } else {
234 |
235 | // Non-emoji representation requested.
236 | if unicodeScalar.isVariationSelector15 {
237 | unicodeScalarStack.pop()
238 | }
239 |
240 | // Emoji representation requested.
241 | else if unicodeScalar.isVariationSelector16 {
242 | unicodeScalarStack.push(unicodeScalar)
243 | }
244 |
245 | // Combining enclosing keycap.
246 | else if unicodeScalar.isKeycapSymbol {
247 | unicodeScalarStack.push(unicodeScalar)
248 | }
249 |
250 | // Joiner for sequences.
251 | else if unicodeScalar.isZeroWidthJoiner {
252 | unicodeScalarStack.push(unicodeScalar)
253 | }
254 |
255 | // Actual emoji.
256 | else if unicodeScalar.isEmoji {
257 | unicodeScalarStack.push(unicodeScalar)
258 | }
259 | }
260 | }
261 |
262 | /* Cleanup time */
263 |
264 | // If head is a keycap base, make sure that the last unicode scalar is an enclosing keycap.
265 | if let head = unicodeScalarStack.head, head.isKeycapBase {
266 | if !unicodeScalarStack.top!.isKeycapSymbol { unicodeScalarStack.clear() }
267 | }
268 |
269 | // Push replacement object to stack if the unicode scalar stack isn't empty.
270 | if !unicodeScalarStack.isEmpty {
271 | let replacement = Replacement(character: character, unicodeScalars: unicodeScalarStack.array, range: range)
272 | replacementStack.push(replacement)
273 | }
274 |
275 | // Update range location for next character.
276 | range.location += range.length
277 | range.length = 0
278 | }
279 |
280 | return replacementStack
281 | }
282 |
283 | /// Format string used for converting a code point to a hexadecimal string.
284 | private var formatString: String {
285 | return "%0\(self.minimumCodePointWidth)x"
286 | }
287 |
288 | /// Generates a replacement string for a grapheme cluster.
289 | /// - parameter replacement: The `Replacement` instance to use in the conversion.
290 | /// - returns: An `NSAttributedString` with the replacement as an attachment, or `nil`.
291 | fileprivate func replacementString(from replacement: Replacement) -> NSAttributedString? {
292 | var name: String {
293 | return replacement.unicodeScalars
294 | .filter { !($0.isZeroWidthJoiner || $0.isVariationSelector16) }
295 | .map { String(format: self.formatString, $0.value) }
296 | .joined(separator: self.separator)
297 | }
298 |
299 | guard let image = UIImage(named: name) else { return nil }
300 |
301 | let attachment = EmojicaAttachment()
302 | attachment.image = image
303 | attachment.resize(to: self.pointSize, with: self.font)
304 |
305 | if self.revertible {
306 | attachment.insert(unicodeScalars: replacement.unicodeScalars)
307 | }
308 |
309 | return NSAttributedString(attachment: attachment)
310 | }
311 |
312 | fileprivate func replaceString(from name: String, spriteSheet: String, coords: CGRect) -> NSAttributedString? {
313 | var spriteSheetImage = _spriteSheetCache[spriteSheet]
314 | if spriteSheetImage == nil {
315 | spriteSheetImage = UIImage(named: spriteSheet)
316 | guard let spriteSheetImage = spriteSheetImage else { return nil }
317 | _spriteSheetCache[spriteSheet] = spriteSheetImage
318 | }
319 | let attachment = EmojicaAttachment()
320 | attachment.image = spriteSheetImage?.extractEmojiAt(coords: coords)
321 | attachment.resize(to: self.pointSize, with: self.font)
322 | return NSAttributedString(attachment: attachment)
323 | }
324 |
325 | /// Used as fallback if `replacementString(from:)` returned `nil`.
326 | /// - parameter replacement: The `Replacement` instance to use in the conversion.
327 | /// - returns: An `NSAttributedString` with the fallback.
328 | fileprivate func fallbackString(from replacement: Replacement) -> NSAttributedString {
329 | let fallback = NSMutableAttributedString()
330 |
331 | let unicodeScalarStack: Stack = Stack(replacement.unicodeScalars)
332 | let failsafeStack: Stack = Stack()
333 |
334 | while let unicodeScalar = unicodeScalarStack.pop() {
335 |
336 | failsafeStack.push(unicodeScalar)
337 |
338 | guard !unicodeScalar.isVariationSelector16, !unicodeScalar.isZeroWidthJoiner else { continue }
339 |
340 | let name = String(format: self.formatString, unicodeScalar.value)
341 |
342 | if let image = UIImage(named: name) {
343 |
344 | let attachment = EmojicaAttachment()
345 | attachment.image = image
346 | attachment.resize(to: self.pointSize, with: self.font)
347 |
348 | if self.revertible {
349 | while let u = failsafeStack.pop() {
350 | attachment.insert(unicodeScalar: u)
351 | }
352 | }
353 |
354 | fallback.insert(NSAttributedString(attachment: attachment), at: 0)
355 |
356 | } else {
357 |
358 | // No image found, so insert unicode scalars into fallback as last resort.
359 | var string = String()
360 |
361 | while let u = failsafeStack.pop() {
362 | string.unicodeScalars.append(u)
363 | }
364 |
365 | fallback.insert(NSAttributedString(string: string), at: 0)
366 | }
367 | }
368 |
369 | return fallback
370 | }
371 | }
372 |
373 |
--------------------------------------------------------------------------------
/Example/EmojicaExample/EmojicaExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 37756A051DE7A8F8009F197A /* Emojica.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37CF900E1DE3B4A700FAE008 /* Emojica.framework */; };
11 | 37756A061DE7A8F8009F197A /* Emojica.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 37CF900E1DE3B4A700FAE008 /* Emojica.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12 | 37CF8FF81DE3B42600FAE008 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CF8FF71DE3B42600FAE008 /* AppDelegate.swift */; };
13 | 37CF8FFA1DE3B42600FAE008 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CF8FF91DE3B42600FAE008 /* ViewController.swift */; };
14 | 37CF8FFD1DE3B42600FAE008 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37CF8FFB1DE3B42600FAE008 /* Main.storyboard */; };
15 | 37CF8FFF1DE3B42600FAE008 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37CF8FFE1DE3B42600FAE008 /* Assets.xcassets */; };
16 | 37CF90021DE3B42600FAE008 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37CF90001DE3B42600FAE008 /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 37756A071DE7A8F8009F197A /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = 37CF90091DE3B4A700FAE008 /* Emojica.xcodeproj */;
23 | proxyType = 1;
24 | remoteGlobalIDString = 3755AE171DDF99F5007E5EBF;
25 | remoteInfo = Emojica;
26 | };
27 | 37CF900D1DE3B4A700FAE008 /* PBXContainerItemProxy */ = {
28 | isa = PBXContainerItemProxy;
29 | containerPortal = 37CF90091DE3B4A700FAE008 /* Emojica.xcodeproj */;
30 | proxyType = 2;
31 | remoteGlobalIDString = 3755AE181DDF99F5007E5EBF;
32 | remoteInfo = Emojica;
33 | };
34 | /* End PBXContainerItemProxy section */
35 |
36 | /* Begin PBXCopyFilesBuildPhase section */
37 | 37756A091DE7A8F8009F197A /* Embed Frameworks */ = {
38 | isa = PBXCopyFilesBuildPhase;
39 | buildActionMask = 2147483647;
40 | dstPath = "";
41 | dstSubfolderSpec = 10;
42 | files = (
43 | 37756A061DE7A8F8009F197A /* Emojica.framework in Embed Frameworks */,
44 | );
45 | name = "Embed Frameworks";
46 | runOnlyForDeploymentPostprocessing = 0;
47 | };
48 | /* End PBXCopyFilesBuildPhase section */
49 |
50 | /* Begin PBXFileReference section */
51 | 37CF8FF41DE3B42600FAE008 /* EmojicaExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EmojicaExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 37CF8FF71DE3B42600FAE008 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
53 | 37CF8FF91DE3B42600FAE008 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
54 | 37CF8FFC1DE3B42600FAE008 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
55 | 37CF8FFE1DE3B42600FAE008 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
56 | 37CF90011DE3B42600FAE008 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
57 | 37CF90031DE3B42600FAE008 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | 37CF90091DE3B4A700FAE008 /* Emojica.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Emojica.xcodeproj; path = ../../Emojica.xcodeproj; sourceTree = ""; };
59 | /* End PBXFileReference section */
60 |
61 | /* Begin PBXFrameworksBuildPhase section */
62 | 37CF8FF11DE3B42600FAE008 /* Frameworks */ = {
63 | isa = PBXFrameworksBuildPhase;
64 | buildActionMask = 2147483647;
65 | files = (
66 | 37756A051DE7A8F8009F197A /* Emojica.framework in Frameworks */,
67 | );
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | /* End PBXFrameworksBuildPhase section */
71 |
72 | /* Begin PBXGroup section */
73 | 376915591FBA4DB500E9E15B /* Images */ = {
74 | isa = PBXGroup;
75 | children = (
76 | );
77 | path = Images;
78 | sourceTree = "";
79 | };
80 | 37CF8FEB1DE3B42500FAE008 = {
81 | isa = PBXGroup;
82 | children = (
83 | 37CF90091DE3B4A700FAE008 /* Emojica.xcodeproj */,
84 | 37CF8FF61DE3B42600FAE008 /* Files */,
85 | 37CF8FF51DE3B42600FAE008 /* Products */,
86 | );
87 | sourceTree = "";
88 | };
89 | 37CF8FF51DE3B42600FAE008 /* Products */ = {
90 | isa = PBXGroup;
91 | children = (
92 | 37CF8FF41DE3B42600FAE008 /* EmojicaExample.app */,
93 | );
94 | name = Products;
95 | sourceTree = "";
96 | };
97 | 37CF8FF61DE3B42600FAE008 /* Files */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 37CF8FF71DE3B42600FAE008 /* AppDelegate.swift */,
101 | 37CF8FF91DE3B42600FAE008 /* ViewController.swift */,
102 | 37CF8FFB1DE3B42600FAE008 /* Main.storyboard */,
103 | 37CF8FFE1DE3B42600FAE008 /* Assets.xcassets */,
104 | 37CF90001DE3B42600FAE008 /* LaunchScreen.storyboard */,
105 | 37CF90031DE3B42600FAE008 /* Info.plist */,
106 | 376915591FBA4DB500E9E15B /* Images */,
107 | );
108 | path = Files;
109 | sourceTree = "";
110 | };
111 | 37CF900A1DE3B4A700FAE008 /* Products */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 37CF900E1DE3B4A700FAE008 /* Emojica.framework */,
115 | );
116 | name = Products;
117 | sourceTree = "";
118 | };
119 | /* End PBXGroup section */
120 |
121 | /* Begin PBXNativeTarget section */
122 | 37CF8FF31DE3B42600FAE008 /* EmojicaExample */ = {
123 | isa = PBXNativeTarget;
124 | buildConfigurationList = 37CF90061DE3B42600FAE008 /* Build configuration list for PBXNativeTarget "EmojicaExample" */;
125 | buildPhases = (
126 | 37CF8FF01DE3B42600FAE008 /* Sources */,
127 | 37CF8FF11DE3B42600FAE008 /* Frameworks */,
128 | 37CF8FF21DE3B42600FAE008 /* Resources */,
129 | 37756A091DE7A8F8009F197A /* Embed Frameworks */,
130 | );
131 | buildRules = (
132 | );
133 | dependencies = (
134 | 37756A081DE7A8F8009F197A /* PBXTargetDependency */,
135 | );
136 | name = EmojicaExample;
137 | productName = EmojicaExample;
138 | productReference = 37CF8FF41DE3B42600FAE008 /* EmojicaExample.app */;
139 | productType = "com.apple.product-type.application";
140 | };
141 | /* End PBXNativeTarget section */
142 |
143 | /* Begin PBXProject section */
144 | 37CF8FEC1DE3B42500FAE008 /* Project object */ = {
145 | isa = PBXProject;
146 | attributes = {
147 | LastSwiftUpdateCheck = 0810;
148 | LastUpgradeCheck = 0910;
149 | ORGANIZATIONNAME = "Dan Lindholm";
150 | TargetAttributes = {
151 | 37CF8FF31DE3B42600FAE008 = {
152 | CreatedOnToolsVersion = 8.1;
153 | LastSwiftMigration = 0910;
154 | ProvisioningStyle = Automatic;
155 | };
156 | };
157 | };
158 | buildConfigurationList = 37CF8FEF1DE3B42500FAE008 /* Build configuration list for PBXProject "EmojicaExample" */;
159 | compatibilityVersion = "Xcode 3.2";
160 | developmentRegion = English;
161 | hasScannedForEncodings = 0;
162 | knownRegions = (
163 | en,
164 | Base,
165 | );
166 | mainGroup = 37CF8FEB1DE3B42500FAE008;
167 | productRefGroup = 37CF8FF51DE3B42600FAE008 /* Products */;
168 | projectDirPath = "";
169 | projectReferences = (
170 | {
171 | ProductGroup = 37CF900A1DE3B4A700FAE008 /* Products */;
172 | ProjectRef = 37CF90091DE3B4A700FAE008 /* Emojica.xcodeproj */;
173 | },
174 | );
175 | projectRoot = "";
176 | targets = (
177 | 37CF8FF31DE3B42600FAE008 /* EmojicaExample */,
178 | );
179 | };
180 | /* End PBXProject section */
181 |
182 | /* Begin PBXReferenceProxy section */
183 | 37CF900E1DE3B4A700FAE008 /* Emojica.framework */ = {
184 | isa = PBXReferenceProxy;
185 | fileType = wrapper.framework;
186 | path = Emojica.framework;
187 | remoteRef = 37CF900D1DE3B4A700FAE008 /* PBXContainerItemProxy */;
188 | sourceTree = BUILT_PRODUCTS_DIR;
189 | };
190 | /* End PBXReferenceProxy section */
191 |
192 | /* Begin PBXResourcesBuildPhase section */
193 | 37CF8FF21DE3B42600FAE008 /* Resources */ = {
194 | isa = PBXResourcesBuildPhase;
195 | buildActionMask = 2147483647;
196 | files = (
197 | 37CF90021DE3B42600FAE008 /* LaunchScreen.storyboard in Resources */,
198 | 37CF8FFF1DE3B42600FAE008 /* Assets.xcassets in Resources */,
199 | 37CF8FFD1DE3B42600FAE008 /* Main.storyboard in Resources */,
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | /* End PBXResourcesBuildPhase section */
204 |
205 | /* Begin PBXSourcesBuildPhase section */
206 | 37CF8FF01DE3B42600FAE008 /* Sources */ = {
207 | isa = PBXSourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | 37CF8FFA1DE3B42600FAE008 /* ViewController.swift in Sources */,
211 | 37CF8FF81DE3B42600FAE008 /* AppDelegate.swift in Sources */,
212 | );
213 | runOnlyForDeploymentPostprocessing = 0;
214 | };
215 | /* End PBXSourcesBuildPhase section */
216 |
217 | /* Begin PBXTargetDependency section */
218 | 37756A081DE7A8F8009F197A /* PBXTargetDependency */ = {
219 | isa = PBXTargetDependency;
220 | name = Emojica;
221 | targetProxy = 37756A071DE7A8F8009F197A /* PBXContainerItemProxy */;
222 | };
223 | /* End PBXTargetDependency section */
224 |
225 | /* Begin PBXVariantGroup section */
226 | 37CF8FFB1DE3B42600FAE008 /* Main.storyboard */ = {
227 | isa = PBXVariantGroup;
228 | children = (
229 | 37CF8FFC1DE3B42600FAE008 /* Base */,
230 | );
231 | name = Main.storyboard;
232 | sourceTree = "";
233 | };
234 | 37CF90001DE3B42600FAE008 /* LaunchScreen.storyboard */ = {
235 | isa = PBXVariantGroup;
236 | children = (
237 | 37CF90011DE3B42600FAE008 /* Base */,
238 | );
239 | name = LaunchScreen.storyboard;
240 | sourceTree = "";
241 | };
242 | /* End PBXVariantGroup section */
243 |
244 | /* Begin XCBuildConfiguration section */
245 | 37CF90041DE3B42600FAE008 /* Debug */ = {
246 | isa = XCBuildConfiguration;
247 | buildSettings = {
248 | ALWAYS_SEARCH_USER_PATHS = NO;
249 | CLANG_ANALYZER_NONNULL = YES;
250 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
251 | CLANG_CXX_LIBRARY = "libc++";
252 | CLANG_ENABLE_MODULES = YES;
253 | CLANG_ENABLE_OBJC_ARC = YES;
254 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
255 | CLANG_WARN_BOOL_CONVERSION = YES;
256 | CLANG_WARN_COMMA = YES;
257 | CLANG_WARN_CONSTANT_CONVERSION = YES;
258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
259 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
260 | CLANG_WARN_EMPTY_BODY = YES;
261 | CLANG_WARN_ENUM_CONVERSION = YES;
262 | CLANG_WARN_INFINITE_RECURSION = YES;
263 | CLANG_WARN_INT_CONVERSION = YES;
264 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
265 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
266 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
267 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
268 | CLANG_WARN_STRICT_PROTOTYPES = YES;
269 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
270 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
271 | CLANG_WARN_UNREACHABLE_CODE = YES;
272 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
273 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
274 | COPY_PHASE_STRIP = NO;
275 | DEBUG_INFORMATION_FORMAT = dwarf;
276 | ENABLE_STRICT_OBJC_MSGSEND = YES;
277 | ENABLE_TESTABILITY = YES;
278 | GCC_C_LANGUAGE_STANDARD = gnu99;
279 | GCC_DYNAMIC_NO_PIC = NO;
280 | GCC_NO_COMMON_BLOCKS = YES;
281 | GCC_OPTIMIZATION_LEVEL = 0;
282 | GCC_PREPROCESSOR_DEFINITIONS = (
283 | "DEBUG=1",
284 | "$(inherited)",
285 | );
286 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
287 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
288 | GCC_WARN_UNDECLARED_SELECTOR = YES;
289 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
290 | GCC_WARN_UNUSED_FUNCTION = YES;
291 | GCC_WARN_UNUSED_VARIABLE = YES;
292 | IPHONEOS_DEPLOYMENT_TARGET = 10.1;
293 | MTL_ENABLE_DEBUG_INFO = YES;
294 | ONLY_ACTIVE_ARCH = YES;
295 | SDKROOT = iphoneos;
296 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
297 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
298 | TARGETED_DEVICE_FAMILY = "1,2";
299 | };
300 | name = Debug;
301 | };
302 | 37CF90051DE3B42600FAE008 /* Release */ = {
303 | isa = XCBuildConfiguration;
304 | buildSettings = {
305 | ALWAYS_SEARCH_USER_PATHS = NO;
306 | CLANG_ANALYZER_NONNULL = YES;
307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
308 | CLANG_CXX_LIBRARY = "libc++";
309 | CLANG_ENABLE_MODULES = YES;
310 | CLANG_ENABLE_OBJC_ARC = YES;
311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
312 | CLANG_WARN_BOOL_CONVERSION = YES;
313 | CLANG_WARN_COMMA = YES;
314 | CLANG_WARN_CONSTANT_CONVERSION = YES;
315 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
316 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
317 | CLANG_WARN_EMPTY_BODY = YES;
318 | CLANG_WARN_ENUM_CONVERSION = YES;
319 | CLANG_WARN_INFINITE_RECURSION = YES;
320 | CLANG_WARN_INT_CONVERSION = YES;
321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
325 | CLANG_WARN_STRICT_PROTOTYPES = YES;
326 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
327 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
328 | CLANG_WARN_UNREACHABLE_CODE = YES;
329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
331 | COPY_PHASE_STRIP = NO;
332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
333 | ENABLE_NS_ASSERTIONS = NO;
334 | ENABLE_STRICT_OBJC_MSGSEND = YES;
335 | GCC_C_LANGUAGE_STANDARD = gnu99;
336 | GCC_NO_COMMON_BLOCKS = YES;
337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
339 | GCC_WARN_UNDECLARED_SELECTOR = YES;
340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
341 | GCC_WARN_UNUSED_FUNCTION = YES;
342 | GCC_WARN_UNUSED_VARIABLE = YES;
343 | IPHONEOS_DEPLOYMENT_TARGET = 10.1;
344 | MTL_ENABLE_DEBUG_INFO = NO;
345 | SDKROOT = iphoneos;
346 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
347 | TARGETED_DEVICE_FAMILY = "1,2";
348 | VALIDATE_PRODUCT = YES;
349 | };
350 | name = Release;
351 | };
352 | 37CF90071DE3B42600FAE008 /* Debug */ = {
353 | isa = XCBuildConfiguration;
354 | buildSettings = {
355 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
357 | DEVELOPMENT_TEAM = "";
358 | INFOPLIST_FILE = "$(SRCROOT)/Files/Info.plist";
359 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
360 | PRODUCT_BUNDLE_IDENTIFIER = com.xoudini.EmojicaExample;
361 | PRODUCT_NAME = "$(TARGET_NAME)";
362 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
363 | SWIFT_VERSION = 4.0;
364 | };
365 | name = Debug;
366 | };
367 | 37CF90081DE3B42600FAE008 /* Release */ = {
368 | isa = XCBuildConfiguration;
369 | buildSettings = {
370 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
371 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
372 | DEVELOPMENT_TEAM = "";
373 | INFOPLIST_FILE = "$(SRCROOT)/Files/Info.plist";
374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
375 | PRODUCT_BUNDLE_IDENTIFIER = com.xoudini.EmojicaExample;
376 | PRODUCT_NAME = "$(TARGET_NAME)";
377 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
378 | SWIFT_VERSION = 4.0;
379 | };
380 | name = Release;
381 | };
382 | /* End XCBuildConfiguration section */
383 |
384 | /* Begin XCConfigurationList section */
385 | 37CF8FEF1DE3B42500FAE008 /* Build configuration list for PBXProject "EmojicaExample" */ = {
386 | isa = XCConfigurationList;
387 | buildConfigurations = (
388 | 37CF90041DE3B42600FAE008 /* Debug */,
389 | 37CF90051DE3B42600FAE008 /* Release */,
390 | );
391 | defaultConfigurationIsVisible = 0;
392 | defaultConfigurationName = Release;
393 | };
394 | 37CF90061DE3B42600FAE008 /* Build configuration list for PBXNativeTarget "EmojicaExample" */ = {
395 | isa = XCConfigurationList;
396 | buildConfigurations = (
397 | 37CF90071DE3B42600FAE008 /* Debug */,
398 | 37CF90081DE3B42600FAE008 /* Release */,
399 | );
400 | defaultConfigurationIsVisible = 0;
401 | defaultConfigurationName = Release;
402 | };
403 | /* End XCConfigurationList section */
404 | };
405 | rootObject = 37CF8FEC1DE3B42500FAE008 /* Project object */;
406 | }
407 |
--------------------------------------------------------------------------------
/Emojica.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3755AE1D1DDF99F5007E5EBF /* Emojica.h in Headers */ = {isa = PBXBuildFile; fileRef = 3755AE1B1DDF99F5007E5EBF /* Emojica.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | 3755AE251DDF9A79007E5EBF /* Emojica.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3755AE241DDF9A79007E5EBF /* Emojica.swift */; };
12 | 3755AE3D1DDF9C09007E5EBF /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3755AE3C1DDF9C09007E5EBF /* Extensions.swift */; };
13 | 37692A271FCC9F2100E9E15B /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37692A261FCC9F2100E9E15B /* Utility.swift */; };
14 | 37692A401FCCC0AB00E9E15B /* EmojicaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37692A3F1FCCC0AB00E9E15B /* EmojicaTests.swift */; };
15 | 37692A421FCCC0AB00E9E15B /* Emojica.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3755AE181DDF99F5007E5EBF /* Emojica.framework */; };
16 | 37CF90141DE4DF5400FAE008 /* Unicode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CF90131DE4DF5400FAE008 /* Unicode.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 37692A431FCCC0AB00E9E15B /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = 3755AE0F1DDF99F5007E5EBF /* Project object */;
23 | proxyType = 1;
24 | remoteGlobalIDString = 3755AE171DDF99F5007E5EBF;
25 | remoteInfo = Emojica;
26 | };
27 | /* End PBXContainerItemProxy section */
28 |
29 | /* Begin PBXFileReference section */
30 | 3755AE181DDF99F5007E5EBF /* Emojica.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Emojica.framework; sourceTree = BUILT_PRODUCTS_DIR; };
31 | 3755AE1B1DDF99F5007E5EBF /* Emojica.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Emojica.h; sourceTree = ""; };
32 | 3755AE1C1DDF99F5007E5EBF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
33 | 3755AE241DDF9A79007E5EBF /* Emojica.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Emojica.swift; sourceTree = ""; };
34 | 3755AE3C1DDF9C09007E5EBF /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
35 | 37692A261FCC9F2100E9E15B /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; };
36 | 37692A3D1FCCC0AB00E9E15B /* EmojicaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EmojicaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
37 | 37692A3F1FCCC0AB00E9E15B /* EmojicaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojicaTests.swift; sourceTree = ""; };
38 | 37692A411FCCC0AB00E9E15B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
39 | 37CF90131DE4DF5400FAE008 /* Unicode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Unicode.swift; sourceTree = ""; };
40 | /* End PBXFileReference section */
41 |
42 | /* Begin PBXFrameworksBuildPhase section */
43 | 3755AE141DDF99F5007E5EBF /* Frameworks */ = {
44 | isa = PBXFrameworksBuildPhase;
45 | buildActionMask = 2147483647;
46 | files = (
47 | );
48 | runOnlyForDeploymentPostprocessing = 0;
49 | };
50 | 37692A3A1FCCC0AB00E9E15B /* Frameworks */ = {
51 | isa = PBXFrameworksBuildPhase;
52 | buildActionMask = 2147483647;
53 | files = (
54 | 37692A421FCCC0AB00E9E15B /* Emojica.framework in Frameworks */,
55 | );
56 | runOnlyForDeploymentPostprocessing = 0;
57 | };
58 | /* End PBXFrameworksBuildPhase section */
59 |
60 | /* Begin PBXGroup section */
61 | 3755AE0E1DDF99F5007E5EBF = {
62 | isa = PBXGroup;
63 | children = (
64 | 3755AE1A1DDF99F5007E5EBF /* Source */,
65 | 37692A3E1FCCC0AB00E9E15B /* EmojicaTests */,
66 | 3755AE191DDF99F5007E5EBF /* Products */,
67 | );
68 | sourceTree = "";
69 | };
70 | 3755AE191DDF99F5007E5EBF /* Products */ = {
71 | isa = PBXGroup;
72 | children = (
73 | 3755AE181DDF99F5007E5EBF /* Emojica.framework */,
74 | 37692A3D1FCCC0AB00E9E15B /* EmojicaTests.xctest */,
75 | );
76 | name = Products;
77 | sourceTree = "";
78 | };
79 | 3755AE1A1DDF99F5007E5EBF /* Source */ = {
80 | isa = PBXGroup;
81 | children = (
82 | 3755AE241DDF9A79007E5EBF /* Emojica.swift */,
83 | 3755AE3C1DDF9C09007E5EBF /* Extensions.swift */,
84 | 37CF90131DE4DF5400FAE008 /* Unicode.swift */,
85 | 37692A261FCC9F2100E9E15B /* Utility.swift */,
86 | 3755AE231DDF9A45007E5EBF /* Supporting Files */,
87 | );
88 | path = Source;
89 | sourceTree = "";
90 | };
91 | 3755AE231DDF9A45007E5EBF /* Supporting Files */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 3755AE1B1DDF99F5007E5EBF /* Emojica.h */,
95 | 3755AE1C1DDF99F5007E5EBF /* Info.plist */,
96 | );
97 | name = "Supporting Files";
98 | sourceTree = "";
99 | };
100 | 37692A3E1FCCC0AB00E9E15B /* EmojicaTests */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 37692A3F1FCCC0AB00E9E15B /* EmojicaTests.swift */,
104 | 37692A411FCCC0AB00E9E15B /* Info.plist */,
105 | );
106 | path = EmojicaTests;
107 | sourceTree = "";
108 | };
109 | /* End PBXGroup section */
110 |
111 | /* Begin PBXHeadersBuildPhase section */
112 | 3755AE151DDF99F5007E5EBF /* Headers */ = {
113 | isa = PBXHeadersBuildPhase;
114 | buildActionMask = 2147483647;
115 | files = (
116 | 3755AE1D1DDF99F5007E5EBF /* Emojica.h in Headers */,
117 | );
118 | runOnlyForDeploymentPostprocessing = 0;
119 | };
120 | /* End PBXHeadersBuildPhase section */
121 |
122 | /* Begin PBXNativeTarget section */
123 | 3755AE171DDF99F5007E5EBF /* Emojica */ = {
124 | isa = PBXNativeTarget;
125 | buildConfigurationList = 3755AE201DDF99F5007E5EBF /* Build configuration list for PBXNativeTarget "Emojica" */;
126 | buildPhases = (
127 | 3755AE131DDF99F5007E5EBF /* Sources */,
128 | 3755AE141DDF99F5007E5EBF /* Frameworks */,
129 | 3755AE151DDF99F5007E5EBF /* Headers */,
130 | 3755AE161DDF99F5007E5EBF /* Resources */,
131 | );
132 | buildRules = (
133 | );
134 | dependencies = (
135 | );
136 | name = Emojica;
137 | productName = Emojica;
138 | productReference = 3755AE181DDF99F5007E5EBF /* Emojica.framework */;
139 | productType = "com.apple.product-type.framework";
140 | };
141 | 37692A3C1FCCC0AB00E9E15B /* EmojicaTests */ = {
142 | isa = PBXNativeTarget;
143 | buildConfigurationList = 37692A471FCCC0AB00E9E15B /* Build configuration list for PBXNativeTarget "EmojicaTests" */;
144 | buildPhases = (
145 | 37692A391FCCC0AB00E9E15B /* Sources */,
146 | 37692A3A1FCCC0AB00E9E15B /* Frameworks */,
147 | 37692A3B1FCCC0AB00E9E15B /* Resources */,
148 | );
149 | buildRules = (
150 | );
151 | dependencies = (
152 | 37692A441FCCC0AB00E9E15B /* PBXTargetDependency */,
153 | );
154 | name = EmojicaTests;
155 | productName = EmojicaTests;
156 | productReference = 37692A3D1FCCC0AB00E9E15B /* EmojicaTests.xctest */;
157 | productType = "com.apple.product-type.bundle.unit-test";
158 | };
159 | /* End PBXNativeTarget section */
160 |
161 | /* Begin PBXProject section */
162 | 3755AE0F1DDF99F5007E5EBF /* Project object */ = {
163 | isa = PBXProject;
164 | attributes = {
165 | LastSwiftUpdateCheck = 0910;
166 | LastUpgradeCheck = 0910;
167 | ORGANIZATIONNAME = "Dan Lindholm";
168 | TargetAttributes = {
169 | 3755AE171DDF99F5007E5EBF = {
170 | CreatedOnToolsVersion = 8.1;
171 | LastSwiftMigration = 0910;
172 | ProvisioningStyle = Automatic;
173 | };
174 | 37692A3C1FCCC0AB00E9E15B = {
175 | CreatedOnToolsVersion = 9.1;
176 | ProvisioningStyle = Automatic;
177 | };
178 | };
179 | };
180 | buildConfigurationList = 3755AE121DDF99F5007E5EBF /* Build configuration list for PBXProject "Emojica" */;
181 | compatibilityVersion = "Xcode 3.2";
182 | developmentRegion = English;
183 | hasScannedForEncodings = 0;
184 | knownRegions = (
185 | en,
186 | );
187 | mainGroup = 3755AE0E1DDF99F5007E5EBF;
188 | productRefGroup = 3755AE191DDF99F5007E5EBF /* Products */;
189 | projectDirPath = "";
190 | projectRoot = "";
191 | targets = (
192 | 3755AE171DDF99F5007E5EBF /* Emojica */,
193 | 37692A3C1FCCC0AB00E9E15B /* EmojicaTests */,
194 | );
195 | };
196 | /* End PBXProject section */
197 |
198 | /* Begin PBXResourcesBuildPhase section */
199 | 3755AE161DDF99F5007E5EBF /* Resources */ = {
200 | isa = PBXResourcesBuildPhase;
201 | buildActionMask = 2147483647;
202 | files = (
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | 37692A3B1FCCC0AB00E9E15B /* Resources */ = {
207 | isa = PBXResourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | };
213 | /* End PBXResourcesBuildPhase section */
214 |
215 | /* Begin PBXSourcesBuildPhase section */
216 | 3755AE131DDF99F5007E5EBF /* Sources */ = {
217 | isa = PBXSourcesBuildPhase;
218 | buildActionMask = 2147483647;
219 | files = (
220 | 37692A271FCC9F2100E9E15B /* Utility.swift in Sources */,
221 | 3755AE251DDF9A79007E5EBF /* Emojica.swift in Sources */,
222 | 3755AE3D1DDF9C09007E5EBF /* Extensions.swift in Sources */,
223 | 37CF90141DE4DF5400FAE008 /* Unicode.swift in Sources */,
224 | );
225 | runOnlyForDeploymentPostprocessing = 0;
226 | };
227 | 37692A391FCCC0AB00E9E15B /* Sources */ = {
228 | isa = PBXSourcesBuildPhase;
229 | buildActionMask = 2147483647;
230 | files = (
231 | 37692A401FCCC0AB00E9E15B /* EmojicaTests.swift in Sources */,
232 | );
233 | runOnlyForDeploymentPostprocessing = 0;
234 | };
235 | /* End PBXSourcesBuildPhase section */
236 |
237 | /* Begin PBXTargetDependency section */
238 | 37692A441FCCC0AB00E9E15B /* PBXTargetDependency */ = {
239 | isa = PBXTargetDependency;
240 | target = 3755AE171DDF99F5007E5EBF /* Emojica */;
241 | targetProxy = 37692A431FCCC0AB00E9E15B /* PBXContainerItemProxy */;
242 | };
243 | /* End PBXTargetDependency section */
244 |
245 | /* Begin XCBuildConfiguration section */
246 | 3755AE1E1DDF99F5007E5EBF /* Debug */ = {
247 | isa = XCBuildConfiguration;
248 | buildSettings = {
249 | ALWAYS_SEARCH_USER_PATHS = NO;
250 | CLANG_ANALYZER_NONNULL = YES;
251 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
252 | CLANG_CXX_LIBRARY = "libc++";
253 | CLANG_ENABLE_MODULES = YES;
254 | CLANG_ENABLE_OBJC_ARC = YES;
255 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
256 | CLANG_WARN_BOOL_CONVERSION = YES;
257 | CLANG_WARN_COMMA = YES;
258 | CLANG_WARN_CONSTANT_CONVERSION = YES;
259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
261 | CLANG_WARN_EMPTY_BODY = YES;
262 | CLANG_WARN_ENUM_CONVERSION = YES;
263 | CLANG_WARN_INFINITE_RECURSION = YES;
264 | CLANG_WARN_INT_CONVERSION = YES;
265 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
266 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
268 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
269 | CLANG_WARN_STRICT_PROTOTYPES = YES;
270 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
271 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
272 | CLANG_WARN_UNREACHABLE_CODE = YES;
273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
274 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
275 | COPY_PHASE_STRIP = NO;
276 | CURRENT_PROJECT_VERSION = 1;
277 | DEBUG_INFORMATION_FORMAT = dwarf;
278 | ENABLE_STRICT_OBJC_MSGSEND = YES;
279 | ENABLE_TESTABILITY = YES;
280 | GCC_C_LANGUAGE_STANDARD = gnu99;
281 | GCC_DYNAMIC_NO_PIC = NO;
282 | GCC_NO_COMMON_BLOCKS = YES;
283 | GCC_OPTIMIZATION_LEVEL = 0;
284 | GCC_PREPROCESSOR_DEFINITIONS = (
285 | "DEBUG=1",
286 | "$(inherited)",
287 | );
288 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
289 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
290 | GCC_WARN_UNDECLARED_SELECTOR = YES;
291 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
292 | GCC_WARN_UNUSED_FUNCTION = YES;
293 | GCC_WARN_UNUSED_VARIABLE = YES;
294 | IPHONEOS_DEPLOYMENT_TARGET = 10.1;
295 | MTL_ENABLE_DEBUG_INFO = YES;
296 | ONLY_ACTIVE_ARCH = YES;
297 | SDKROOT = iphoneos;
298 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
299 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
300 | TARGETED_DEVICE_FAMILY = "1,2";
301 | VERSIONING_SYSTEM = "apple-generic";
302 | VERSION_INFO_PREFIX = "";
303 | };
304 | name = Debug;
305 | };
306 | 3755AE1F1DDF99F5007E5EBF /* Release */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | CLANG_ANALYZER_NONNULL = YES;
311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
312 | CLANG_CXX_LIBRARY = "libc++";
313 | CLANG_ENABLE_MODULES = YES;
314 | CLANG_ENABLE_OBJC_ARC = YES;
315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
316 | CLANG_WARN_BOOL_CONVERSION = YES;
317 | CLANG_WARN_COMMA = YES;
318 | CLANG_WARN_CONSTANT_CONVERSION = YES;
319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
321 | CLANG_WARN_EMPTY_BODY = YES;
322 | CLANG_WARN_ENUM_CONVERSION = YES;
323 | CLANG_WARN_INFINITE_RECURSION = YES;
324 | CLANG_WARN_INT_CONVERSION = YES;
325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
329 | CLANG_WARN_STRICT_PROTOTYPES = YES;
330 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
331 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
332 | CLANG_WARN_UNREACHABLE_CODE = YES;
333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
334 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
335 | COPY_PHASE_STRIP = NO;
336 | CURRENT_PROJECT_VERSION = 1;
337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
338 | ENABLE_NS_ASSERTIONS = NO;
339 | ENABLE_STRICT_OBJC_MSGSEND = YES;
340 | GCC_C_LANGUAGE_STANDARD = gnu99;
341 | GCC_NO_COMMON_BLOCKS = YES;
342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
344 | GCC_WARN_UNDECLARED_SELECTOR = YES;
345 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
346 | GCC_WARN_UNUSED_FUNCTION = YES;
347 | GCC_WARN_UNUSED_VARIABLE = YES;
348 | IPHONEOS_DEPLOYMENT_TARGET = 10.1;
349 | MTL_ENABLE_DEBUG_INFO = NO;
350 | SDKROOT = iphoneos;
351 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
352 | TARGETED_DEVICE_FAMILY = "1,2";
353 | VALIDATE_PRODUCT = YES;
354 | VERSIONING_SYSTEM = "apple-generic";
355 | VERSION_INFO_PREFIX = "";
356 | };
357 | name = Release;
358 | };
359 | 3755AE211DDF99F5007E5EBF /* Debug */ = {
360 | isa = XCBuildConfiguration;
361 | buildSettings = {
362 | CLANG_ENABLE_MODULES = YES;
363 | CODE_SIGN_IDENTITY = "";
364 | DEFINES_MODULE = YES;
365 | DYLIB_COMPATIBILITY_VERSION = 1;
366 | DYLIB_CURRENT_VERSION = 1;
367 | DYLIB_INSTALL_NAME_BASE = "@rpath";
368 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
369 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
370 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
371 | PRODUCT_BUNDLE_IDENTIFIER = com.xoudini.Emojica;
372 | PRODUCT_NAME = "$(TARGET_NAME)";
373 | SKIP_INSTALL = YES;
374 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
375 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
376 | SWIFT_VERSION = 4.0;
377 | };
378 | name = Debug;
379 | };
380 | 3755AE221DDF99F5007E5EBF /* Release */ = {
381 | isa = XCBuildConfiguration;
382 | buildSettings = {
383 | CLANG_ENABLE_MODULES = YES;
384 | CODE_SIGN_IDENTITY = "";
385 | DEFINES_MODULE = YES;
386 | DYLIB_COMPATIBILITY_VERSION = 1;
387 | DYLIB_CURRENT_VERSION = 1;
388 | DYLIB_INSTALL_NAME_BASE = "@rpath";
389 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
390 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
391 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
392 | PRODUCT_BUNDLE_IDENTIFIER = com.xoudini.Emojica;
393 | PRODUCT_NAME = "$(TARGET_NAME)";
394 | SKIP_INSTALL = YES;
395 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
396 | SWIFT_VERSION = 4.0;
397 | };
398 | name = Release;
399 | };
400 | 37692A451FCCC0AB00E9E15B /* Debug */ = {
401 | isa = XCBuildConfiguration;
402 | buildSettings = {
403 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
405 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
406 | CODE_SIGN_IDENTITY = "iPhone Developer";
407 | CODE_SIGN_STYLE = Automatic;
408 | GCC_C_LANGUAGE_STANDARD = gnu11;
409 | INFOPLIST_FILE = EmojicaTests/Info.plist;
410 | IPHONEOS_DEPLOYMENT_TARGET = 11.1;
411 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
412 | PRODUCT_BUNDLE_IDENTIFIER = com.xoudini.EmojicaTests;
413 | PRODUCT_NAME = "$(TARGET_NAME)";
414 | SWIFT_VERSION = 4.0;
415 | TARGETED_DEVICE_FAMILY = "1,2";
416 | };
417 | name = Debug;
418 | };
419 | 37692A461FCCC0AB00E9E15B /* Release */ = {
420 | isa = XCBuildConfiguration;
421 | buildSettings = {
422 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
423 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
424 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
425 | CODE_SIGN_IDENTITY = "iPhone Developer";
426 | CODE_SIGN_STYLE = Automatic;
427 | GCC_C_LANGUAGE_STANDARD = gnu11;
428 | INFOPLIST_FILE = EmojicaTests/Info.plist;
429 | IPHONEOS_DEPLOYMENT_TARGET = 11.1;
430 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
431 | PRODUCT_BUNDLE_IDENTIFIER = com.xoudini.EmojicaTests;
432 | PRODUCT_NAME = "$(TARGET_NAME)";
433 | SWIFT_VERSION = 4.0;
434 | TARGETED_DEVICE_FAMILY = "1,2";
435 | };
436 | name = Release;
437 | };
438 | /* End XCBuildConfiguration section */
439 |
440 | /* Begin XCConfigurationList section */
441 | 3755AE121DDF99F5007E5EBF /* Build configuration list for PBXProject "Emojica" */ = {
442 | isa = XCConfigurationList;
443 | buildConfigurations = (
444 | 3755AE1E1DDF99F5007E5EBF /* Debug */,
445 | 3755AE1F1DDF99F5007E5EBF /* Release */,
446 | );
447 | defaultConfigurationIsVisible = 0;
448 | defaultConfigurationName = Release;
449 | };
450 | 3755AE201DDF99F5007E5EBF /* Build configuration list for PBXNativeTarget "Emojica" */ = {
451 | isa = XCConfigurationList;
452 | buildConfigurations = (
453 | 3755AE211DDF99F5007E5EBF /* Debug */,
454 | 3755AE221DDF99F5007E5EBF /* Release */,
455 | );
456 | defaultConfigurationIsVisible = 0;
457 | defaultConfigurationName = Release;
458 | };
459 | 37692A471FCCC0AB00E9E15B /* Build configuration list for PBXNativeTarget "EmojicaTests" */ = {
460 | isa = XCConfigurationList;
461 | buildConfigurations = (
462 | 37692A451FCCC0AB00E9E15B /* Debug */,
463 | 37692A461FCCC0AB00E9E15B /* Release */,
464 | );
465 | defaultConfigurationIsVisible = 0;
466 | defaultConfigurationName = Release;
467 | };
468 | /* End XCConfigurationList section */
469 | };
470 | rootObject = 3755AE0F1DDF99F5007E5EBF /* Project object */;
471 | }
472 |
--------------------------------------------------------------------------------
/Source/Unicode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ------------------------------------------------------------------------
3 | //
4 | // Copyright 2017 Dan Lindholm
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // ------------------------------------------------------------------------
19 | //
20 | // Unicode.swift
21 | //
22 |
23 | import Foundation
24 |
25 | /// A container for all base character code points relevant for emoji.
26 | struct Unicode {
27 |
28 | /// The code points for additional characters not found in any of the blocks in `Unicode.Block`.
29 | static var additionalCharacters: [UInt32] {
30 | return [
31 | // Basic Latin
32 | 0x0023, // NUMBER SIGN
33 | 0x002a, // ASTERISK
34 | 0x0030, // DIGIT ZERO
35 | 0x0031, // DIGIT ONE
36 | 0x0032, // DIGIT TWO
37 | 0x0033, // DIGIT THREE
38 | 0x0034, // DIGIT FOUR
39 | 0x0035, // DIGIT FIVE
40 | 0x0036, // DIGIT SIX
41 | 0x0037, // DIGIT SEVEN
42 | 0x0038, // DIGIT EIGHT
43 | 0x0039, // DIGIT NINE
44 |
45 | // Latin 1 Supplement
46 | 0x00a9, // COPYRIGHT SIGN
47 | 0x00ae, // REGISTERED SIGN
48 |
49 | // General Punctuation
50 | 0x203c, // DOUBLE EXCLAMATION MARK
51 | 0x2049, // EXCLAMATION QUESTION MARK
52 |
53 | // Letterlike Symbols
54 | 0x2122, // TRADE MARK SIGN
55 | 0x2139, // INFORMATION SOURCE
56 |
57 | // Arrows
58 | 0x2194, // LEFT RIGHT ARROW
59 | 0x2195, // UP DOWN ARROW
60 | 0x2196, // NORTH WEST ARROW
61 | 0x2197, // NORTH EAST ARROW
62 | 0x2198, // SOUTH EAST ARROW
63 | 0x2199, // SOUTH WEST ARROW
64 | 0x21a9, // LEFTWARDS ARROW WITH HOOK
65 | 0x21aa, // RIGHTWARDS ARROW WITH HOOK
66 |
67 | // Miscellaneous Technical
68 | 0x231a, // WATCH
69 | 0x231b, // HOURGLASS
70 | 0x2328, // KEYBOARD
71 | 0x23cf, // EJECT SYMBOL
72 | 0x23e9, //
73 | 0x23ea, //
74 | 0x23eb, //
75 | 0x23ec, //
76 | 0x23ed, // BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR
77 | 0x23ee, // BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR
78 | 0x23ef, // BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR
79 | 0x23f0, //
80 | 0x23f1, // STOPWATCH
81 | 0x23f2, // TIMER CLOCK
82 | 0x23f3, //
83 | 0x23f8, // DOUBLE VERTICAL BAR
84 | 0x23f9, // BLACK SQUARE FOR STOP
85 | 0x23fa, // BLACK CIRCLE FOR RECORD
86 |
87 | // Enclosed Alphanumerics
88 | 0x24c2, // CIRCLED LATIN CAPITAL LETTER M
89 |
90 | // Geometric Shapes
91 | 0x25aa, // BLACK SMALL SQUARE
92 | 0x25ab, // WHITE SMALL SQUARE
93 | 0x25b6, // BLACK RIGHT-POINTING TRIANGLE
94 | 0x25c0, // BLACK LEFT-POINTING TRIANGLE
95 | 0x25fb, // WHITE MEDIUM SQUARE
96 | 0x25fc, // BLACK MEDIUM SQUARE
97 | 0x25fd, // WHITE MEDIUM SMALL SQUARE
98 | 0x25fe, // BLACK MEDIUM SMALL SQUARE
99 |
100 | // Supplemental Arrows B
101 | 0x2934, // ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS
102 | 0x2935, // ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS
103 |
104 | // Miscellaneous Symbols And Arrows
105 | 0x2b05, // LEFTWARDS BLACK ARROW
106 | 0x2b06, // UPWARDS BLACK ARROW
107 | 0x2b07, // DOWNWARDS BLACK ARROW
108 | 0x2b1b, // BLACK LARGE SQUARE
109 | 0x2b1c, // WHITE LARGE SQUARE
110 | 0x2b50, // WHITE MEDIUM STAR
111 | 0x2b55, // HEAVY LARGE CIRCLE
112 |
113 | // CJK Symbols And Punctuation
114 | 0x3030, // WAVY DASH
115 | 0x303d, // PART ALTERNATION MARK
116 |
117 | // Enclosed CJK Letters And Months
118 | 0x3297, // CIRCLED IDEOGRAPH CONGRATULATION
119 | 0x3299, // CIRCLED IDEOGRAPH SECRET
120 |
121 | // Mahjong Tiles
122 | 0x1f004, // MAHJONG TILE RED DRAGON
123 |
124 | // Playing Cards
125 | 0x1f0cf, //
126 |
127 | // Enclosed Alphanumeric Supplement
128 | 0x1f170, // NEGATIVE SQUARED LATIN CAPITAL LETTER A
129 | 0x1f171, // NEGATIVE SQUARED LATIN CAPITAL LETTER B
130 | 0x1f17e, // NEGATIVE SQUARED LATIN CAPITAL LETTER O
131 | 0x1f17f, // NEGATIVE SQUARED LATIN CAPITAL LETTER P
132 | 0x1f18e, //
133 | 0x1f191, //
134 | 0x1f192, //
135 | 0x1f193, //
136 | 0x1f194, //
137 | 0x1f195, //
138 | 0x1f196, //
139 | 0x1f197, //
140 | 0x1f198, //
141 | 0x1f199, //
142 | 0x1f19a, //
143 | 0x1f1e6, // REGIONAL INDICATOR SYMBOL LETTER A
144 | 0x1f1e7, // REGIONAL INDICATOR SYMBOL LETTER B
145 | 0x1f1e8, // REGIONAL INDICATOR SYMBOL LETTER C
146 | 0x1f1e9, // REGIONAL INDICATOR SYMBOL LETTER D
147 | 0x1f1ea, // REGIONAL INDICATOR SYMBOL LETTER E
148 | 0x1f1eb, // REGIONAL INDICATOR SYMBOL LETTER F
149 | 0x1f1ec, // REGIONAL INDICATOR SYMBOL LETTER G
150 | 0x1f1ed, // REGIONAL INDICATOR SYMBOL LETTER H
151 | 0x1f1ee, // REGIONAL INDICATOR SYMBOL LETTER I
152 | 0x1f1ef, // REGIONAL INDICATOR SYMBOL LETTER J
153 | 0x1f1f0, // REGIONAL INDICATOR SYMBOL LETTER K
154 | 0x1f1f1, // REGIONAL INDICATOR SYMBOL LETTER L
155 | 0x1f1f2, // REGIONAL INDICATOR SYMBOL LETTER M
156 | 0x1f1f3, // REGIONAL INDICATOR SYMBOL LETTER N
157 | 0x1f1f4, // REGIONAL INDICATOR SYMBOL LETTER O
158 | 0x1f1f5, // REGIONAL INDICATOR SYMBOL LETTER P
159 | 0x1f1f6, // REGIONAL INDICATOR SYMBOL LETTER Q
160 | 0x1f1f7, // REGIONAL INDICATOR SYMBOL LETTER R
161 | 0x1f1f8, // REGIONAL INDICATOR SYMBOL LETTER S
162 | 0x1f1f9, // REGIONAL INDICATOR SYMBOL LETTER T
163 | 0x1f1fa, // REGIONAL INDICATOR SYMBOL LETTER U
164 | 0x1f1fb, // REGIONAL INDICATOR SYMBOL LETTER V
165 | 0x1f1fc, // REGIONAL INDICATOR SYMBOL LETTER W
166 | 0x1f1fd, // REGIONAL INDICATOR SYMBOL LETTER X
167 | 0x1f1fe, // REGIONAL INDICATOR SYMBOL LETTER Y
168 | 0x1f1ff, // REGIONAL INDICATOR SYMBOL LETTER Z
169 |
170 | // Enclosed Ideographic Supplement
171 | 0x1f201, //
172 | 0x1f202, // SQUARED KATAKANA SA
173 | 0x1f21a, // SQUARED CJK UNIFIED IDEOGRAPH-7121
174 | 0x1f22f, // SQUARED CJK UNIFIED IDEOGRAPH-6307
175 | 0x1f232, //
176 | 0x1f233, //
177 | 0x1f234, //
178 | 0x1f235, //
179 | 0x1f236, //
180 | 0x1f237, // SQUARED CJK UNIFIED IDEOGRAPH-6708
181 | 0x1f238, //
182 | 0x1f239, //
183 | 0x1f23a, //
184 | 0x1f250, //
185 | 0x1f251 //
186 | ]
187 | }
188 |
189 | /// The code points of keycap base characters.
190 | static var keycapBaseCharacters: [UInt32] {
191 | return [
192 | 0x0023, // NUMBER SIGN
193 | 0x002a, // ASTERISK
194 | 0x0030, // DIGIT ZERO
195 | 0x0031, // DIGIT ONE
196 | 0x0032, // DIGIT TWO
197 | 0x0033, // DIGIT THREE
198 | 0x0034, // DIGIT FOUR
199 | 0x0035, // DIGIT FIVE
200 | 0x0036, // DIGIT SIX
201 | 0x0037, // DIGIT SEVEN
202 | 0x0038, // DIGIT EIGHT
203 | 0x0039 // DIGIT NINE
204 | ]
205 | }
206 | }
207 |
208 | extension Unicode {
209 | /// Enumerator for the six main blocks used for emoji.
210 | enum Block {
211 | /// Miscellaneous Symbols
212 | case miscellaneousSymbols
213 | /// Dingbats
214 | case dingbats
215 | /// Miscellaneous Symbols And Pictographs
216 | case miscellaneousSymbolsAndPictographs
217 | /// Emoticons
218 | case emoticons
219 | /// Transport And Map Symbols
220 | case transportAndMapSymbols
221 | /// Supplemental Symbols And Pictographs
222 | case supplementalSymbolsAndPictographs
223 | }
224 | }
225 |
226 | extension Unicode.Block {
227 |
228 | /// The range of the code points in the current block.
229 | var range: CountableClosedRange {
230 | switch self {
231 | case .miscellaneousSymbols:
232 | return 0x2600...0x26ff
233 | case .dingbats:
234 | return 0x2700...0x27bf
235 | case .miscellaneousSymbolsAndPictographs:
236 | return 0x1f300...0x1f5ff
237 | case .emoticons:
238 | return 0x1f600...0x1f64f
239 | case .transportAndMapSymbols:
240 | return 0x1f680...0x1f6ff
241 | case .supplementalSymbolsAndPictographs:
242 | return 0x1f900...0x1f9ff
243 | }
244 | }
245 |
246 | /// The code points of unassigned characters in the current block, as an array of `UInt32` values.
247 | ///
248 | /// The return values in this computed property are array literals for a few reasons:
249 | /// 1. The values are constant, and the Unicode® Standard is amended infrequently (roughly once a year).
250 | /// 2. This property is accessed quite often.
251 | /// 3. Flattening an array of ranges would increase complexity – see `flatMap(_:)`.
252 | var unassigned: [UInt32] {
253 | switch self {
254 | case .miscellaneousSymbols,
255 | .dingbats,
256 | .miscellaneousSymbolsAndPictographs,
257 | .emoticons:
258 | // These blocks don't contain any unassigned characters.
259 | return []
260 |
261 | case .transportAndMapSymbols:
262 | return [
263 | 0x1f6d5,
264 | 0x1f6d6,
265 | 0x1f6d7,
266 | 0x1f6d8,
267 | 0x1f6d9,
268 | 0x1f6da,
269 | 0x1f6db,
270 | 0x1f6dc,
271 | 0x1f6dd,
272 | 0x1f6de,
273 | 0x1f6df,
274 |
275 | 0x1f6ed,
276 | 0x1f6ee,
277 | 0x1f6ef,
278 |
279 | 0x1f6f9,
280 | 0x1f6fa,
281 | 0x1f6fb,
282 | 0x1f6fc,
283 | 0x1f6fd,
284 | 0x1f6fe,
285 | 0x1f6ff
286 | ]
287 | case .supplementalSymbolsAndPictographs:
288 | return [
289 | 0x1f90c,
290 | 0x1f90d,
291 | 0x1f90e,
292 | 0x1f90f,
293 |
294 | 0x1f93f,
295 |
296 | 0x1f94d,
297 | 0x1f94e,
298 | 0x1f94f,
299 |
300 | 0x1f96c,
301 | 0x1f96d,
302 | 0x1f96e,
303 | 0x1f96f,
304 |
305 | 0x1f970,
306 | 0x1f971,
307 | 0x1f972,
308 | 0x1f973,
309 | 0x1f974,
310 | 0x1f975,
311 | 0x1f976,
312 | 0x1f977,
313 | 0x1f978,
314 | 0x1f979,
315 | 0x1f97a,
316 | 0x1f97b,
317 | 0x1f97c,
318 | 0x1f97d,
319 | 0x1f97e,
320 | 0x1f97f,
321 |
322 | 0x1f998,
323 | 0x1f999,
324 | 0x1f99a,
325 | 0x1f99b,
326 | 0x1f99c,
327 | 0x1f99d,
328 | 0x1f99e,
329 | 0x1f99f,
330 |
331 | 0x1f9a0,
332 | 0x1f9a1,
333 | 0x1f9a2,
334 | 0x1f9a3,
335 | 0x1f9a4,
336 | 0x1f9a5,
337 | 0x1f9a6,
338 | 0x1f9a7,
339 | 0x1f9a8,
340 | 0x1f9a9,
341 | 0x1f9aa,
342 | 0x1f9ab,
343 | 0x1f9ac,
344 | 0x1f9ad,
345 | 0x1f9ae,
346 | 0x1f9af,
347 |
348 | 0x1f9b0,
349 | 0x1f9b1,
350 | 0x1f9b2,
351 | 0x1f9b3,
352 | 0x1f9b4,
353 | 0x1f9b5,
354 | 0x1f9b6,
355 | 0x1f9b7,
356 | 0x1f9b8,
357 | 0x1f9b9,
358 | 0x1f9ba,
359 | 0x1f9bb,
360 | 0x1f9bc,
361 | 0x1f9bd,
362 | 0x1f9be,
363 | 0x1f9bf,
364 |
365 | 0x1f9c1,
366 | 0x1f9c2,
367 | 0x1f9c3,
368 | 0x1f9c4,
369 | 0x1f9c5,
370 | 0x1f9c6,
371 | 0x1f9c7,
372 | 0x1f9c8,
373 | 0x1f9c9,
374 | 0x1f9ca,
375 | 0x1f9cb,
376 | 0x1f9cc,
377 | 0x1f9cd,
378 | 0x1f9ce,
379 | 0x1f9cf,
380 |
381 | 0x1f9e7,
382 | 0x1f9e8,
383 | 0x1f9e9,
384 | 0x1f9ea,
385 | 0x1f9eb,
386 | 0x1f9ec,
387 | 0x1f9ed,
388 | 0x1f9ee,
389 | 0x1f9ef,
390 |
391 | 0x1f9f0,
392 | 0x1f9f1,
393 | 0x1f9f2,
394 | 0x1f9f3,
395 | 0x1f9f4,
396 | 0x1f9f5,
397 | 0x1f9f6,
398 | 0x1f9f7,
399 | 0x1f9f8,
400 | 0x1f9f9,
401 | 0x1f9fa,
402 | 0x1f9fb,
403 | 0x1f9fc,
404 | 0x1f9fd,
405 | 0x1f9fe,
406 | 0x1f9ff
407 | ]
408 | }
409 | }
410 |
411 | /// The code points of the characters not considered to be emoji in the current block, as an array of `UInt32` values.
412 | ///
413 | /// The return values in this computed property are array literals for a few reasons:
414 | /// 1. The values are constant, and the Unicode® Standard is amended infrequently (roughly once a year).
415 | /// 2. This property is accessed quite often.
416 | /// 3. Flattening an array of ranges would increase complexity – see `flatMap(_:)`.
417 | var nonEmoji: [UInt32] {
418 | switch self {
419 | case .emoticons:
420 | // These blocks don't contain any characters considered not to be emoji.
421 | return []
422 |
423 | case .miscellaneousSymbols:
424 | return [
425 | 0x2605,
426 | 0x2606,
427 | 0x2607,
428 | 0x2608,
429 | 0x2609,
430 | 0x260a,
431 | 0x260b,
432 | 0x260c,
433 | 0x260d,
434 | 0x260f,
435 | 0x2610,
436 | 0x2612,
437 | 0x2613,
438 | 0x2616,
439 | 0x2617,
440 | 0x2619,
441 | 0x261a,
442 | 0x261b,
443 | 0x261c,
444 | 0x261e,
445 | 0x261f,
446 | 0x2621,
447 | 0x2624,
448 | 0x2625,
449 | 0x2627,
450 | 0x2628,
451 | 0x2629,
452 | 0x262b,
453 | 0x262c,
454 | 0x262d,
455 | 0x2630,
456 | 0x2631,
457 | 0x2632,
458 | 0x2633,
459 | 0x2634,
460 | 0x2635,
461 | 0x2636,
462 | 0x2637,
463 | 0x263b,
464 | 0x263c,
465 | 0x263d,
466 | 0x263e,
467 | 0x263f,
468 | 0x2641,
469 | 0x2643,
470 | 0x2644,
471 | 0x2645,
472 | 0x2646,
473 | 0x2647,
474 | 0x2654,
475 | 0x2655,
476 | 0x2656,
477 | 0x2657,
478 | 0x2658,
479 | 0x2659,
480 | 0x265a,
481 | 0x265b,
482 | 0x265c,
483 | 0x265d,
484 | 0x265e,
485 | 0x265f,
486 | 0x2661,
487 | 0x2662,
488 | 0x2664,
489 | 0x2667,
490 | 0x2669,
491 | 0x266a,
492 | 0x266b,
493 | 0x266c,
494 | 0x266d,
495 | 0x266e,
496 | 0x266f,
497 | 0x2670,
498 | 0x2671,
499 | 0x2672,
500 | 0x2673,
501 | 0x2674,
502 | 0x2675,
503 | 0x2676,
504 | 0x2677,
505 | 0x2678,
506 | 0x2679,
507 | 0x267a,
508 | 0x267c,
509 | 0x267d,
510 | 0x267e,
511 | 0x2680,
512 | 0x2681,
513 | 0x2682,
514 | 0x2683,
515 | 0x2684,
516 | 0x2685,
517 | 0x2686,
518 | 0x2687,
519 | 0x2688,
520 | 0x2689,
521 | 0x268a,
522 | 0x268b,
523 | 0x268c,
524 | 0x268d,
525 | 0x268e,
526 | 0x268f,
527 | 0x2690,
528 | 0x2691,
529 | 0x2698,
530 | 0x269a,
531 | 0x269d,
532 | 0x269e,
533 | 0x269f,
534 | 0x26a2,
535 | 0x26a3,
536 | 0x26a4,
537 | 0x26a5,
538 | 0x26a6,
539 | 0x26a7,
540 | 0x26a8,
541 | 0x26a9,
542 | 0x26ac,
543 | 0x26ad,
544 | 0x26ae,
545 | 0x26af,
546 | 0x26b2,
547 | 0x26b3,
548 | 0x26b4,
549 | 0x26b5,
550 | 0x26b6,
551 | 0x26b7,
552 | 0x26b8,
553 | 0x26b9,
554 | 0x26ba,
555 | 0x26bb,
556 | 0x26bc,
557 | 0x26bf,
558 | 0x26c0,
559 | 0x26c1,
560 | 0x26c2,
561 | 0x26c3,
562 | 0x26c6,
563 | 0x26c7,
564 | 0x26c9,
565 | 0x26ca,
566 | 0x26cb,
567 | 0x26cc,
568 | 0x26cd,
569 | 0x26d0,
570 | 0x26d2,
571 | 0x26d5,
572 | 0x26d6,
573 | 0x26d7,
574 | 0x26d8,
575 | 0x26d9,
576 | 0x26da,
577 | 0x26db,
578 | 0x26dc,
579 | 0x26dd,
580 | 0x26de,
581 | 0x26df,
582 | 0x26e0,
583 | 0x26e1,
584 | 0x26e2,
585 | 0x26e3,
586 | 0x26e4,
587 | 0x26e5,
588 | 0x26e6,
589 | 0x26e7,
590 | 0x26e8,
591 | 0x26eb,
592 | 0x26ec,
593 | 0x26ed,
594 | 0x26ee,
595 | 0x26ef,
596 | 0x26f6,
597 | 0x26fb,
598 | 0x26fc,
599 | 0x26fe,
600 | 0x26ff
601 | ]
602 | case .dingbats:
603 | return [
604 | 0x2700,
605 | 0x2701,
606 | 0x2703,
607 | 0x2704,
608 | 0x2706,
609 | 0x2707,
610 | 0x270e,
611 | 0x2710,
612 | 0x2711,
613 | 0x2713,
614 | 0x2715,
615 | 0x2717,
616 | 0x2718,
617 | 0x2719,
618 | 0x271a,
619 | 0x271b,
620 | 0x271c,
621 | 0x271e,
622 | 0x271f,
623 | 0x2720,
624 | 0x2722,
625 | 0x2723,
626 | 0x2724,
627 | 0x2725,
628 | 0x2726,
629 | 0x2727,
630 | 0x2729,
631 | 0x272a,
632 | 0x272b,
633 | 0x272c,
634 | 0x272d,
635 | 0x272e,
636 | 0x272f,
637 | 0x2730,
638 | 0x2731,
639 | 0x2732,
640 | 0x2735,
641 | 0x2736,
642 | 0x2737,
643 | 0x2738,
644 | 0x2739,
645 | 0x273a,
646 | 0x273b,
647 | 0x273c,
648 | 0x273d,
649 | 0x273e,
650 | 0x273f,
651 | 0x2740,
652 | 0x2741,
653 | 0x2742,
654 | 0x2743,
655 | 0x2745,
656 | 0x2746,
657 | 0x2748,
658 | 0x2749,
659 | 0x274a,
660 | 0x274b,
661 | 0x274d,
662 | 0x274f,
663 | 0x2750,
664 | 0x2751,
665 | 0x2752,
666 | 0x2756,
667 | 0x2758,
668 | 0x2759,
669 | 0x275a,
670 | 0x275b,
671 | 0x275c,
672 | 0x275d,
673 | 0x275e,
674 | 0x275f,
675 | 0x2760,
676 | 0x2761,
677 | 0x2762,
678 | 0x2765,
679 | 0x2766,
680 | 0x2767,
681 | 0x2768,
682 | 0x2769,
683 | 0x276a,
684 | 0x276b,
685 | 0x276c,
686 | 0x276d,
687 | 0x276e,
688 | 0x276f,
689 | 0x2770,
690 | 0x2771,
691 | 0x2772,
692 | 0x2773,
693 | 0x2774,
694 | 0x2775,
695 | 0x2776,
696 | 0x2777,
697 | 0x2778,
698 | 0x2779,
699 | 0x277a,
700 | 0x277b,
701 | 0x277c,
702 | 0x277d,
703 | 0x277e,
704 | 0x277f,
705 | 0x2780,
706 | 0x2781,
707 | 0x2782,
708 | 0x2783,
709 | 0x2784,
710 | 0x2785,
711 | 0x2786,
712 | 0x2787,
713 | 0x2788,
714 | 0x2789,
715 | 0x278a,
716 | 0x278b,
717 | 0x278c,
718 | 0x278d,
719 | 0x278e,
720 | 0x278f,
721 | 0x2790,
722 | 0x2791,
723 | 0x2792,
724 | 0x2793,
725 | 0x2794,
726 | 0x2798,
727 | 0x2799,
728 | 0x279a,
729 | 0x279b,
730 | 0x279c,
731 | 0x279d,
732 | 0x279e,
733 | 0x279f,
734 | 0x27a0,
735 | 0x27a2,
736 | 0x27a3,
737 | 0x27a4,
738 | 0x27a5,
739 | 0x27a6,
740 | 0x27a7,
741 | 0x27a8,
742 | 0x27a9,
743 | 0x27aa,
744 | 0x27ab,
745 | 0x27ac,
746 | 0x27ad,
747 | 0x27ae,
748 | 0x27af,
749 | 0x27b1,
750 | 0x27b2,
751 | 0x27b3,
752 | 0x27b4,
753 | 0x27b5,
754 | 0x27b6,
755 | 0x27b7,
756 | 0x27b8,
757 | 0x27b9,
758 | 0x27ba,
759 | 0x27bb,
760 | 0x27bc,
761 | 0x27bd,
762 | 0x27be
763 | ]
764 | case .miscellaneousSymbolsAndPictographs:
765 | return [
766 | 0x1f322,
767 | 0x1f323,
768 | 0x1f394,
769 | 0x1f395,
770 | 0x1f398,
771 | 0x1f39c,
772 | 0x1f39d,
773 | 0x1f3f1,
774 | 0x1f3f2,
775 | 0x1f3f6,
776 | 0x1f4fe,
777 | 0x1f53e,
778 | 0x1f53f,
779 | 0x1f540,
780 | 0x1f541,
781 | 0x1f542,
782 | 0x1f543,
783 | 0x1f544,
784 | 0x1f545,
785 | 0x1f546,
786 | 0x1f547,
787 | 0x1f548,
788 | 0x1f54f,
789 | 0x1f568,
790 | 0x1f569,
791 | 0x1f56a,
792 | 0x1f56b,
793 | 0x1f56c,
794 | 0x1f56d,
795 | 0x1f56e,
796 | 0x1f571,
797 | 0x1f572,
798 | 0x1f57b,
799 | 0x1f57c,
800 | 0x1f57d,
801 | 0x1f57e,
802 | 0x1f57f,
803 | 0x1f580,
804 | 0x1f581,
805 | 0x1f582,
806 | 0x1f583,
807 | 0x1f584,
808 | 0x1f585,
809 | 0x1f586,
810 | 0x1f588,
811 | 0x1f589,
812 | 0x1f58e,
813 | 0x1f58f,
814 | 0x1f591,
815 | 0x1f592,
816 | 0x1f593,
817 | 0x1f594,
818 | 0x1f597,
819 | 0x1f598,
820 | 0x1f599,
821 | 0x1f59a,
822 | 0x1f59b,
823 | 0x1f59c,
824 | 0x1f59d,
825 | 0x1f59e,
826 | 0x1f59f,
827 | 0x1f5a0,
828 | 0x1f5a1,
829 | 0x1f5a2,
830 | 0x1f5a3,
831 | 0x1f5a6,
832 | 0x1f5a7,
833 | 0x1f5a9,
834 | 0x1f5aa,
835 | 0x1f5ab,
836 | 0x1f5ac,
837 | 0x1f5ad,
838 | 0x1f5ae,
839 | 0x1f5af,
840 | 0x1f5b0,
841 | 0x1f5b3,
842 | 0x1f5b4,
843 | 0x1f5b5,
844 | 0x1f5b6,
845 | 0x1f5b7,
846 | 0x1f5b8,
847 | 0x1f5b9,
848 | 0x1f5ba,
849 | 0x1f5bb,
850 | 0x1f5bd,
851 | 0x1f5be,
852 | 0x1f5bf,
853 | 0x1f5c0,
854 | 0x1f5c1,
855 | 0x1f5c5,
856 | 0x1f5c6,
857 | 0x1f5c7,
858 | 0x1f5c8,
859 | 0x1f5c9,
860 | 0x1f5ca,
861 | 0x1f5cb,
862 | 0x1f5cc,
863 | 0x1f5cd,
864 | 0x1f5ce,
865 | 0x1f5cf,
866 | 0x1f5d0,
867 | 0x1f5d4,
868 | 0x1f5d5,
869 | 0x1f5d6,
870 | 0x1f5d7,
871 | 0x1f5d8,
872 | 0x1f5d9,
873 | 0x1f5da,
874 | 0x1f5db,
875 | 0x1f5df,
876 | 0x1f5e0,
877 | 0x1f5e2,
878 | 0x1f5e4,
879 | 0x1f5e5,
880 | 0x1f5e6,
881 | 0x1f5e7,
882 | 0x1f5e9,
883 | 0x1f5ea,
884 | 0x1f5eb,
885 | 0x1f5ec,
886 | 0x1f5ed,
887 | 0x1f5ee,
888 | 0x1f5f0,
889 | 0x1f5f1,
890 | 0x1f5f2,
891 | 0x1f5f4,
892 | 0x1f5f5,
893 | 0x1f5f6,
894 | 0x1f5f7,
895 | 0x1f5f8,
896 | 0x1f5f9
897 | ]
898 | case .transportAndMapSymbols:
899 | return [
900 | 0x1f6c6,
901 | 0x1f6c7,
902 | 0x1f6c8,
903 | 0x1f6c9,
904 | 0x1f6ca,
905 |
906 | 0x1f6d3,
907 | 0x1f6d4,
908 |
909 | 0x1f6e6,
910 | 0x1f6e7,
911 | 0x1f6e8,
912 |
913 | 0x1f6ea,
914 |
915 | 0x1f6f1,
916 | 0x1f6f2
917 | ]
918 | case .supplementalSymbolsAndPictographs:
919 | return [
920 | 0x1f900,
921 | 0x1f901,
922 | 0x1f902,
923 | 0x1f903,
924 | 0x1f904,
925 | 0x1f905,
926 | 0x1f906,
927 | 0x1f907,
928 | 0x1f908,
929 | 0x1f909,
930 | 0x1f90a,
931 | 0x1f90b,
932 |
933 | 0x1f93b,
934 |
935 | 0x1f946
936 | ]
937 | }
938 | }
939 | }
940 |
941 |
942 | //
943 | // INFORMATION REGARDING THIS FILE
944 | //
945 | // - Version of the Unicode® Standard: 10.0
946 | // - Date checked: Nov 13, 2017
947 | //
948 | // As of the above version there are 1144 complete base characters (singletons) that can be
949 | // represented as emoji, and an additional 38 incomplete singletons, totaling 1182 code points.
950 | //
951 | // There are six blocks that are mainly used for emoji, so the ranges of these blocks are used as a
952 | // first-level validation to check whether a character is emoji or not. These blocks and the ranges
953 | // of their code points are:
954 | //
955 | // # Name Range Count / Block Added
956 | // 1. Miscellaneous Symbols U+ 2600...U+ 26ff 80 / 256 -
957 | // 2. Dingbats U+ 2700...U+ 27bf 33 / 192 -
958 | // 3. Miscellaneous Symbols And Pictographs U+1f300...U+1f5ff 637 / 768 -
959 | // 4. Emoticons U+1f600...U+1f64f 80 / 80 -
960 | // 5. Transport And Map Symbols U+1f680...U+1f6ff 94 / 128 2
961 | // 6. Supplemental Symbols And Pictographs U+1f900...U+1f9ff 134 / 256 54
962 | // ---- ----
963 | // 1058 56
964 | //
965 | //
966 | // The above blocks contain 1058 emoji in total, which leaves 124 emoji outside of these ranges.
967 | //
968 | // Some of these blocks contain unassigned characters or characters not considered to be emoji. The
969 | // values of these code points are listed in the appendices [1] and [2] below. The Emoticons block
970 | // is currently the only one fully consisting of emoji.
971 | //
972 | // Of the 124 emoji characters that are not found in the above blocks, 38 are incomplete singletons
973 | // (26 regional indicator symbols and 12 keycap base characters). The remaining 86 characters are
974 | // found in various other blocks. All of these 124 additional characters are listed in appendix [3]
975 | // below.
976 | //
977 | //
978 | //
979 | // [1] - Unassigned characters.
980 | //
981 | // 1. Miscellaneous Symbols - 0 unassigned characters.
982 | //
983 | // 2. Dingbats - 0 unassigned characters.
984 | //
985 | // 3. Miscellaneous Symbols And Pictographs - 0 unassigned characters.
986 | //
987 | // 4. Emoticons - 0 unassigned characters.
988 | //
989 | // 5. Transport And Map Symbols - 21 unassigned characters.
990 | // 0x1f6d5...0x1f6df,
991 | // 0x1f6ed...0x1f6ef,
992 | // 0x1f6f9...0x1f6ff
993 | //
994 | // 6. Supplemental Symbols And Pictographs - 108 unassigned characters.
995 | // 0x1f90c...0x1f90f,
996 | // 0x1f93f,
997 | // 0x1f94d...0x1f94f,
998 | // 0x1f96c...0x1f97f,
999 | // 0x1f998...0x1f9bf,
1000 | // 0x1f9c1...0x1f9cf
1001 | // 0x1f9e7...0x1f9ff
1002 | //
1003 | //
1004 | //
1005 | // [2] - Non-emoji characters.
1006 | //
1007 | // 1. Miscellaneous Symbols - 176 non-emoji characters.
1008 | // 0x2605...0x260d,
1009 | // 0x260f...0x2610,
1010 | // 0x2612...0x2613,
1011 | // 0x2616...0x2617,
1012 | // 0x2619...0x261c,
1013 | // 0x261e...0x261f,
1014 | // 0x2621,
1015 | // 0x2624...0x2625,
1016 | // 0x2627...0x2629,
1017 | // 0x262b...0x262d,
1018 | // 0x2630...0x2637,
1019 | // 0x263b...0x263f,
1020 | // 0x2641,
1021 | // 0x2643...0x2647,
1022 | // 0x2654...0x265f,
1023 | // 0x2661...0x2662,
1024 | // 0x2664,
1025 | // 0x2667,
1026 | // 0x2669...0x267a,
1027 | // 0x267c...0x267e,
1028 | // 0x2680...0x2691,
1029 | // 0x2698,
1030 | // 0x269a,
1031 | // 0x269d...0x269f,
1032 | // 0x26a2...0x26a9,
1033 | // 0x26ac...0x26af,
1034 | // 0x26b2...0x26bc,
1035 | // 0x26bf...0x26c3,
1036 | // 0x26c6...0x26c7,
1037 | // 0x26c9...0x26cd,
1038 | // 0x26d0,
1039 | // 0x26d2,
1040 | // 0x26d5...0x26e8,
1041 | // 0x26eb...0x26ef,
1042 | // 0x26f6,
1043 | // 0x26fb...0x26fc,
1044 | // 0x26fe...0x26ff
1045 | //
1046 | // 2. Dingbats - 159 non-emoji characters.
1047 | // 0x2700...0x2701,
1048 | // 0x2703...0x2704,
1049 | // 0x2706...0x2707,
1050 | // 0x270e,
1051 | // 0x2710...0x2711,
1052 | // 0x2713,
1053 | // 0x2715,
1054 | // 0x2717...0x271c,
1055 | // 0x271e...0x2720,
1056 | // 0x2722...0x2727,
1057 | // 0x2729...0x2732,
1058 | // 0x2735...0x2743,
1059 | // 0x2745...0x2746,
1060 | // 0x2748...0x274b,
1061 | // 0x274d,
1062 | // 0x274f...0x2752,
1063 | // 0x2756,
1064 | // 0x2758...0x2762,
1065 | // 0x2765...0x2794,
1066 | // 0x2798...0x27a0,
1067 | // 0x27a2...0x27af,
1068 | // 0x27b1...0x27be
1069 | //
1070 | // 3. Miscellaneous Symbols And Pictographs - 131 non-emoji characters.
1071 | // 0x1f322...0x1f323,
1072 | // 0x1f394...0x1f395,
1073 | // 0x1f398,
1074 | // 0x1f39c...0x1f39d,
1075 | // 0x1f3f1...0x1f3f2,
1076 | // 0x1f3f6,
1077 | // 0x1f4fe,
1078 | // 0x1f53e...0x1f548,
1079 | // 0x1f54f,
1080 | // 0x1f568...0x1f56e,
1081 | // 0x1f571...0x1f572,
1082 | // 0x1f57b...0x1f586,
1083 | // 0x1f588...0x1f589,
1084 | // 0x1f58e...0x1f58f,
1085 | // 0x1f591...0x1f594,
1086 | // 0x1f597...0x1f5a3,
1087 | // 0x1f5a6...0x1f5a7,
1088 | // 0x1f5a9...0x1f5b0,
1089 | // 0x1f5b3...0x1f5bb,
1090 | // 0x1f5bd...0x1f5c1,
1091 | // 0x1f5c5...0x1f5d0,
1092 | // 0x1f5d4...0x1f5db,
1093 | // 0x1f5df...0x1f5e0,
1094 | // 0x1f5e2,
1095 | // 0x1f5e4...0x1f5e7,
1096 | // 0x1f5e9...0x1f5ee,
1097 | // 0x1f5f0...0x1f5f2,
1098 | // 0x1f5f4...0x1f5f9
1099 | //
1100 | // 4. Emoticons - 0 non-emoji characters.
1101 | //
1102 | // 5. Transport And Map Symbols - 13 non-emoji characters.
1103 | // 0x1f6c6...0x1f6ca,
1104 | // 0x1f6d3...0x1f6d4,
1105 | // 0x1f6e6...0x1f6e8,
1106 | // 0x1f6ea,
1107 | // 0x1f6f1...0x1f6f2
1108 | //
1109 | // 6. Supplemental Symbols And Pictographs - 14 non-emoji characters.
1110 | // 0x1f93b,
1111 | // 0x1f946
1112 | //
1113 | //
1114 | //
1115 | // [3] - Additional characters.
1116 | //
1117 | // a. Latin 1 Supplement - 2 characters.
1118 | // 0x00a9,
1119 | // 0x00ae
1120 | //
1121 | // b. Basic Latin - 12 characters.
1122 | // 0x0023, (keycap base character)
1123 | // 0x002a, (keycap base character)
1124 | // 0x0030...0x0039 (keycap base characters)
1125 | //
1126 | // c. General Punctuation - 2 characters.
1127 | // 0x203c,
1128 | // 0x2049
1129 | //
1130 | // d. Letterlike Symbols - 2 characters.
1131 | // 0x2122,
1132 | // 0x2139
1133 | //
1134 | // e. Arrows - 8 characters.
1135 | // 0x2194...0x2199,
1136 | // 0x21a9...0x21aa
1137 | //
1138 | // f. Miscellaneous Technical - 18 characters.
1139 | // 0x231a...0x231b,
1140 | // 0x2328,
1141 | // 0x23cf,
1142 | // 0x23e9...0x23f3,
1143 | // 0x23f8...0x23fa
1144 | //
1145 | // g. Enclosed Alphanumerics - 1 characters.
1146 | // 0x24c2
1147 | //
1148 | // h. Geometric Shapes - 8 characters.
1149 | // 0x25aa...0x25ab,
1150 | // 0x25b6,
1151 | // 0x25c0,
1152 | // 0x25fb...0x25fe
1153 | //
1154 | // i. Supplemental Arrows B - 2 characters.
1155 | // 0x2934...0x2935
1156 | //
1157 | // j. Miscellaneous Symbols And Arrows - 7 characters.
1158 | // 0x2b05...0x2b07,
1159 | // 0x2b1b...0x2b1c,
1160 | // 0x2b50,
1161 | // 0x2b55
1162 | //
1163 | // k. CJK Symbols And Punctuation - 2 characters.
1164 | // 0x3030,
1165 | // 0x303d
1166 | //
1167 | // l. Enclosed CJK Letters And Months - 2 characters.
1168 | // 0x3297,
1169 | // 0x3299
1170 | //
1171 | // m. Mahjong Tiles - 1 characters.
1172 | // 0x1f004
1173 | //
1174 | // n. Playing Cards - 1 characters.
1175 | // 0x1f0cf
1176 | //
1177 | // o. Enclosed Alphanumeric Supplement - 41 characters.
1178 | // 0x1f170...0x1f171,
1179 | // 0x1f17e...0x1f17f,
1180 | // 0x1f18e,
1181 | // 0x1f191...0x1f19a,
1182 | // 0x1f1e6...0x1f1ff (regional indicator symbols)
1183 | //
1184 | // p. Enclosed Ideographic Supplement - 15 characters.
1185 | // 0x1f201...0x1f202,
1186 | // 0x1f21a,
1187 | // 0x1f22f,
1188 | // 0x1f232...0x1f23a,
1189 | // 0x1f250...0x1f251
1190 | //
1191 |
--------------------------------------------------------------------------------