├── .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 | ![emojica](https://raw.githubusercontent.com/xoudini/emojica/images/emojica.png) 2 | ===== 3 | 4 | 5 | Emojica – a Swift framework for using custom emoji in strings. 6 | 7 | 8 | ![gif](https://raw.githubusercontent.com/xoudini/emojica/images/demo.gif) 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 | --------------------------------------------------------------------------------