├── Examples ├── Annotated Configuration.json └── Example Configuration.json ├── License.txt ├── README.md ├── XCAssetPacker Tests ├── Complication Rules.json ├── Info.plist ├── Skip Circles Rules.json ├── Watch │ ├── Image Generation.xcodeproj │ │ └── project.pbxproj │ ├── Source │ │ ├── App Icon.sketch │ │ ├── Bed-38.png │ │ ├── Bed-42.png │ │ ├── ColorComplicationIndicator-38.png │ │ ├── ColorComplicationIndicator-42.png │ │ ├── Complication Rules.json │ │ ├── GrayscaleComplicationIndicator-38.png │ │ ├── GrayscaleComplicationIndicator-42.png │ │ ├── Icons.sketch │ │ ├── Moon-38.png │ │ ├── Moon-42.png │ │ ├── ReminderSelected-38.png │ │ ├── ReminderSelected-42.png │ │ ├── ReminderUnselected-38.png │ │ ├── ReminderUnselected-42.png │ │ ├── Sun-38.png │ │ ├── Sun-42.png │ │ ├── Watch App Icon.png │ │ ├── Watch App Notification Icon-38.png │ │ ├── Watch App Notification Icon-42.png │ │ ├── Watch App Rules.json │ │ ├── Watch App Short Look Icon-38.png │ │ └── Watch App Short Look Icon-42.png │ ├── Watch │ │ └── Images │ │ │ ├── CircularSmallCircle │ │ │ ├── CircularSmallCircle0-38.png │ │ │ ├── CircularSmallCircle0-42.png │ │ │ ├── CircularSmallCircle1-38.png │ │ │ ├── CircularSmallCircle1-42.png │ │ │ ├── CircularSmallCircle2-38.png │ │ │ └── CircularSmallCircle2-42.png │ │ │ ├── ComplicationPreview │ │ │ ├── ComplicationPreview0-38.png │ │ │ ├── ComplicationPreview0-42.png │ │ │ ├── ComplicationPreview1-38.png │ │ │ └── ComplicationPreview1-42.png │ │ │ ├── ModularSmallCircle │ │ │ ├── ModularSmallCircle0-38.png │ │ │ ├── ModularSmallCircle0-42.png │ │ │ ├── ModularSmallCircle1-38.png │ │ │ ├── ModularSmallCircle1-42.png │ │ │ ├── ModularSmallCircle2-38.png │ │ │ ├── ModularSmallCircle2-42.png │ │ │ ├── ModularSmallCircle3-38.png │ │ │ ├── ModularSmallCircle3-42.png │ │ │ ├── ModularSmallCircle4-38.png │ │ │ └── ModularSmallCircle4-42.png │ │ │ ├── SleepDuration │ │ │ ├── SleepDuration0-38.png │ │ │ ├── SleepDuration0-42.png │ │ │ ├── SleepDuration1-38.png │ │ │ ├── SleepDuration1-42.png │ │ │ ├── SleepDuration2-38.png │ │ │ ├── SleepDuration2-42.png │ │ │ ├── SleepDuration3-38.png │ │ │ ├── SleepDuration3-42.png │ │ │ ├── SleepDuration4-38.png │ │ │ └── SleepDuration4-42.png │ │ │ ├── UtilitarianExtraLargeCircle │ │ │ ├── UtilitarianExtraLargeCircle0-38.png │ │ │ └── UtilitarianExtraLargeCircle0-42.png │ │ │ └── WakeTime │ │ │ ├── WakeTime0.png │ │ │ ├── WakeTime1.png │ │ │ ├── WakeTime10.png │ │ │ ├── WakeTime11.png │ │ │ ├── WakeTime12.png │ │ │ ├── WakeTime13.png │ │ │ ├── WakeTime14.png │ │ │ ├── WakeTime15.png │ │ │ ├── WakeTime16.png │ │ │ ├── WakeTime17.png │ │ │ ├── WakeTime18.png │ │ │ ├── WakeTime19.png │ │ │ ├── WakeTime2.png │ │ │ ├── WakeTime20.png │ │ │ ├── WakeTime21.png │ │ │ ├── WakeTime22.png │ │ │ ├── WakeTime23.png │ │ │ ├── WakeTime24.png │ │ │ ├── WakeTime25.png │ │ │ ├── WakeTime26.png │ │ │ ├── WakeTime27.png │ │ │ ├── WakeTime28.png │ │ │ ├── WakeTime29.png │ │ │ ├── WakeTime3.png │ │ │ ├── WakeTime30.png │ │ │ ├── WakeTime31.png │ │ │ ├── WakeTime32.png │ │ │ ├── WakeTime33.png │ │ │ ├── WakeTime34.png │ │ │ ├── WakeTime35.png │ │ │ ├── WakeTime36.png │ │ │ ├── WakeTime37.png │ │ │ ├── WakeTime38.png │ │ │ ├── WakeTime39.png │ │ │ ├── WakeTime4.png │ │ │ ├── WakeTime40.png │ │ │ ├── WakeTime41.png │ │ │ ├── WakeTime42.png │ │ │ ├── WakeTime43.png │ │ │ ├── WakeTime44.png │ │ │ ├── WakeTime45.png │ │ │ ├── WakeTime46.png │ │ │ ├── WakeTime47.png │ │ │ ├── WakeTime48.png │ │ │ ├── WakeTime49.png │ │ │ ├── WakeTime5.png │ │ │ ├── WakeTime50.png │ │ │ ├── WakeTime51.png │ │ │ ├── WakeTime52.png │ │ │ ├── WakeTime6.png │ │ │ ├── WakeTime7.png │ │ │ ├── WakeTime8.png │ │ │ └── WakeTime9.png │ └── generate.sh └── XCAssetPacker_Tests.swift ├── XCAssetPacker.xcodeproj └── project.pbxproj └── XCAssetPacker ├── AssetCatalogGenerator+Creation.swift ├── AssetCatalogGenerator.swift ├── CommandLine ├── .gitignore ├── .travis.yml ├── CommandLineKit.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── CommandLine.xcscheme ├── CommandLineKit │ ├── CommandLine.swift │ ├── Info.plist │ ├── Option.swift │ └── StringExtensions.swift ├── LICENSE ├── Package.swift ├── README.md ├── Tests │ ├── CommandLineKitTests │ │ ├── CommandLineTests.swift │ │ ├── Info.plist │ │ └── StringExtensionTests.swift │ └── LinuxMain.swift └── install-linux-swift.sh ├── Constants.swift ├── DeviceIdiom.swift ├── Extensions.swift ├── ImageProperties.swift ├── SwiftGeneration.swift └── main.swift /Examples/Annotated Configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | /* Skip images */ 3 | "skip-images" : { 4 | "patterns" : ["Watch App .*", "Bed.*"] 5 | }, 6 | 7 | 8 | /* Set which images to include. Defaults to ["png"] */ 9 | "valid-image-extensions" : ["png", "jpeg", "jpg", "tiff"], 10 | 11 | // Set the base rendering options 12 | "base" : { 13 | "template-rendering-intent" : "template" 14 | }, 15 | 16 | /* Apply properties based on the target device */ 17 | "devices" : [ 18 | { 19 | "device-type" : "watch", 20 | "properties" : { 21 | "template-rendering-intent" : "template" 22 | } 23 | } 24 | ], 25 | 26 | /* By default images following the AppIcon-{size}.png naming convention 27 | will be treated as prerendered app icons and won't be exposed to Swift */ 28 | "app-icon" : { 29 | "pattern" : "Custom App Icon Name.*", 30 | "prerendered" : false /* Defaults to true */ 31 | }, 32 | 33 | /* Apply custom sets of properties to images matched by the given patterns */ 34 | "custom" : [ 35 | { 36 | "patterns" : [".*Preview.*", "SleepDuration.*", "WakeTime.*"], 37 | "properties" : { 38 | "template-rendering-intent" : "original" 39 | } 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Examples/Example Configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "skip-images" : { 3 | "patterns" : ["Watch App .*", "Bed.*"] 4 | }, 5 | 6 | "valid-image-extensions" : ["png", "jpeg", "jpg", "tiff"], 7 | 8 | "base" : { 9 | "template-rendering-intent" : "template" 10 | }, 11 | 12 | "devices" : [ 13 | { 14 | "device-type" : "watch", 15 | "properties" : { 16 | "template-rendering-intent" : "template" 17 | } 18 | } 19 | ], 20 | 21 | "app-icon" : { 22 | "pattern" : "Custom App Icon Name.*", 23 | "prerendered" : false 24 | }, 25 | 26 | "custom" : [ 27 | { 28 | "patterns" : [".*Preview.*", "SleepDuration.*", "WakeTime.*"], 29 | "properties" : { 30 | "template-rendering-intent" : "original" 31 | } 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | # XCAssetPacker 2 | Copyright (c) 2016 Inquisitive Software. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | 17 | # CommandLine.swift 18 | Copyright (c) 2014 Ben Gollmer. 19 | 20 | Licensed under the Apache License, Version 2.0 (the "License"); 21 | you may not use this file except in compliance with the License. 22 | You may obtain a copy of the License at 23 | 24 | http://www.apache.org/licenses/LICENSE-2.0 25 | 26 | Unless required by applicable law or agreed to in writing, software 27 | distributed under the License is distributed on an "AS IS" BASIS, 28 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 | See the License for the specific language governing permissions and 30 | limitations under the License. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XCAssetPacker 2 | XCAssetPacker is a command line tool to create an Xcode `.xcasset` package from a folder of images. 3 | 4 | ## Installation 5 | The easiest way to get up and running is to use homebrew: `brew install inquisitiveSoft/tools/XCAssetPacker` 6 | 7 | Alternatively, it's very easy to download and build from source using Xcode. All dependencies are included directly in the project. 8 | 9 | ## Usage 10 | I recommend calling `xcassetpacker` with the `--force` flag and treating the .xcassets and Swift output file as purely machine-generated. 11 | 12 | ``` 13 | xcassetpacker 14 | --input "Images/Source" 15 | --output "Resources/App.xcassets" 16 | --config "Images/Source/Main App Images Configuration.json" 17 | --swift "Code/Images.swift" 18 | --force 19 | ``` 20 | 21 | ### Command Line Options 22 | `-i`, `--input`: Path to the input folder. 23 | `-c`, `--config`: The location of a json configuration file. If none is specified then uses sensible defaults. 24 | `-o`, `--output`: Path to the output file or folder. If a folder is given then an Assets.xcassets package will be created inside it. 25 | `--swift`: Path to the output swift file or folder. If a folder is given then an Images.swift package will be created inside it. 26 | `-f`, `--force`: Overwrite any existing .xcassets package or Swift file. 27 | `-h`, `--help`: Prints a help message. 28 | 29 | You can set the target for generated Swift using: `--iOS`, `--mac` or `--watch`. By default it will generate iOS code. 30 | 31 | ## Image Naming Conventions 32 | There are a number of image naming conventions which will be auto-detected. 33 | 34 | - Scale is determined using a trailing `@1x`, `@2x` or `@3x`. 35 | 36 | - Image type will also be detected using the following suffixes: 37 | 38 | | Suffix | Detected Image Type | 39 | | ------ | ---------- | 40 | | -38 | Image targeting the smaller Apple Watch | 41 | | -42 | Image targeting the larger Apple Watch | 42 | | -20 | Notification icon | 43 | | -29 | Settings icon | 44 | | -40 | Spotlight icon | 45 | | -60 | iPhone App icon | 46 | | -76 | iPad App icon | 47 | | -83.5 | iPad Pro App icon | 48 | 49 | - In addition `AppIcon` is recognized as a standard icon name. App icons are stored slightly differently internally and will be marked as `prerendered` by default. For example an image named `AppIcon-40@3x.png` would be detected as the spotlight icon for the Plus sized iPhone. 50 | 51 | ### Configuration.json 52 | XCAssetPacker has sensible defaults, so you can get started straight away, but you can also configure it using a JSON file. The most common uses are for filtering which images are included, and to set image properties such as target device or rendering intent of images. 53 | 54 | --- 55 | 56 | Skip images that match the given regex `patterns`: 57 | 58 | ``` 59 | "skip-images" : { 60 | "patterns" : ["Watch App .*", "Bed.*"] 61 | } 62 | ``` 63 | 64 | --- 65 | Determine which images to include. Defaults to `png` only: 66 | 67 | ``` 68 | "valid-image-extensions" : ["png", "jpeg", "jpg", "tiff"] 69 | ``` 70 | 71 | --- 72 | Apply a dictionary of properties to every image: 73 | 74 | ``` 75 | "base" : { 76 | "template-rendering-intent" : "template" 77 | } 78 | ``` 79 | 80 | --- 81 | Apply properties based on the target device: 82 | 83 | ``` 84 | "devices" : [ 85 | { 86 | "device-type" : "watch", 87 | "properties" : { 88 | "template-rendering-intent" : "template" 89 | } 90 | } 91 | ] 92 | ``` 93 | 94 | --- 95 | By default, images following the `AppIcon-{size}.png` naming convention 96 | will be treated as prerendered app icons and won't be exposed to Swift, or you can customize the app-icon properties: 97 | ``` 98 | "app-icon" : { 99 | "pattern" : "Custom App Icon Name.*", 100 | "prerendered" : false /* Defaults to true */ 101 | } 102 | ``` 103 | 104 | --- 105 | Apply a dictionary of properties to images matched by the given patterns: 106 | ``` 107 | "custom" : [ 108 | { 109 | "patterns" : [".*Preview.*", "SleepDuration.*", "WakeTime.*"], 110 | "properties" : { 111 | "template-rendering-intent" : "original" 112 | } 113 | } 114 | ] 115 | ``` 116 | 117 | --- 118 | `template-rendering-intent` is a property that iOS uses to determine whether an image has a tintColor applied. Valid properties are `rendering` (tinted) or `original` (not-tinted). Not supplying a value is equivalent to the 'Default' option in Xcode's UI. 119 | 120 | A good starting point might be the [Example Configuration.json](Examples/Example%20Configuration.json). 121 | 122 | ## Credits 123 | Uses Ben Gollmer's nifty [CommandLine framework](https://github.com/jatoben/CommandLine) for parsing CLI input. 124 | 125 | ## License 126 | Open sourced under the Apache Version 2.0 License. 127 | -------------------------------------------------------------------------------- /XCAssetPacker Tests/Complication Rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "base" : { 3 | "template-rendering-intent" : "template" 4 | }, 5 | "devices" : [ 6 | { 7 | "device-type" : "watch", 8 | "properties" : { 9 | "template-rendering-intent" : "template" 10 | } 11 | } 12 | ], 13 | "custom" : [ 14 | { 15 | "patterns" : [".*Preview.*", "SleepDuration.*", "WakeTime.*"], 16 | "properties" : { 17 | "template-rendering-intent" : "original" 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /XCAssetPacker Tests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /XCAssetPacker Tests/Skip Circles Rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "base" : { 3 | "template-rendering-intent" : "template" 4 | }, 5 | "devices" : [ 6 | { 7 | "device-type" : "watch", 8 | "properties" : { 9 | "template-rendering-intent" : "template" 10 | } 11 | } 12 | ], 13 | "custom" : [ 14 | { 15 | "patterns" : [".*Preview.*", "SleepDuration.*", "WakeTime.*"], 16 | "properties" : { 17 | "template-rendering-intent" : "original" 18 | } 19 | } 20 | ], 21 | "skip-images" : { 22 | "patterns" : [".*Circle"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Image Generation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 4A66849B1DEB3E1600BEE075 /* Generate Images */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 4A66849C1DEB3E1600BEE075 /* Build configuration list for PBXAggregateTarget "Generate Images" */; 13 | buildPhases = ( 14 | 4A6684A31DEB3E2000BEE075 /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | 4A6684A01DEB3E1C00BEE075 /* PBXTargetDependency */, 18 | ); 19 | name = "Generate Images"; 20 | productName = "Generate Images"; 21 | }; 22 | /* End PBXAggregateTarget section */ 23 | 24 | /* Begin PBXBuildFile section */ 25 | 4A0656D91DF8FC9E000073E0 /* Blank.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0656D81DF8FC9E000073E0 /* Blank.swift */; }; 26 | 4A7023F21DF4A83C00F04BD0 /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7023EF1DF4A83C00F04BD0 /* CommandLine.swift */; }; 27 | 4A7023F31DF4A83C00F04BD0 /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7023F01DF4A83C00F04BD0 /* Option.swift */; }; 28 | 4A7023F41DF4A83C00F04BD0 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7023F11DF4A83C00F04BD0 /* StringExtensions.swift */; }; 29 | 4AE3F5A41DE6744A003B192E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE3F59A1DE67087003B192E /* main.swift */; }; 30 | 4AE3F5A51DE6744A003B192E /* ImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE3F5991DE67087003B192E /* ImageGenerator.swift */; }; 31 | 4AE3F5A61DE6744A003B192E /* Maths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE3F59B1DE67087003B192E /* Maths.swift */; }; 32 | 4AE3F5A71DE6744A003B192E /* Circle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE3F59C1DE67087003B192E /* Circle.swift */; }; 33 | 4AE3F5BE1DE73FE4003B192E /* SleepDuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE3F5BD1DE73FE4003B192E /* SleepDuration.swift */; }; 34 | 4AE3F5C01DE74A8F003B192E /* WakeTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE3F5BF1DE74A8F003B192E /* WakeTime.swift */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXContainerItemProxy section */ 38 | 4A66849F1DEB3E1C00BEE075 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 4AE3F5211DE60EB4003B192E /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 4AE3F5281DE60EB4003B192E; 43 | remoteInfo = ImageGeneration; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXCopyFilesBuildPhase section */ 48 | 4AE3F5271DE60EB4003B192E /* CopyFiles */ = { 49 | isa = PBXCopyFilesBuildPhase; 50 | buildActionMask = 2147483647; 51 | dstPath = /usr/share/man/man1/; 52 | dstSubfolderSpec = 0; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 1; 56 | }; 57 | /* End PBXCopyFilesBuildPhase section */ 58 | 59 | /* Begin PBXFileReference section */ 60 | 4A0656D81DF8FC9E000073E0 /* Blank.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Blank.swift; path = ImageGeneration/Blank.swift; sourceTree = SOURCE_ROOT; }; 61 | 4A7023EF1DF4A83C00F04BD0 /* CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CommandLine.swift; path = CommandLine/CommandLineKit/CommandLine.swift; sourceTree = SOURCE_ROOT; }; 62 | 4A7023F01DF4A83C00F04BD0 /* Option.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Option.swift; path = CommandLine/CommandLineKit/Option.swift; sourceTree = SOURCE_ROOT; }; 63 | 4A7023F11DF4A83C00F04BD0 /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringExtensions.swift; path = CommandLine/CommandLineKit/StringExtensions.swift; sourceTree = SOURCE_ROOT; }; 64 | 4AE3F5291DE60EB4003B192E /* ImageGeneration */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ImageGeneration; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 4AE3F5991DE67087003B192E /* ImageGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageGenerator.swift; path = ImageGeneration/ImageGenerator.swift; sourceTree = SOURCE_ROOT; }; 66 | 4AE3F59A1DE67087003B192E /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = main.swift; path = ImageGeneration/main.swift; sourceTree = SOURCE_ROOT; }; 67 | 4AE3F59B1DE67087003B192E /* Maths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Maths.swift; path = ImageGeneration/Maths.swift; sourceTree = SOURCE_ROOT; }; 68 | 4AE3F59C1DE67087003B192E /* Circle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Circle.swift; path = ImageGeneration/Circle.swift; sourceTree = SOURCE_ROOT; }; 69 | 4AE3F5BD1DE73FE4003B192E /* SleepDuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SleepDuration.swift; path = ImageGeneration/SleepDuration.swift; sourceTree = SOURCE_ROOT; }; 70 | 4AE3F5BF1DE74A8F003B192E /* WakeTime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WakeTime.swift; path = ImageGeneration/WakeTime.swift; sourceTree = SOURCE_ROOT; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 4AE3F5261DE60EB4003B192E /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXFrameworksBuildPhase section */ 82 | 83 | /* Begin PBXGroup section */ 84 | 4AE3F5201DE60EB4003B192E = { 85 | isa = PBXGroup; 86 | children = ( 87 | 4AE3F52B1DE60EB4003B192E /* ImageGeneration */, 88 | 4AE3F54A1DE66424003B192E /* CommandLIne */, 89 | 4AE3F52A1DE60EB4003B192E /* Products */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 4AE3F52A1DE60EB4003B192E /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 4AE3F5291DE60EB4003B192E /* ImageGeneration */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | 4AE3F52B1DE60EB4003B192E /* ImageGeneration */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 4AE3F59A1DE67087003B192E /* main.swift */, 105 | 4AE3F5991DE67087003B192E /* ImageGenerator.swift */, 106 | 4AE3F59B1DE67087003B192E /* Maths.swift */, 107 | 4AE3F5BC1DE73FB8003B192E /* App Images */, 108 | 4AE3F5B91DE674F1003B192E /* Complications */, 109 | ); 110 | name = ImageGeneration; 111 | path = "Image Generation"; 112 | sourceTree = ""; 113 | }; 114 | 4AE3F54A1DE66424003B192E /* CommandLIne */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 4A7023EF1DF4A83C00F04BD0 /* CommandLine.swift */, 118 | 4A7023F01DF4A83C00F04BD0 /* Option.swift */, 119 | 4A7023F11DF4A83C00F04BD0 /* StringExtensions.swift */, 120 | ); 121 | name = CommandLIne; 122 | path = XCAssetPacker; 123 | sourceTree = ""; 124 | }; 125 | 4AE3F5B91DE674F1003B192E /* Complications */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 4AE3F59C1DE67087003B192E /* Circle.swift */, 129 | 4A0656D81DF8FC9E000073E0 /* Blank.swift */, 130 | ); 131 | name = Complications; 132 | sourceTree = ""; 133 | }; 134 | 4AE3F5BC1DE73FB8003B192E /* App Images */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 4AE3F5BD1DE73FE4003B192E /* SleepDuration.swift */, 138 | 4AE3F5BF1DE74A8F003B192E /* WakeTime.swift */, 139 | ); 140 | name = "App Images"; 141 | sourceTree = ""; 142 | }; 143 | /* End PBXGroup section */ 144 | 145 | /* Begin PBXNativeTarget section */ 146 | 4AE3F5281DE60EB4003B192E /* ImageGeneration */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = 4AE3F5301DE60EB4003B192E /* Build configuration list for PBXNativeTarget "ImageGeneration" */; 149 | buildPhases = ( 150 | 4AE3F5251DE60EB4003B192E /* Sources */, 151 | 4AE3F5261DE60EB4003B192E /* Frameworks */, 152 | 4AE3F5271DE60EB4003B192E /* CopyFiles */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = ImageGeneration; 159 | productName = "Image Generation"; 160 | productReference = 4AE3F5291DE60EB4003B192E /* ImageGeneration */; 161 | productType = "com.apple.product-type.tool"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 4AE3F5211DE60EB4003B192E /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastSwiftUpdateCheck = 0810; 170 | LastUpgradeCheck = 0810; 171 | ORGANIZATIONNAME = "Inquisitive Software"; 172 | TargetAttributes = { 173 | 4A66849B1DEB3E1600BEE075 = { 174 | CreatedOnToolsVersion = 8.1; 175 | DevelopmentTeam = D92MT22EX5; 176 | ProvisioningStyle = Automatic; 177 | }; 178 | 4AE3F5281DE60EB4003B192E = { 179 | CreatedOnToolsVersion = 8.1; 180 | DevelopmentTeam = D92MT22EX5; 181 | LastSwiftMigration = 0810; 182 | ProvisioningStyle = Automatic; 183 | }; 184 | }; 185 | }; 186 | buildConfigurationList = 4AE3F5241DE60EB4003B192E /* Build configuration list for PBXProject "Image Generation" */; 187 | compatibilityVersion = "Xcode 3.2"; 188 | developmentRegion = English; 189 | hasScannedForEncodings = 0; 190 | knownRegions = ( 191 | en, 192 | ); 193 | mainGroup = 4AE3F5201DE60EB4003B192E; 194 | productRefGroup = 4AE3F52A1DE60EB4003B192E /* Products */; 195 | projectDirPath = ""; 196 | projectRoot = ""; 197 | targets = ( 198 | 4AE3F5281DE60EB4003B192E /* ImageGeneration */, 199 | 4A66849B1DEB3E1600BEE075 /* Generate Images */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXShellScriptBuildPhase section */ 205 | 4A6684A31DEB3E2000BEE075 /* ShellScript */ = { 206 | isa = PBXShellScriptBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | ); 210 | inputPaths = ( 211 | ); 212 | outputPaths = ( 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | shellPath = /bin/sh; 216 | shellScript = "\"${PROJECT_DIR}/generate.sh\""; 217 | }; 218 | /* End PBXShellScriptBuildPhase section */ 219 | 220 | /* Begin PBXSourcesBuildPhase section */ 221 | 4AE3F5251DE60EB4003B192E /* Sources */ = { 222 | isa = PBXSourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | 4A7023F31DF4A83C00F04BD0 /* Option.swift in Sources */, 226 | 4AE3F5A61DE6744A003B192E /* Maths.swift in Sources */, 227 | 4AE3F5A51DE6744A003B192E /* ImageGenerator.swift in Sources */, 228 | 4A7023F41DF4A83C00F04BD0 /* StringExtensions.swift in Sources */, 229 | 4AE3F5C01DE74A8F003B192E /* WakeTime.swift in Sources */, 230 | 4AE3F5A71DE6744A003B192E /* Circle.swift in Sources */, 231 | 4AE3F5BE1DE73FE4003B192E /* SleepDuration.swift in Sources */, 232 | 4A7023F21DF4A83C00F04BD0 /* CommandLine.swift in Sources */, 233 | 4A0656D91DF8FC9E000073E0 /* Blank.swift in Sources */, 234 | 4AE3F5A41DE6744A003B192E /* main.swift in Sources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXSourcesBuildPhase section */ 239 | 240 | /* Begin PBXTargetDependency section */ 241 | 4A6684A01DEB3E1C00BEE075 /* PBXTargetDependency */ = { 242 | isa = PBXTargetDependency; 243 | target = 4AE3F5281DE60EB4003B192E /* ImageGeneration */; 244 | targetProxy = 4A66849F1DEB3E1C00BEE075 /* PBXContainerItemProxy */; 245 | }; 246 | /* End PBXTargetDependency section */ 247 | 248 | /* Begin XCBuildConfiguration section */ 249 | 4A66849D1DEB3E1600BEE075 /* Debug */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | DEVELOPMENT_TEAM = D92MT22EX5; 253 | PRODUCT_NAME = "$(TARGET_NAME)"; 254 | }; 255 | name = Debug; 256 | }; 257 | 4A66849E1DEB3E1600BEE075 /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | DEVELOPMENT_TEAM = D92MT22EX5; 261 | PRODUCT_NAME = "$(TARGET_NAME)"; 262 | }; 263 | name = Release; 264 | }; 265 | 4AE3F52E1DE60EB4003B192E /* Debug */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_CONSTANT_CONVERSION = YES; 276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 277 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 278 | CLANG_WARN_EMPTY_BODY = YES; 279 | CLANG_WARN_ENUM_CONVERSION = YES; 280 | CLANG_WARN_INFINITE_RECURSION = YES; 281 | CLANG_WARN_INT_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | CODE_SIGN_IDENTITY = "-"; 287 | CONFIGURATION_BUILD_DIR = $PROJECT_DIR/build; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = dwarf; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | ENABLE_TESTABILITY = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_OPTIMIZATION_LEVEL = 0; 296 | GCC_PREPROCESSOR_DEFINITIONS = ( 297 | "DEBUG=1", 298 | "$(inherited)", 299 | ); 300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 302 | GCC_WARN_UNDECLARED_SELECTOR = YES; 303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 304 | GCC_WARN_UNUSED_FUNCTION = YES; 305 | GCC_WARN_UNUSED_VARIABLE = YES; 306 | MACOSX_DEPLOYMENT_TARGET = 10.11; 307 | MTL_ENABLE_DEBUG_INFO = YES; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = macosx; 310 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 311 | SWIFT_VERSION = 3.0.1; 312 | }; 313 | name = Debug; 314 | }; 315 | 4AE3F52F1DE60EB4003B192E /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 328 | CLANG_WARN_EMPTY_BODY = YES; 329 | CLANG_WARN_ENUM_CONVERSION = YES; 330 | CLANG_WARN_INFINITE_RECURSION = YES; 331 | CLANG_WARN_INT_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | CODE_SIGN_IDENTITY = "-"; 337 | CONFIGURATION_BUILD_DIR = $PROJECT_DIR/build; 338 | COPY_PHASE_STRIP = NO; 339 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 340 | ENABLE_NS_ASSERTIONS = NO; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | MACOSX_DEPLOYMENT_TARGET = 10.11; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = macosx; 353 | SWIFT_VERSION = 3.0.1; 354 | }; 355 | name = Release; 356 | }; 357 | 4AE3F5311DE60EB4003B192E /* Debug */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | CLANG_ENABLE_MODULES = YES; 361 | DEVELOPMENT_TEAM = D92MT22EX5; 362 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 365 | SWIFT_VERSION = 3.0.1; 366 | }; 367 | name = Debug; 368 | }; 369 | 4AE3F5321DE60EB4003B192E /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | CLANG_ENABLE_MODULES = YES; 373 | DEVELOPMENT_TEAM = D92MT22EX5; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SWIFT_VERSION = 3.0.1; 377 | }; 378 | name = Release; 379 | }; 380 | /* End XCBuildConfiguration section */ 381 | 382 | /* Begin XCConfigurationList section */ 383 | 4A66849C1DEB3E1600BEE075 /* Build configuration list for PBXAggregateTarget "Generate Images" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 4A66849D1DEB3E1600BEE075 /* Debug */, 387 | 4A66849E1DEB3E1600BEE075 /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | 4AE3F5241DE60EB4003B192E /* Build configuration list for PBXProject "Image Generation" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | 4AE3F52E1DE60EB4003B192E /* Debug */, 396 | 4AE3F52F1DE60EB4003B192E /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | 4AE3F5301DE60EB4003B192E /* Build configuration list for PBXNativeTarget "ImageGeneration" */ = { 402 | isa = XCConfigurationList; 403 | buildConfigurations = ( 404 | 4AE3F5311DE60EB4003B192E /* Debug */, 405 | 4AE3F5321DE60EB4003B192E /* Release */, 406 | ); 407 | defaultConfigurationIsVisible = 0; 408 | defaultConfigurationName = Release; 409 | }; 410 | /* End XCConfigurationList section */ 411 | }; 412 | rootObject = 4AE3F5211DE60EB4003B192E /* Project object */; 413 | } 414 | -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/App Icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/App Icon.sketch -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Bed-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Bed-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Bed-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Bed-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Complication Rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "base" : { 3 | "template-rendering-intent" : "template" 4 | }, 5 | "devices" : [ 6 | { 7 | "device-type" : "watch", 8 | "properties" : { 9 | "template-rendering-intent" : "template" 10 | } 11 | } 12 | ], 13 | "custom" : [ 14 | { 15 | "patterns" : [".*Preview.*", "SleepDuration.*", "WakeTime.*"], 16 | "properties" : { 17 | "template-rendering-intent" : "original" 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Icons.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Icons.sketch -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Moon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Moon-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Moon-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Moon-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderSelected-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/ReminderSelected-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderSelected-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/ReminderSelected-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderUnselected-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/ReminderUnselected-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderUnselected-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/ReminderUnselected-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Sun-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Sun-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Sun-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Sun-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Watch App Icon.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Notification Icon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Watch App Notification Icon-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Notification Icon-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Watch App Notification Icon-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "skip-images" : { 3 | "patterns" : ["Watch App .*", "Bed.*"] 4 | }, 5 | "base" : { 6 | "template-rendering-intent" : "original" 7 | } 8 | } -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Short Look Icon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Watch App Short Look Icon-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Short Look Icon-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Source/Watch App Short Look Icon-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime0.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime1.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime10.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime11.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime12.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime13.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime14.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime15.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime16.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime17.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime18.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime19.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime2.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime20.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime21.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime22.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime23.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime24.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime25.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime26.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime27.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime28.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime29.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime3.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime30.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime31.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime32.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime33.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime34.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime35.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime36.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime37.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime39.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime4.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime40.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime41.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime43.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime44.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime45.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime46.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime47.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime48.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime49.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime5.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime50.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime51.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime52.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime6.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime7.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime8.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/05d4864effaeaa576318f44cf57683dfcb607ecb/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime9.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create the .xcassets package for generated complications 4 | XCAssetPacker --input "Generated/Images" --config "../Source/Complication Rules.json" --output "Generated/Complications.xcassets" -f 5 | 6 | # Create the .xcassets package for static images 7 | XCAssetPacker --input "Source" --config "../Source/Watch App Rules.json" --output "Generated/Watch App.xcassets" -f 8 | -------------------------------------------------------------------------------- /XCAssetPacker Tests/XCAssetPacker_Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XCAssetPacker_Tests.swift 3 | // XCAssetPacker Tests 4 | // 5 | // Created by Harry Jordan on 17/04/2017. 6 | // Copyright © 2017 Inquisitive Software. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | 12 | class XCAssetPacker_Tests: XCTestCase { 13 | 14 | 15 | func testWatchNoConstraints() { 16 | validateAssetCatalog(directory: "Watch") { (assetCatalog, log) in 17 | XCTAssertEqual(log.numberOfImages, 85, "Expect to find 85 test images") 18 | } 19 | } 20 | 21 | 22 | func testWatchSkipImagesContainingTheWordCircle() { 23 | validateAssetCatalog(directory: "Watch", configurationFileName: "Skip Circles Rules") { (assetCatalog, log) in 24 | XCTAssertEqual(log.numberOfImages, 67, "Expect to find 67 test images") 25 | } 26 | } 27 | 28 | 29 | func testSwiftGeneration() { 30 | validateAssetCatalog(directory: "Watch", swift: .watch) { (assetCatalog, log) in 31 | XCTAssertEqual(log.numberOfImages, 85, "Expect to find 85 test images") 32 | } 33 | } 34 | 35 | 36 | func validateAssetCatalog(directory: String, configurationFileName: String? = nil, swift swiftTarget: SwiftTarget? = nil, expectations: (AssetCatalogGenerator, AssetCatalogLog) -> Void) { 37 | do { 38 | let bundle = Bundle(for: XCAssetPacker_Tests.self) 39 | let watchURL = bundle.url(forResource: directory, withExtension: nil, subdirectory: nil)! 40 | let destinationURL = watchURL.deletingLastPathComponent() 41 | let swiftDestinationURL: URL? 42 | let target: SwiftTarget 43 | 44 | if let swiftTarget = swiftTarget { 45 | swiftDestinationURL = destinationURL.appendingPathComponent("\(swiftTarget.libraryName).swift") 46 | target = swiftTarget 47 | } else { 48 | swiftDestinationURL = nil 49 | target = .iOS 50 | } 51 | 52 | let configuration: [String: Any] 53 | if let configurationFileName = configurationFileName { 54 | configuration = try self.configuration(forFileName: configurationFileName, inBundle: bundle) 55 | XCTAssert(!configuration.isEmpty, "Couldn't load configuration for name: \(configurationFileName)") 56 | } else { 57 | configuration = [:] 58 | } 59 | 60 | let assetCatalog = try AssetCatalogGenerator(from: watchURL, to: destinationURL, swift: swiftDestinationURL, target: target, overwrite: true, configuration: configuration) 61 | let log = try assetCatalog.applyChanges(logLevel: .detailed, dryRun: true) 62 | 63 | expectations(assetCatalog, log) 64 | } catch let error as AssetCatalogError { 65 | switch error { 66 | case .ioError(let decription): 67 | XCTFail(decription) 68 | } 69 | } catch let error { 70 | XCTFail(String(describing: error)) 71 | } 72 | } 73 | 74 | 75 | func configuration(forFileName jsonFileName: String, inBundle bundle: Bundle) throws -> [String: Any] { 76 | guard let configurationFileURL = bundle.url(forResource: jsonFileName, withExtension: "json", subdirectory: nil) else { 77 | throw AssetCatalogError.ioError("Ivli") 78 | } 79 | 80 | 81 | let data = try Data(contentsOf: configurationFileURL) 82 | let json = try JSONSerialization.jsonObject(with: data, options: []) 83 | 84 | if let configurationJSON = json as? [String: Any] { 85 | return configurationJSON 86 | } else { 87 | throw AssetCatalogError.ioError("Invalid json") 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /XCAssetPacker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A07727C1DF9634A0068A929 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07727B1DF9634A0068A929 /* Extensions.swift */; }; 11 | 4A07727E1DF963C70068A929 /* ImageProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07727D1DF963C70068A929 /* ImageProperties.swift */; }; 12 | 4A0772821DF9731B0068A929 /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07727F1DF9731B0068A929 /* CommandLine.swift */; }; 13 | 4A0772831DF9731B0068A929 /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0772801DF9731B0068A929 /* Option.swift */; }; 14 | 4A0772841DF9731B0068A929 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0772811DF9731B0068A929 /* StringExtensions.swift */; }; 15 | 4A56D14B1DE5B573000B40D8 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A56D14A1DE5B573000B40D8 /* main.swift */; }; 16 | 4A56D1591DE5BA85000B40D8 /* AssetCatalogGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A56D1581DE5BA85000B40D8 /* AssetCatalogGenerator.swift */; }; 17 | 4A5F178F1EA3EDE8007C37DE /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5F178E1EA3EDE8007C37DE /* Constants.swift */; }; 18 | 4AD71E741EA4899500016A1A /* SwiftGeneration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD71E731EA4899500016A1A /* SwiftGeneration.swift */; }; 19 | 4AD71E7F1EA4CCBA00016A1A /* XCAssetPacker_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD71E7E1EA4CCBA00016A1A /* XCAssetPacker_Tests.swift */; }; 20 | 4AD71E841EA4CD0800016A1A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5F178E1EA3EDE8007C37DE /* Constants.swift */; }; 21 | 4AD71E851EA4CD0800016A1A /* AssetCatalogGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A56D1581DE5BA85000B40D8 /* AssetCatalogGenerator.swift */; }; 22 | 4AD71E861EA4CD0800016A1A /* ImageProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07727D1DF963C70068A929 /* ImageProperties.swift */; }; 23 | 4AD71E871EA4CD0800016A1A /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07727B1DF9634A0068A929 /* Extensions.swift */; }; 24 | 4AD71E881EA4CD0800016A1A /* SwiftGeneration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD71E731EA4899500016A1A /* SwiftGeneration.swift */; }; 25 | 4AD71E8A1EA4CD0800016A1A /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07727F1DF9731B0068A929 /* CommandLine.swift */; }; 26 | 4AD71E8B1EA4CD0800016A1A /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0772801DF9731B0068A929 /* Option.swift */; }; 27 | 4AD71E8C1EA4CD0800016A1A /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0772811DF9731B0068A929 /* StringExtensions.swift */; }; 28 | 4AD71E8E1EA4CDAE00016A1A /* Watch in Resources */ = {isa = PBXBuildFile; fileRef = 4AD71E8D1EA4CDAE00016A1A /* Watch */; }; 29 | 4AD71E901EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD71E8F1EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift */; }; 30 | 4AD71E911EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD71E8F1EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift */; }; 31 | 4AD71E951EA4DD2700016A1A /* Complication Rules.json in Resources */ = {isa = PBXBuildFile; fileRef = 4AD71E941EA4DD2700016A1A /* Complication Rules.json */; }; 32 | 4AD71E971EA4EEEC00016A1A /* Skip Circles Rules.json in Resources */ = {isa = PBXBuildFile; fileRef = 4AD71E961EA4EEEC00016A1A /* Skip Circles Rules.json */; }; 33 | 4AFFC5911F5D8E35000FDCFD /* DeviceIdiom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AFFC5901F5D8E35000FDCFD /* DeviceIdiom.swift */; }; 34 | 4AFFC5931F5D8E83000FDCFD /* DeviceIdiom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AFFC5921F5D8E83000FDCFD /* DeviceIdiom.swift */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXCopyFilesBuildPhase section */ 38 | 4A56D1451DE5B573000B40D8 /* CopyFiles */ = { 39 | isa = PBXCopyFilesBuildPhase; 40 | buildActionMask = 2147483647; 41 | dstPath = /usr/share/man/man1/; 42 | dstSubfolderSpec = 0; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 1; 46 | }; 47 | /* End PBXCopyFilesBuildPhase section */ 48 | 49 | /* Begin PBXFileReference section */ 50 | 4A07727B1DF9634A0068A929 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 51 | 4A07727D1DF963C70068A929 /* ImageProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageProperties.swift; sourceTree = ""; }; 52 | 4A07727F1DF9731B0068A929 /* CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CommandLine.swift; path = CommandLine/CommandLineKit/CommandLine.swift; sourceTree = ""; }; 53 | 4A0772801DF9731B0068A929 /* Option.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Option.swift; path = CommandLine/CommandLineKit/Option.swift; sourceTree = ""; }; 54 | 4A0772811DF9731B0068A929 /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringExtensions.swift; path = CommandLine/CommandLineKit/StringExtensions.swift; sourceTree = ""; }; 55 | 4A56D1471DE5B573000B40D8 /* xcassetpacker */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xcassetpacker; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 4A56D14A1DE5B573000B40D8 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 57 | 4A56D1581DE5BA85000B40D8 /* AssetCatalogGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetCatalogGenerator.swift; sourceTree = ""; }; 58 | 4A5F178E1EA3EDE8007C37DE /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 59 | 4AD71E731EA4899500016A1A /* SwiftGeneration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftGeneration.swift; sourceTree = ""; }; 60 | 4AD71E7C1EA4CCBA00016A1A /* XCAssetPacker Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "XCAssetPacker Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 4AD71E7E1EA4CCBA00016A1A /* XCAssetPacker_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCAssetPacker_Tests.swift; sourceTree = ""; }; 62 | 4AD71E801EA4CCBA00016A1A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | 4AD71E8D1EA4CDAE00016A1A /* Watch */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Watch; path = Watch/Watch; sourceTree = ""; }; 64 | 4AD71E8F1EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AssetCatalogGenerator+Creation.swift"; sourceTree = ""; }; 65 | 4AD71E941EA4DD2700016A1A /* Complication Rules.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Complication Rules.json"; sourceTree = ""; }; 66 | 4AD71E961EA4EEEC00016A1A /* Skip Circles Rules.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Skip Circles Rules.json"; sourceTree = ""; }; 67 | 4AFFC5901F5D8E35000FDCFD /* DeviceIdiom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceIdiom.swift; sourceTree = ""; }; 68 | 4AFFC5921F5D8E83000FDCFD /* DeviceIdiom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DeviceIdiom.swift; path = XCAssetPacker/DeviceIdiom.swift; sourceTree = ""; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 4A56D1441DE5B573000B40D8 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 4AD71E791EA4CCBA00016A1A /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 4A56D13E1DE5B573000B40D8 = { 90 | isa = PBXGroup; 91 | children = ( 92 | 4AFFC5921F5D8E83000FDCFD /* DeviceIdiom.swift */, 93 | 4A56D1491DE5B573000B40D8 /* XCAssetPacker */, 94 | 4A56D1571DE5B5AB000B40D8 /* CommandLine */, 95 | 4AD71E7D1EA4CCBA00016A1A /* XCAssetPacker Tests */, 96 | 4A56D1481DE5B573000B40D8 /* Products */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | 4A56D1481DE5B573000B40D8 /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 4A56D1471DE5B573000B40D8 /* xcassetpacker */, 104 | 4AD71E7C1EA4CCBA00016A1A /* XCAssetPacker Tests.xctest */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | 4A56D1491DE5B573000B40D8 /* XCAssetPacker */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 4A56D14A1DE5B573000B40D8 /* main.swift */, 113 | 4A5F178E1EA3EDE8007C37DE /* Constants.swift */, 114 | 4AD71E8F1EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift */, 115 | 4A56D1581DE5BA85000B40D8 /* AssetCatalogGenerator.swift */, 116 | 4A07727D1DF963C70068A929 /* ImageProperties.swift */, 117 | 4AFFC5901F5D8E35000FDCFD /* DeviceIdiom.swift */, 118 | 4A07727B1DF9634A0068A929 /* Extensions.swift */, 119 | 4AD71E731EA4899500016A1A /* SwiftGeneration.swift */, 120 | ); 121 | path = XCAssetPacker; 122 | sourceTree = ""; 123 | }; 124 | 4A56D1571DE5B5AB000B40D8 /* CommandLine */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 4A07727F1DF9731B0068A929 /* CommandLine.swift */, 128 | 4A0772801DF9731B0068A929 /* Option.swift */, 129 | 4A0772811DF9731B0068A929 /* StringExtensions.swift */, 130 | ); 131 | name = CommandLine; 132 | path = XCAssetPacker; 133 | sourceTree = ""; 134 | }; 135 | 4AD71E7D1EA4CCBA00016A1A /* XCAssetPacker Tests */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 4AD71E7E1EA4CCBA00016A1A /* XCAssetPacker_Tests.swift */, 139 | 4AD71E941EA4DD2700016A1A /* Complication Rules.json */, 140 | 4AD71E961EA4EEEC00016A1A /* Skip Circles Rules.json */, 141 | 4AD71E8D1EA4CDAE00016A1A /* Watch */, 142 | 4AD71E801EA4CCBA00016A1A /* Info.plist */, 143 | ); 144 | path = "XCAssetPacker Tests"; 145 | sourceTree = ""; 146 | }; 147 | /* End PBXGroup section */ 148 | 149 | /* Begin PBXNativeTarget section */ 150 | 4A56D1461DE5B573000B40D8 /* xcassetpacker */ = { 151 | isa = PBXNativeTarget; 152 | buildConfigurationList = 4A56D14E1DE5B573000B40D8 /* Build configuration list for PBXNativeTarget "xcassetpacker" */; 153 | buildPhases = ( 154 | 4A56D1431DE5B573000B40D8 /* Sources */, 155 | 4A56D1441DE5B573000B40D8 /* Frameworks */, 156 | 4A56D1451DE5B573000B40D8 /* CopyFiles */, 157 | ); 158 | buildRules = ( 159 | ); 160 | dependencies = ( 161 | ); 162 | name = xcassetpacker; 163 | productName = XCAssetPacker; 164 | productReference = 4A56D1471DE5B573000B40D8 /* xcassetpacker */; 165 | productType = "com.apple.product-type.tool"; 166 | }; 167 | 4AD71E7B1EA4CCBA00016A1A /* XCAssetPacker Tests */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = 4AD71E811EA4CCBA00016A1A /* Build configuration list for PBXNativeTarget "XCAssetPacker Tests" */; 170 | buildPhases = ( 171 | 4AD71E781EA4CCBA00016A1A /* Sources */, 172 | 4AD71E791EA4CCBA00016A1A /* Frameworks */, 173 | 4AD71E7A1EA4CCBA00016A1A /* Resources */, 174 | ); 175 | buildRules = ( 176 | ); 177 | dependencies = ( 178 | ); 179 | name = "XCAssetPacker Tests"; 180 | productName = "XCAssetPacker Tests"; 181 | productReference = 4AD71E7C1EA4CCBA00016A1A /* XCAssetPacker Tests.xctest */; 182 | productType = "com.apple.product-type.bundle.unit-test"; 183 | }; 184 | /* End PBXNativeTarget section */ 185 | 186 | /* Begin PBXProject section */ 187 | 4A56D13F1DE5B573000B40D8 /* Project object */ = { 188 | isa = PBXProject; 189 | attributes = { 190 | LastSwiftUpdateCheck = 0830; 191 | LastUpgradeCheck = 0830; 192 | ORGANIZATIONNAME = "Inquisitive Software"; 193 | TargetAttributes = { 194 | 4A56D1461DE5B573000B40D8 = { 195 | CreatedOnToolsVersion = 8.1; 196 | DevelopmentTeam = D92MT22EX5; 197 | LastSwiftMigration = 0900; 198 | ProvisioningStyle = Automatic; 199 | }; 200 | 4AD71E7B1EA4CCBA00016A1A = { 201 | CreatedOnToolsVersion = 8.3.1; 202 | DevelopmentTeam = D92MT22EX5; 203 | LastSwiftMigration = 0900; 204 | ProvisioningStyle = Automatic; 205 | }; 206 | }; 207 | }; 208 | buildConfigurationList = 4A56D1421DE5B573000B40D8 /* Build configuration list for PBXProject "XCAssetPacker" */; 209 | compatibilityVersion = "Xcode 3.2"; 210 | developmentRegion = English; 211 | hasScannedForEncodings = 0; 212 | knownRegions = ( 213 | en, 214 | ); 215 | mainGroup = 4A56D13E1DE5B573000B40D8; 216 | productRefGroup = 4A56D1481DE5B573000B40D8 /* Products */; 217 | projectDirPath = ""; 218 | projectRoot = ""; 219 | targets = ( 220 | 4A56D1461DE5B573000B40D8 /* xcassetpacker */, 221 | 4AD71E7B1EA4CCBA00016A1A /* XCAssetPacker Tests */, 222 | ); 223 | }; 224 | /* End PBXProject section */ 225 | 226 | /* Begin PBXResourcesBuildPhase section */ 227 | 4AD71E7A1EA4CCBA00016A1A /* Resources */ = { 228 | isa = PBXResourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 4AD71E971EA4EEEC00016A1A /* Skip Circles Rules.json in Resources */, 232 | 4AD71E951EA4DD2700016A1A /* Complication Rules.json in Resources */, 233 | 4AD71E8E1EA4CDAE00016A1A /* Watch in Resources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXResourcesBuildPhase section */ 238 | 239 | /* Begin PBXSourcesBuildPhase section */ 240 | 4A56D1431DE5B573000B40D8 /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 4A0772821DF9731B0068A929 /* CommandLine.swift in Sources */, 245 | 4AFFC5911F5D8E35000FDCFD /* DeviceIdiom.swift in Sources */, 246 | 4A5F178F1EA3EDE8007C37DE /* Constants.swift in Sources */, 247 | 4A07727C1DF9634A0068A929 /* Extensions.swift in Sources */, 248 | 4A07727E1DF963C70068A929 /* ImageProperties.swift in Sources */, 249 | 4A0772841DF9731B0068A929 /* StringExtensions.swift in Sources */, 250 | 4A56D1591DE5BA85000B40D8 /* AssetCatalogGenerator.swift in Sources */, 251 | 4A0772831DF9731B0068A929 /* Option.swift in Sources */, 252 | 4A56D14B1DE5B573000B40D8 /* main.swift in Sources */, 253 | 4AD71E741EA4899500016A1A /* SwiftGeneration.swift in Sources */, 254 | 4AD71E901EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | 4AD71E781EA4CCBA00016A1A /* Sources */ = { 259 | isa = PBXSourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | 4AD71E7F1EA4CCBA00016A1A /* XCAssetPacker_Tests.swift in Sources */, 263 | 4AD71E841EA4CD0800016A1A /* Constants.swift in Sources */, 264 | 4AD71E851EA4CD0800016A1A /* AssetCatalogGenerator.swift in Sources */, 265 | 4AD71E861EA4CD0800016A1A /* ImageProperties.swift in Sources */, 266 | 4AD71E871EA4CD0800016A1A /* Extensions.swift in Sources */, 267 | 4AD71E881EA4CD0800016A1A /* SwiftGeneration.swift in Sources */, 268 | 4AD71E8A1EA4CD0800016A1A /* CommandLine.swift in Sources */, 269 | 4AFFC5931F5D8E83000FDCFD /* DeviceIdiom.swift in Sources */, 270 | 4AD71E8B1EA4CD0800016A1A /* Option.swift in Sources */, 271 | 4AD71E8C1EA4CD0800016A1A /* StringExtensions.swift in Sources */, 272 | 4AD71E911EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXSourcesBuildPhase section */ 277 | 278 | /* Begin XCBuildConfiguration section */ 279 | 4A56D14C1DE5B573000B40D8 /* Debug */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ALWAYS_SEARCH_USER_PATHS = NO; 283 | CLANG_ANALYZER_NONNULL = YES; 284 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 285 | CLANG_CXX_LIBRARY = "libc++"; 286 | CLANG_ENABLE_MODULES = YES; 287 | CLANG_ENABLE_OBJC_ARC = YES; 288 | CLANG_WARN_BOOL_CONVERSION = YES; 289 | CLANG_WARN_CONSTANT_CONVERSION = YES; 290 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 291 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 292 | CLANG_WARN_EMPTY_BODY = YES; 293 | CLANG_WARN_ENUM_CONVERSION = YES; 294 | CLANG_WARN_INFINITE_RECURSION = YES; 295 | CLANG_WARN_INT_CONVERSION = YES; 296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 297 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 298 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 299 | CLANG_WARN_UNREACHABLE_CODE = YES; 300 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 301 | CODE_SIGN_IDENTITY = "-"; 302 | COPY_PHASE_STRIP = NO; 303 | DEBUG_INFORMATION_FORMAT = dwarf; 304 | ENABLE_STRICT_OBJC_MSGSEND = YES; 305 | ENABLE_TESTABILITY = YES; 306 | GCC_C_LANGUAGE_STANDARD = gnu99; 307 | GCC_DYNAMIC_NO_PIC = NO; 308 | GCC_NO_COMMON_BLOCKS = YES; 309 | GCC_OPTIMIZATION_LEVEL = 0; 310 | GCC_PREPROCESSOR_DEFINITIONS = ( 311 | "DEBUG=1", 312 | "$(inherited)", 313 | ); 314 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 315 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 316 | GCC_WARN_UNDECLARED_SELECTOR = YES; 317 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 318 | GCC_WARN_UNUSED_FUNCTION = YES; 319 | GCC_WARN_UNUSED_VARIABLE = YES; 320 | MACOSX_DEPLOYMENT_TARGET = 10.11; 321 | MTL_ENABLE_DEBUG_INFO = YES; 322 | ONLY_ACTIVE_ARCH = YES; 323 | SDKROOT = macosx; 324 | SWIFT_INSTALL_OBJC_HEADER = NO; 325 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 326 | }; 327 | name = Debug; 328 | }; 329 | 4A56D14D1DE5B573000B40D8 /* Release */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ALWAYS_SEARCH_USER_PATHS = NO; 333 | CLANG_ANALYZER_NONNULL = YES; 334 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 335 | CLANG_CXX_LIBRARY = "libc++"; 336 | CLANG_ENABLE_MODULES = YES; 337 | CLANG_ENABLE_OBJC_ARC = YES; 338 | CLANG_WARN_BOOL_CONVERSION = YES; 339 | CLANG_WARN_CONSTANT_CONVERSION = YES; 340 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 341 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 342 | CLANG_WARN_EMPTY_BODY = YES; 343 | CLANG_WARN_ENUM_CONVERSION = YES; 344 | CLANG_WARN_INFINITE_RECURSION = YES; 345 | CLANG_WARN_INT_CONVERSION = YES; 346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 347 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 348 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 349 | CLANG_WARN_UNREACHABLE_CODE = YES; 350 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 351 | CODE_SIGN_IDENTITY = "-"; 352 | COPY_PHASE_STRIP = NO; 353 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 354 | ENABLE_NS_ASSERTIONS = NO; 355 | ENABLE_STRICT_OBJC_MSGSEND = YES; 356 | GCC_C_LANGUAGE_STANDARD = gnu99; 357 | GCC_NO_COMMON_BLOCKS = YES; 358 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 359 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 360 | GCC_WARN_UNDECLARED_SELECTOR = YES; 361 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 362 | GCC_WARN_UNUSED_FUNCTION = YES; 363 | GCC_WARN_UNUSED_VARIABLE = YES; 364 | MACOSX_DEPLOYMENT_TARGET = 10.11; 365 | MTL_ENABLE_DEBUG_INFO = NO; 366 | SDKROOT = macosx; 367 | SWIFT_INSTALL_OBJC_HEADER = NO; 368 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 369 | }; 370 | name = Release; 371 | }; 372 | 4A56D14F1DE5B573000B40D8 /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | DEVELOPMENT_TEAM = D92MT22EX5; 376 | PRODUCT_NAME = "$(TARGET_NAME)"; 377 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 378 | SWIFT_VERSION = 4.0; 379 | }; 380 | name = Debug; 381 | }; 382 | 4A56D1501DE5B573000B40D8 /* Release */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | DEVELOPMENT_TEAM = D92MT22EX5; 386 | PRODUCT_NAME = "$(TARGET_NAME)"; 387 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 388 | SWIFT_VERSION = 4.0; 389 | }; 390 | name = Release; 391 | }; 392 | 4AD71E821EA4CCBA00016A1A /* Debug */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 396 | COMBINE_HIDPI_IMAGES = YES; 397 | DEVELOPMENT_TEAM = D92MT22EX5; 398 | INFOPLIST_FILE = "XCAssetPacker Tests/Info.plist"; 399 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 400 | MACOSX_DEPLOYMENT_TARGET = 10.12; 401 | PRODUCT_BUNDLE_IDENTIFIER = "com.inquisitiveSoftware.XCAssetPacker-Tests"; 402 | PRODUCT_NAME = "$(TARGET_NAME)"; 403 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 404 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 405 | SWIFT_VERSION = 4.0; 406 | }; 407 | name = Debug; 408 | }; 409 | 4AD71E831EA4CCBA00016A1A /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 413 | COMBINE_HIDPI_IMAGES = YES; 414 | DEVELOPMENT_TEAM = D92MT22EX5; 415 | INFOPLIST_FILE = "XCAssetPacker Tests/Info.plist"; 416 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 417 | MACOSX_DEPLOYMENT_TARGET = 10.12; 418 | PRODUCT_BUNDLE_IDENTIFIER = "com.inquisitiveSoftware.XCAssetPacker-Tests"; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 421 | SWIFT_VERSION = 4.0; 422 | }; 423 | name = Release; 424 | }; 425 | /* End XCBuildConfiguration section */ 426 | 427 | /* Begin XCConfigurationList section */ 428 | 4A56D1421DE5B573000B40D8 /* Build configuration list for PBXProject "XCAssetPacker" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | 4A56D14C1DE5B573000B40D8 /* Debug */, 432 | 4A56D14D1DE5B573000B40D8 /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Release; 436 | }; 437 | 4A56D14E1DE5B573000B40D8 /* Build configuration list for PBXNativeTarget "xcassetpacker" */ = { 438 | isa = XCConfigurationList; 439 | buildConfigurations = ( 440 | 4A56D14F1DE5B573000B40D8 /* Debug */, 441 | 4A56D1501DE5B573000B40D8 /* Release */, 442 | ); 443 | defaultConfigurationIsVisible = 0; 444 | defaultConfigurationName = Release; 445 | }; 446 | 4AD71E811EA4CCBA00016A1A /* Build configuration list for PBXNativeTarget "XCAssetPacker Tests" */ = { 447 | isa = XCConfigurationList; 448 | buildConfigurations = ( 449 | 4AD71E821EA4CCBA00016A1A /* Debug */, 450 | 4AD71E831EA4CCBA00016A1A /* Release */, 451 | ); 452 | defaultConfigurationIsVisible = 0; 453 | defaultConfigurationName = Release; 454 | }; 455 | /* End XCConfigurationList section */ 456 | }; 457 | rootObject = 4A56D13F1DE5B573000B40D8 /* Project object */; 458 | } 459 | -------------------------------------------------------------------------------- /XCAssetPacker/AssetCatalogGenerator+Creation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssetCatalog.swift 3 | // XCAssetPacker 4 | // 5 | // Created by Harry Jordan on 17/04/2017. 6 | // Copyright © 2017 Inquisitive Software. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | 13 | extension AssetCatalogGenerator { 14 | 15 | convenience init(from sourceDirectoryURL: URL, to destinationDirectoryURL: URL, swift swiftDestinationURL: URL?, target: SwiftTarget, overwrite shouldOverwrite: Bool, configuration: [String: Any]) throws { 16 | let fileManager = FileManager() 17 | 18 | guard let fileEnumerator = fileManager.enumerator(at: sourceDirectoryURL, includingPropertiesForKeys: [.isRegularFileKey], options: [], errorHandler: nil) else { 19 | print() 20 | throw AssetCatalogError.ioError("Can't create file enumerator for \(sourceDirectoryURL)") 21 | } 22 | 23 | 24 | // Validate the destination url 25 | var isDirectory: ObjCBool = false 26 | var destinationURL = destinationDirectoryURL 27 | 28 | if fileManager.fileExists(atPath: destinationURL.path, isDirectory: &isDirectory), 29 | isDirectory.boolValue, 30 | destinationURL.pathExtension != FileExtension.assetPackage.rawValue { 31 | destinationURL = destinationURL.appendingPathComponent("Assets.\(FileExtension.assetPackage.rawValue)") 32 | } 33 | 34 | 35 | // Validate the Swift output url 36 | var swiftFileURL: URL? = swiftDestinationURL 37 | 38 | if let swiftDestinationURL = swiftDestinationURL, 39 | fileManager.fileExists(atPath: swiftDestinationURL.path, isDirectory: &isDirectory), 40 | isDirectory.boolValue, 41 | swiftDestinationURL.pathExtension != FileExtension.swift.rawValue { 42 | swiftFileURL = swiftDestinationURL.appendingPathComponent("Images.\(FileExtension.swift)") 43 | } 44 | 45 | self.init(at: destinationURL, swift: swiftFileURL, target: target, shouldOverwrite: shouldOverwrite, configuration: configuration) 46 | 47 | 48 | // Limit to a set of image extensions 49 | let validImageExtensions: [String] 50 | 51 | if let imageExtensions = configuration.value(for: .validImageExtensions) as? [String], !imageExtensions.isEmpty { 52 | validImageExtensions = imageExtensions.map { $0.lowercased() } 53 | } else { 54 | validImageExtensions = ["png"] 55 | } 56 | 57 | 58 | // Enumarate images 59 | while let file = fileEnumerator.nextObject() { 60 | if let fileURL = file as? URL, let resourceValues = try? fileURL.resourceValues(forKeys: [.isRegularFileKey]) { 61 | if validImageExtensions.contains(fileURL.pathExtension.lowercased()), let isRegularFile = resourceValues.isRegularFile, isRegularFile { 62 | self.addImageAsset(from: fileURL, inDirectory: sourceDirectoryURL) 63 | } else if fileURL.pathExtension == FileExtension.assetPackage.rawValue { 64 | // Don't search within existing .xcasset packages 65 | fileEnumerator.skipDescendants() 66 | } 67 | } 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /XCAssetPacker/AssetCatalogGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssetCatalogGenerator.swift 3 | // XCAssetPacker 4 | // 5 | // Created by Harry Jordan on 23/11/2016. 6 | // Copyright © 2016 Inquisitive Software. All rights reserved. 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 | import Foundation 22 | 23 | 24 | enum AssetCatalogError: Error { 25 | case ioError(String) 26 | } 27 | 28 | 29 | enum AssetCatalogLogLevel { 30 | // Fast only adds up the number of images included 31 | case fast 32 | 33 | // Detailed populates the images and imageProperties arrays 34 | case detailed 35 | } 36 | 37 | 38 | 39 | struct AssetCatalogLog { 40 | 41 | init(logLevel: AssetCatalogLogLevel, images: [[String: Any]]) { 42 | self.logLevel = logLevel 43 | numberOfImages = images.count 44 | 45 | if logLevel == .detailed { 46 | self.images = images 47 | } 48 | } 49 | 50 | let logLevel: AssetCatalogLogLevel 51 | var numberOfImages: Int 52 | var images: [[String: Any]] = [] 53 | var code: String? 54 | 55 | mutating func append(_ otherResult: AssetCatalogLog) { 56 | numberOfImages += otherResult.numberOfImages 57 | 58 | if logLevel == .detailed { 59 | images.append(contentsOf: otherResult.images) 60 | } 61 | } 62 | 63 | } 64 | 65 | 66 | class Node { 67 | let name: String 68 | var sourceURL: URL? 69 | var properties: ImageProperties? 70 | let parent: Node? 71 | var children: [Node] = [] 72 | var isImageSet = false 73 | var isAppIcon = false 74 | 75 | init(sourceURL: URL? = nil, name: String, parent: Node?) { 76 | self.sourceURL = sourceURL 77 | self.name = name 78 | self.parent = parent 79 | } 80 | 81 | 82 | func childNode(named childName: String) -> Node { 83 | for child in children { 84 | if child.name.compare(childName, options: [.caseInsensitive]) == .orderedSame { 85 | return child 86 | } 87 | } 88 | 89 | let newNode = Node(name: childName, parent: self) 90 | children.append(newNode) 91 | 92 | return newNode 93 | } 94 | 95 | 96 | var isDirectory: Bool { 97 | return sourceURL == nil 98 | } 99 | 100 | 101 | var pathComponents: [String] { 102 | var currentNode: Node? = self 103 | var pathComponents: [String] = [] 104 | 105 | while let node = currentNode, node.parent != nil { 106 | pathComponents.append(node.name) 107 | currentNode = node.parent 108 | } 109 | 110 | return pathComponents.reversed() 111 | } 112 | 113 | 114 | func printTree(depth: Int = 0) { 115 | let depthPadding = (0...depth).reduce("") { (existing, _) -> String in 116 | return existing + " " 117 | } 118 | 119 | print(depthPadding + "- \(name)") 120 | let childDepth = depth + 1 121 | 122 | for child in children { 123 | child.printTree(depth: childDepth) 124 | } 125 | } 126 | 127 | } 128 | 129 | 130 | class AssetCatalogGenerator { 131 | let destinationURL: URL 132 | let swiftFileURL: URL? 133 | let swiftTarget: SwiftTarget 134 | let shouldOverwrite: Bool 135 | let baseIdiom: DeviceIdiom? 136 | let rootNode: Node 137 | 138 | let numberOfRootPathComponents: Int 139 | let configuration: [String: Any] 140 | 141 | let fileManager = FileManager() 142 | 143 | 144 | init(at destinationURL: URL, swift: URL?, target: SwiftTarget, shouldOverwrite: Bool, configuration: [String: Any]) { 145 | self.destinationURL = destinationURL 146 | self.rootNode = Node(name: destinationURL.lastPathComponent, parent: nil) 147 | self.numberOfRootPathComponents = destinationURL.pathComponents.count 148 | 149 | self.swiftFileURL = swift 150 | self.swiftTarget = target 151 | 152 | self.shouldOverwrite = shouldOverwrite 153 | self.configuration = configuration 154 | 155 | if let base = configuration.value(for: .base) as? [String: Any], 156 | let idiom = base.value(for: .idiom) as? String { 157 | baseIdiom = DeviceIdiom(idiom) 158 | } else { 159 | baseIdiom = nil 160 | } 161 | } 162 | 163 | 164 | // MARK: Construct a tree of source images 165 | 166 | func addImageAsset(from sourceURL: URL, inDirectory sourceDirectoryURL: URL) { 167 | let numberOfPathComponents = sourceDirectoryURL.pathComponents.count 168 | let fileName = sourceURL.lastPathComponent 169 | var isAppIcon = false 170 | 171 | if let include = configuration.value(for: .includeImages) as? [String: Any], 172 | let patternsToRequire = include.value(for: .multiplePatterns) as? [String], 173 | !fileName.isMatchedBy(patternsToRequire) { 174 | return 175 | } 176 | 177 | if let skip = configuration.value(for: .skipImages) as? [String: Any], 178 | let patternsToSkip = skip.value(for: .multiplePatterns) as? [String], 179 | fileName.isMatchedBy(patternsToSkip) { 180 | return 181 | } 182 | 183 | if let appIcon = configuration.value(for: .appIcon) as? [String: Any], 184 | let appIconPattern = appIcon.value(for: .pattern) as? String { 185 | isAppIcon = fileName.isMatchedBy(appIconPattern) 186 | } else { 187 | isAppIcon = fileName.isMatchedBy(FileName.appIcon.rawValue) 188 | } 189 | 190 | let imageProperties = ImageProperties(from: fileName, isAppIcon: isAppIcon, configuration: configuration) 191 | 192 | let groupFileExtension: FileExtension = isAppIcon ? .appIconSet : .imageSet 193 | let generalizedImageSetName = (imageProperties.name as NSString).appendingPathExtension(groupFileExtension.rawValue)! 194 | 195 | var pathComponents = sourceURL.pathComponents.suffix(from: numberOfPathComponents) 196 | 197 | // Create an asset with a generalized name stripped of the -38, -42 or @2x etc. prefixes 198 | _ = pathComponents.popLast() 199 | pathComponents.append(generalizedImageSetName) 200 | 201 | var currentNode = rootNode 202 | 203 | for pathComponent in pathComponents { 204 | currentNode = currentNode.childNode(named: pathComponent) 205 | } 206 | 207 | currentNode.isImageSet = true 208 | currentNode.isAppIcon = isAppIcon 209 | 210 | let imageNode = currentNode.childNode(named: fileName) 211 | imageNode.properties = imageProperties 212 | imageNode.sourceURL = sourceURL 213 | } 214 | 215 | 216 | // MARK: Outputing the .xcasset package 217 | 218 | @discardableResult func applyChanges(logLevel: AssetCatalogLogLevel = .fast, dryRun: Bool = false) throws -> AssetCatalogLog { 219 | // Overwrite an existing .xcasset package if the --force argument is supplied 220 | if shouldOverwrite { 221 | if fileManager.fileExists(atPath: destinationURL.path) { 222 | try fileManager.removeItem(at: destinationURL) 223 | } 224 | } else { 225 | // Otherwise bail if an existing .xcasset package or swift file exist 226 | let destinationExists = fileManager.fileExists(atPath: destinationURL.path) 227 | 228 | if destinationExists { 229 | throw AssetCatalogError.ioError("An asset collection already exists at: \(destinationURL)") 230 | } 231 | 232 | if let swiftFileURL = swiftFileURL { 233 | let swiftFileExists = fileManager.fileExists(atPath: swiftFileURL.path) 234 | 235 | if(swiftFileExists) { 236 | throw AssetCatalogError.ioError("An swift file already exists at: \(swiftFileURL)") 237 | } 238 | } 239 | } 240 | 241 | 242 | // Generate Swift code 243 | let generatedCode: String? 244 | 245 | if let swiftFileURL = swiftFileURL { 246 | let code = try swiftCode(forTarget: swiftTarget, rootNode: rootNode) 247 | try code.write(to: swiftFileURL, atomically: true, encoding: .utf8) 248 | generatedCode = code 249 | } else { 250 | generatedCode = nil 251 | } 252 | 253 | // Create the folder structure, the metadata and copy the images across 254 | var result = try applyChanges(forNode: rootNode, dryRun: dryRun, logLevel: logLevel) 255 | result.code = generatedCode 256 | 257 | return result 258 | } 259 | 260 | 261 | func applyChanges(forNode node: Node, dryRun: Bool, logLevel: AssetCatalogLogLevel) throws -> AssetCatalogLog { 262 | // Create a Contents.json for each directory 263 | var images: [[String : Any]] = [] 264 | var copies: [(from: URL, to: URL)] = [] 265 | var imageProperties: [ImageProperties] = [] 266 | 267 | for child in node.children where !child.isDirectory { 268 | if let sourceURL = child.sourceURL, let properties = child.properties { 269 | imageProperties.append(properties) 270 | 271 | let imageFileName = sourceURL.lastPathComponent 272 | let assetDestinationURL = destinationURL.appending(pathComponents: node.pathComponents).appendingPathComponent(imageFileName) 273 | 274 | // Xcode 9 seperates Notification, Settings and Spotlight images for iPhone and iPad 275 | // if no base idiom is supplied then add both images 276 | // 277 | // This is rather messy, so would ideally be tidied up 278 | switch properties.type { 279 | case .notification, .settings, .spotlight: 280 | if let idiom = baseIdiom { 281 | images.append(imageDictionary(for: imageFileName, properties: properties, customIdiom: idiom.idiomString)) 282 | } else { 283 | let targetIdioms: [DeviceIdiom] = [.iPhone, .iPad] 284 | 285 | for targetIdiom in targetIdioms { 286 | images.append(imageDictionary(for: imageFileName, properties: properties, customIdiom: targetIdiom.idiomString)) 287 | } 288 | } 289 | 290 | default: 291 | images.append(imageDictionary(for: imageFileName, properties: properties)) 292 | } 293 | 294 | copies.append((sourceURL, assetDestinationURL)) 295 | } 296 | } 297 | 298 | 299 | var contents = contentsDictionaryFor(node: node, imageProperties: imageProperties) 300 | 301 | if !images.isEmpty { 302 | contents[Configuration.images.rawValue] = images 303 | } 304 | 305 | if !dryRun { 306 | // Create the destination folder 307 | let folderURL = destinationURL.appending(pathComponents: node.pathComponents) 308 | try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil) 309 | 310 | // Save the Contents.json 311 | try write(contents, to: folderURL, fileName: FileName.contents.rawValue) 312 | 313 | // Perform copies 314 | for (sourceURL, destinationURL) in copies { 315 | try fileManager.copyItem(at: sourceURL, to: destinationURL) 316 | } 317 | } 318 | 319 | // Apply recursively for each child directory 320 | var result = AssetCatalogLog(logLevel: logLevel, images: images) 321 | 322 | for child in node.children where child.isDirectory { 323 | let childResult = try applyChanges(forNode: child, dryRun: dryRun, logLevel: logLevel) 324 | result.append(childResult) 325 | } 326 | 327 | return result 328 | } 329 | 330 | 331 | func write(_ json: [String: Any], to url: URL, fileName: String) throws { 332 | let destinationURL = url.appendingPathComponent(fileName) 333 | 334 | let jsonData = try JSONSerialization.data(withJSONObject: json, options: []) 335 | try jsonData.write(to: destinationURL) 336 | } 337 | 338 | 339 | // MARK: Generate json for each component within the .xcassets package 340 | 341 | 342 | func imageDictionary(for imageName: String, properties: ImageProperties, customIdiom: String? = nil) -> [String: Any] { 343 | var imageDictionary: [String: Any] = [:] 344 | imageDictionary[Configuration.filename.rawValue] = imageName 345 | 346 | if let idiom = customIdiom { 347 | imageDictionary[Configuration.idiom.rawValue] = idiom 348 | } else if let idiom = properties.idiom { 349 | imageDictionary[Configuration.idiom.rawValue] = idiom 350 | } 351 | 352 | if let scale = properties.scaleString { 353 | imageDictionary[Configuration.scale.rawValue] = scale 354 | } 355 | 356 | if let size = properties.type.sizeString { 357 | imageDictionary[Configuration.size.rawValue] = size 358 | } 359 | 360 | if let screenWidth = properties.type.screenWidth { 361 | imageDictionary[Configuration.screenWidth.rawValue] = screenWidth 362 | } 363 | 364 | if let prerendered = properties.prerendered { 365 | imageDictionary[Configuration.prerendered.rawValue] = prerendered 366 | } 367 | 368 | return imageDictionary 369 | } 370 | 371 | 372 | func contentsDictionaryFor(node: Node, imageProperties: [ImageProperties]) -> [String: Any] { 373 | var contents = (configuration.value(for: .info) as? [String: Any]) ?? [ 374 | "info" : [ 375 | "version" : 1, 376 | "author" : "xcode" 377 | ] 378 | ] 379 | 380 | var combinedProperties: [String: Any] = [:] 381 | 382 | if let baseProperties = configuration.value(for: .base) as? [String: Any] { 383 | for (key, value) in baseProperties { 384 | combinedProperties[key] = value 385 | } 386 | } 387 | 388 | 389 | if let devices = configuration.value(for: .devices) as? [[String: Any]] { 390 | for device in devices { 391 | // Test that the device type matches the image format 392 | if let deviceType = device.value(for: .deviceType) as? String, 393 | deviceType == imageProperties.first?.type.deviceType, 394 | let properties = device.value(for: .properties) as? [String: Any] { 395 | for (key, value) in properties { 396 | combinedProperties[key] = value 397 | } 398 | } 399 | } 400 | } 401 | 402 | 403 | if let customProperties = configuration.value(for: .custom) as? [[String: Any]] { 404 | for customProperty in customProperties { 405 | if let patterns = customProperty.value(for: .multiplePatterns) as? [String], 406 | node.name.isMatchedBy(patterns), 407 | let properties = customProperty.value(for: .properties) as? [String: Any] { 408 | 409 | for (key, value) in properties { 410 | combinedProperties[key] = value 411 | } 412 | } 413 | } 414 | } 415 | 416 | if !combinedProperties.isEmpty { 417 | contents[Configuration.properties.rawValue] = combinedProperties 418 | } 419 | 420 | return contents 421 | } 422 | 423 | } 424 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | .build/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.hmap 18 | *.ipa 19 | *.xcuserstate 20 | 21 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: osx 4 | language: objective-c # this is a lie 5 | xcode_project: CommandLine.xcodeproj 6 | xcode_scheme: CommandLine 7 | script: xcodebuild -scheme CommandLine test 8 | osx_image: xcode8.2 9 | - os: linux 10 | sudo: required 11 | dist: trusty 12 | before_install: 13 | - sudo apt-get install -y clang libicu-dev 14 | - sudo locale-gen sv_SE.UTF-8 # for alternate-locale tests 15 | - ./install-linux-swift.sh 16 | - export PATH="/swift/usr/bin:${PATH}" 17 | script: 18 | - swift build -v 19 | - swift test 20 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/CommandLineKit.xcodeproj/xcshareddata/xcschemes/CommandLine.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/CommandLineKit/CommandLine.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * CommandLine.swift 3 | * Copyright (c) 2014 Ben Gollmer. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import Foundation 19 | /* Required for setlocale(3) */ 20 | #if os(OSX) 21 | import Darwin 22 | #elseif os(Linux) 23 | import Glibc 24 | #endif 25 | 26 | let shortOptionPrefix = "-" 27 | let longOptionPrefix = "--" 28 | 29 | /* Stop parsing arguments when an ArgumentStopper (--) is detected. This is a GNU getopt 30 | * convention; cf. https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html 31 | */ 32 | let argumentStopper = "--" 33 | 34 | /* Allow arguments to be attached to flags when separated by this character. 35 | * --flag=argument is equivalent to --flag argument 36 | */ 37 | let argumentAttacher: Character = "=" 38 | 39 | /* An output stream to stderr; used by CommandLine.printUsage(). */ 40 | private struct StderrOutputStream: TextOutputStream { 41 | static let stream = StderrOutputStream() 42 | func write(_ s: String) { 43 | fputs(s, stderr) 44 | } 45 | } 46 | 47 | /** 48 | * The CommandLine class implements a command-line interface for your app. 49 | * 50 | * To use it, define one or more Options (see Option.swift) and add them to your 51 | * CommandLine object, then invoke `parse()`. Each Option object will be populated with 52 | * the value given by the user. 53 | * 54 | * If any required options are missing or if an invalid value is found, `parse()` will throw 55 | * a `ParseError`. You can then call `printUsage()` to output an automatically-generated usage 56 | * message. 57 | */ 58 | public class CommandLine { 59 | private var _arguments: [String] 60 | private var _options: [Option] = [Option]() 61 | private var _maxFlagDescriptionWidth: Int = 0 62 | private var _usedFlags: Set { 63 | var usedFlags = Set(minimumCapacity: _options.count * 2) 64 | 65 | for option in _options { 66 | for case let flag? in [option.shortFlag, option.longFlag] { 67 | usedFlags.insert(flag) 68 | } 69 | } 70 | 71 | return usedFlags 72 | } 73 | 74 | /** 75 | * After calling `parse()`, this property will contain any values that weren't captured 76 | * by an Option. For example: 77 | * 78 | * ``` 79 | * let cli = CommandLine() 80 | * let fileType = StringOption(shortFlag: "t", longFlag: "type", required: true, helpMessage: "Type of file") 81 | * 82 | * do { 83 | * try cli.parse() 84 | * print("File type is \(type), files are \(cli.unparsedArguments)") 85 | * catch { 86 | * cli.printUsage(error) 87 | * exit(EX_USAGE) 88 | * } 89 | * 90 | * --- 91 | * 92 | * $ ./readfiles --type=pdf ~/file1.pdf ~/file2.pdf 93 | * File type is pdf, files are ["~/file1.pdf", "~/file2.pdf"] 94 | * ``` 95 | */ 96 | public private(set) var unparsedArguments: [String] = [String]() 97 | 98 | /** 99 | * If supplied, this function will be called when printing usage messages. 100 | * 101 | * You can use the `defaultFormat` function to get the normally-formatted 102 | * output, either before or after modifying the provided string. For example: 103 | * 104 | * ``` 105 | * let cli = CommandLine() 106 | * cli.formatOutput = { str, type in 107 | * switch(type) { 108 | * case .Error: 109 | * // Make errors shouty 110 | * return defaultFormat(str.uppercaseString, type: type) 111 | * case .OptionHelp: 112 | * // Don't use the default indenting 113 | * return ">> \(s)\n" 114 | * default: 115 | * return defaultFormat(str, type: type) 116 | * } 117 | * } 118 | * ``` 119 | * 120 | * - note: Newlines are not appended to the result of this function. If you don't use 121 | * `defaultFormat()`, be sure to add them before returning. 122 | */ 123 | public var formatOutput: ((String, OutputType) -> String)? 124 | 125 | /** 126 | * The maximum width of all options' `flagDescription` properties; provided for use by 127 | * output formatters. 128 | * 129 | * - seealso: `defaultFormat`, `formatOutput` 130 | */ 131 | public var maxFlagDescriptionWidth: Int { 132 | if _maxFlagDescriptionWidth == 0 { 133 | _maxFlagDescriptionWidth = _options.map { $0.flagDescription.characters.count }.sorted().first ?? 0 134 | } 135 | 136 | return _maxFlagDescriptionWidth 137 | } 138 | 139 | /** 140 | * The type of output being supplied to an output formatter. 141 | * 142 | * - seealso: `formatOutput` 143 | */ 144 | public enum OutputType { 145 | /** About text: `Usage: command-example [options]` and the like */ 146 | case about 147 | 148 | /** An error message: `Missing required option --extract` */ 149 | case error 150 | 151 | /** An Option's `flagDescription`: `-h, --help:` */ 152 | case optionFlag 153 | 154 | /** An Option's help message */ 155 | case optionHelp 156 | } 157 | 158 | /** A ParseError is thrown if the `parse()` method fails. */ 159 | public enum ParseError: Error, CustomStringConvertible { 160 | /** Thrown if an unrecognized argument is passed to `parse()` in strict mode */ 161 | case invalidArgument(String) 162 | 163 | /** Thrown if the value for an Option is invalid (e.g. a string is passed to an IntOption) */ 164 | case invalidValueForOption(Option, [String]) 165 | 166 | /** Thrown if an Option with required: true is missing */ 167 | case missingRequiredOptions([Option]) 168 | 169 | public var description: String { 170 | switch self { 171 | case let .invalidArgument(arg): 172 | return "Invalid argument: \(arg)" 173 | case let .invalidValueForOption(opt, vals): 174 | let vs = vals.joined(separator: ", ") 175 | return "Invalid value(s) for option \(opt.flagDescription): \(vs)" 176 | case let .missingRequiredOptions(opts): 177 | return "Missing required options: \(opts.map { return $0.flagDescription })" 178 | } 179 | } 180 | } 181 | 182 | /** 183 | * Initializes a CommandLine object. 184 | * 185 | * - parameter arguments: Arguments to parse. If omitted, the arguments passed to the app 186 | * on the command line will automatically be used. 187 | * 188 | * - returns: An initalized CommandLine object. 189 | */ 190 | public init(arguments: [String] = Swift.CommandLine.arguments) { 191 | self._arguments = arguments 192 | 193 | /* Initialize locale settings from the environment */ 194 | setlocale(LC_ALL, "") 195 | } 196 | 197 | /* Returns all argument values from flagIndex to the next flag or the end of the argument array. */ 198 | private func _getFlagValues(_ flagIndex: Int, _ attachedArg: String? = nil) -> [String] { 199 | var args: [String] = [String]() 200 | var skipFlagChecks = false 201 | 202 | if let a = attachedArg { 203 | args.append(a) 204 | } 205 | 206 | for i in flagIndex + 1 ..< _arguments.count { 207 | if !skipFlagChecks { 208 | if _arguments[i] == argumentStopper { 209 | skipFlagChecks = true 210 | continue 211 | } 212 | 213 | if _arguments[i].hasPrefix(shortOptionPrefix) && Int(_arguments[i]) == nil && 214 | _arguments[i].toDouble() == nil { 215 | break 216 | } 217 | } 218 | 219 | args.append(_arguments[i]) 220 | } 221 | 222 | return args 223 | } 224 | 225 | /** 226 | * Adds an Option to the command line. 227 | * 228 | * - parameter option: The option to add. 229 | */ 230 | public func addOption(_ option: Option) { 231 | let uf = _usedFlags 232 | for case let flag? in [option.shortFlag, option.longFlag] { 233 | assert(!uf.contains(flag), "Flag '\(flag)' already in use") 234 | } 235 | 236 | _options.append(option) 237 | _maxFlagDescriptionWidth = 0 238 | } 239 | 240 | /** 241 | * Adds one or more Options to the command line. 242 | * 243 | * - parameter options: An array containing the options to add. 244 | */ 245 | public func addOptions(_ options: [Option]) { 246 | for o in options { 247 | addOption(o) 248 | } 249 | } 250 | 251 | /** 252 | * Adds one or more Options to the command line. 253 | * 254 | * - parameter options: The options to add. 255 | */ 256 | public func addOptions(_ options: Option...) { 257 | for o in options { 258 | addOption(o) 259 | } 260 | } 261 | 262 | /** 263 | * Sets the command line Options. Any existing options will be overwritten. 264 | * 265 | * - parameter options: An array containing the options to set. 266 | */ 267 | public func setOptions(_ options: [Option]) { 268 | _options = [Option]() 269 | addOptions(options) 270 | } 271 | 272 | /** 273 | * Sets the command line Options. Any existing options will be overwritten. 274 | * 275 | * - parameter options: The options to set. 276 | */ 277 | public func setOptions(_ options: Option...) { 278 | _options = [Option]() 279 | addOptions(options) 280 | } 281 | 282 | /** 283 | * Parses command-line arguments into their matching Option values. 284 | * 285 | * - parameter strict: Fail if any unrecognized flags are present (default: false). 286 | * 287 | * - throws: A `ParseError` if argument parsing fails: 288 | * - `.InvalidArgument` if an unrecognized flag is present and `strict` is true 289 | * - `.InvalidValueForOption` if the value supplied to an option is not valid (for 290 | * example, a string is supplied for an IntOption) 291 | * - `.MissingRequiredOptions` if a required option isn't present 292 | */ 293 | public func parse(strict: Bool = false) throws { 294 | var strays = _arguments 295 | 296 | /* Nuke executable name */ 297 | strays[0] = "" 298 | 299 | let argumentsEnumerator = _arguments.enumerated() 300 | for (idx, arg) in argumentsEnumerator { 301 | if arg == argumentStopper { 302 | break 303 | } 304 | 305 | if !arg.hasPrefix(shortOptionPrefix) { 306 | continue 307 | } 308 | 309 | let skipChars = arg.hasPrefix(longOptionPrefix) ? 310 | longOptionPrefix.characters.count : shortOptionPrefix.characters.count 311 | let flagWithArg = arg[arg.index(arg.startIndex, offsetBy: skipChars).. 348 | */ 349 | let vals = (i == flagLength - 1) ? self._getFlagValues(idx, attachedArg) : [String]() 350 | guard option.setValue(vals) else { 351 | throw ParseError.invalidValueForOption(option, vals) 352 | } 353 | 354 | var claimedIdx = idx + option.claimedValues 355 | if attachedArg != nil { claimedIdx -= 1 } 356 | for i in idx...claimedIdx { 357 | strays[i] = "" 358 | } 359 | 360 | flagMatched = true 361 | break 362 | } 363 | } 364 | } 365 | 366 | /* Invalid flag */ 367 | guard !strict || flagMatched else { 368 | throw ParseError.invalidArgument(arg) 369 | } 370 | } 371 | 372 | /* Check to see if any required options were not matched */ 373 | let missingOptions = _options.filter { $0.required && !$0.wasSet } 374 | guard missingOptions.count == 0 else { 375 | throw ParseError.missingRequiredOptions(missingOptions) 376 | } 377 | 378 | unparsedArguments = strays.filter { $0 != "" } 379 | } 380 | 381 | /** 382 | * Provides the default formatting of `printUsage()` output. 383 | * 384 | * - parameter s: The string to format. 385 | * - parameter type: Type of output. 386 | * 387 | * - returns: The formatted string. 388 | * - seealso: `formatOutput` 389 | */ 390 | public func defaultFormat(s: String, type: OutputType) -> String { 391 | switch type { 392 | case .about: 393 | return "\(s)\n" 394 | case .error: 395 | return "\(s)\n\n" 396 | case .optionFlag: 397 | return " \(s.padded(toWidth: maxFlagDescriptionWidth)):\n" 398 | case .optionHelp: 399 | return " \(s)\n" 400 | } 401 | } 402 | 403 | /* printUsage() is generic for OutputStreamType because the Swift compiler crashes 404 | * on inout protocol function parameters in Xcode 7 beta 1 (rdar://21372694). 405 | */ 406 | 407 | /** 408 | * Prints a usage message. 409 | * 410 | * - parameter to: An OutputStreamType to write the error message to. 411 | */ 412 | public func printUsage(_ to: inout TargetStream) { 413 | /* Nil coalescing operator (??) doesn't work on closures :( */ 414 | let format = formatOutput != nil ? formatOutput! : defaultFormat 415 | 416 | let name = _arguments[0] 417 | print(format("Usage: \(name) [options]", .about), terminator: "", to: &to) 418 | 419 | for opt in _options { 420 | print(format(opt.flagDescription, .optionFlag), terminator: "", to: &to) 421 | print(format(opt.helpMessage, .optionHelp), terminator: "", to: &to) 422 | } 423 | } 424 | 425 | /** 426 | * Prints a usage message. 427 | * 428 | * - parameter error: An error thrown from `parse()`. A description of the error 429 | * (e.g. "Missing required option --extract") will be printed before the usage message. 430 | * - parameter to: An OutputStreamType to write the error message to. 431 | */ 432 | public func printUsage(_ error: Error, to: inout TargetStream) { 433 | let format = formatOutput != nil ? formatOutput! : defaultFormat 434 | print(format("\(error)", .error), terminator: "", to: &to) 435 | printUsage(&to) 436 | } 437 | 438 | /** 439 | * Prints a usage message. 440 | * 441 | * - parameter error: An error thrown from `parse()`. A description of the error 442 | * (e.g. "Missing required option --extract") will be printed before the usage message. 443 | */ 444 | public func printUsage(_ error: Error) { 445 | var out = StderrOutputStream.stream 446 | printUsage(error, to: &out) 447 | } 448 | 449 | /** 450 | * Prints a usage message. 451 | */ 452 | public func printUsage() { 453 | var out = StderrOutputStream.stream 454 | printUsage(&out) 455 | } 456 | } 457 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/CommandLineKit/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSHumanReadableCopyright 24 | Copyright © 2014 Ben Gollmer. Licensed under the Apache License, Version 2.0. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/CommandLineKit/Option.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Option.swift 3 | * Copyright (c) 2014 Ben Gollmer. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * The base class for a command-line option. 20 | */ 21 | public class Option { 22 | public let shortFlag: String? 23 | public let longFlag: String? 24 | public let required: Bool 25 | public let helpMessage: String 26 | 27 | /** True if the option was set when parsing command-line arguments */ 28 | public var wasSet: Bool { 29 | return false 30 | } 31 | 32 | public var claimedValues: Int { return 0 } 33 | 34 | public var flagDescription: String { 35 | switch (shortFlag, longFlag) { 36 | case let (sf?, lf?): 37 | return "\(shortOptionPrefix)\(sf), \(longOptionPrefix)\(lf)" 38 | case (nil, let lf?): 39 | return "\(longOptionPrefix)\(lf)" 40 | case (let sf?, nil): 41 | return "\(shortOptionPrefix)\(sf)" 42 | default: 43 | return "" 44 | } 45 | } 46 | 47 | internal init(_ shortFlag: String?, _ longFlag: String?, _ required: Bool, _ helpMessage: String) { 48 | if let sf = shortFlag { 49 | assert(sf.characters.count == 1, "Short flag must be a single character") 50 | assert(Int(sf) == nil && sf.toDouble() == nil, "Short flag cannot be a numeric value") 51 | } 52 | 53 | if let lf = longFlag { 54 | assert(Int(lf) == nil && lf.toDouble() == nil, "Long flag cannot be a numeric value") 55 | } 56 | 57 | self.shortFlag = shortFlag 58 | self.longFlag = longFlag 59 | self.helpMessage = helpMessage 60 | self.required = required 61 | } 62 | 63 | /* The optional casts in these initalizers force them to call the private initializer. Without 64 | * the casts, they recursively call themselves. 65 | */ 66 | 67 | /** Initializes a new Option that has both long and short flags. */ 68 | public convenience init(shortFlag: String, longFlag: String, required: Bool = false, helpMessage: String) { 69 | self.init(shortFlag as String?, longFlag, required, helpMessage) 70 | } 71 | 72 | /** Initializes a new Option that has only a short flag. */ 73 | public convenience init(shortFlag: String, required: Bool = false, helpMessage: String) { 74 | self.init(shortFlag as String?, nil, required, helpMessage) 75 | } 76 | 77 | /** Initializes a new Option that has only a long flag. */ 78 | public convenience init(longFlag: String, required: Bool = false, helpMessage: String) { 79 | self.init(nil, longFlag as String?, required, helpMessage) 80 | } 81 | 82 | func flagMatch(_ flag: String) -> Bool { 83 | return flag == shortFlag || flag == longFlag 84 | } 85 | 86 | func setValue(_ values: [String]) -> Bool { 87 | return false 88 | } 89 | } 90 | 91 | /** 92 | * A boolean option. The presence of either the short or long flag will set the value to true; 93 | * absence of the flag(s) is equivalent to false. 94 | */ 95 | public class BoolOption: Option { 96 | private var _value: Bool = false 97 | 98 | public var value: Bool { 99 | return _value 100 | } 101 | 102 | override public var wasSet: Bool { 103 | return _value 104 | } 105 | 106 | override func setValue(_ values: [String]) -> Bool { 107 | _value = true 108 | return true 109 | } 110 | } 111 | 112 | /** An option that accepts a positive or negative integer value. */ 113 | public class IntOption: Option { 114 | private var _value: Int? 115 | 116 | public var value: Int? { 117 | return _value 118 | } 119 | 120 | override public var wasSet: Bool { 121 | return _value != nil 122 | } 123 | 124 | override public var claimedValues: Int { 125 | return _value != nil ? 1 : 0 126 | } 127 | 128 | override func setValue(_ values: [String]) -> Bool { 129 | if values.count == 0 { 130 | return false 131 | } 132 | 133 | if let val = Int(values[0]) { 134 | _value = val 135 | return true 136 | } 137 | 138 | return false 139 | } 140 | } 141 | 142 | /** 143 | * An option that represents an integer counter. Each time the short or long flag is found 144 | * on the command-line, the counter will be incremented. 145 | */ 146 | public class CounterOption: Option { 147 | private var _value: Int = 0 148 | 149 | public var value: Int { 150 | return _value 151 | } 152 | 153 | override public var wasSet: Bool { 154 | return _value > 0 155 | } 156 | 157 | public func reset() { 158 | _value = 0 159 | } 160 | 161 | override func setValue(_ values: [String]) -> Bool { 162 | _value += 1 163 | return true 164 | } 165 | } 166 | 167 | /** An option that accepts a positive or negative floating-point value. */ 168 | public class DoubleOption: Option { 169 | private var _value: Double? 170 | 171 | public var value: Double? { 172 | return _value 173 | } 174 | 175 | override public var wasSet: Bool { 176 | return _value != nil 177 | } 178 | 179 | override public var claimedValues: Int { 180 | return _value != nil ? 1 : 0 181 | } 182 | 183 | override func setValue(_ values: [String]) -> Bool { 184 | if values.count == 0 { 185 | return false 186 | } 187 | 188 | if let val = values[0].toDouble() { 189 | _value = val 190 | return true 191 | } 192 | 193 | return false 194 | } 195 | } 196 | 197 | /** An option that accepts a string value. */ 198 | public class StringOption: Option { 199 | private var _value: String? = nil 200 | 201 | public var value: String? { 202 | return _value 203 | } 204 | 205 | override public var wasSet: Bool { 206 | return _value != nil 207 | } 208 | 209 | override public var claimedValues: Int { 210 | return _value != nil ? 1 : 0 211 | } 212 | 213 | override func setValue(_ values: [String]) -> Bool { 214 | if values.count == 0 { 215 | return false 216 | } 217 | 218 | _value = values[0] 219 | return true 220 | } 221 | } 222 | 223 | /** An option that accepts one or more string values. */ 224 | public class MultiStringOption: Option { 225 | private var _value: [String]? 226 | 227 | public var value: [String]? { 228 | return _value 229 | } 230 | 231 | override public var wasSet: Bool { 232 | return _value != nil 233 | } 234 | 235 | override public var claimedValues: Int { 236 | if let v = _value { 237 | return v.count 238 | } 239 | 240 | return 0 241 | } 242 | 243 | override func setValue(_ values: [String]) -> Bool { 244 | if values.count == 0 { 245 | return false 246 | } 247 | 248 | _value = values 249 | return true 250 | } 251 | } 252 | 253 | /** An option that represents an enum value. */ 254 | public class EnumOption: Option where T.RawValue == String { 255 | private var _value: T? 256 | public var value: T? { 257 | return _value 258 | } 259 | 260 | override public var wasSet: Bool { 261 | return _value != nil 262 | } 263 | 264 | override public var claimedValues: Int { 265 | return _value != nil ? 1 : 0 266 | } 267 | 268 | /* Re-defining the intializers is necessary to make the Swift 2 compiler happy, as 269 | * of Xcode 7 beta 2. 270 | */ 271 | 272 | internal override init(_ shortFlag: String?, _ longFlag: String?, _ required: Bool, _ helpMessage: String) { 273 | super.init(shortFlag, longFlag, required, helpMessage) 274 | } 275 | 276 | /** Initializes a new Option that has both long and short flags. */ 277 | public convenience init(shortFlag: String, longFlag: String, required: Bool = false, helpMessage: String) { 278 | self.init(shortFlag as String?, longFlag, required, helpMessage) 279 | } 280 | 281 | /** Initializes a new Option that has only a short flag. */ 282 | public convenience init(shortFlag: String, required: Bool = false, helpMessage: String) { 283 | self.init(shortFlag as String?, nil, required, helpMessage) 284 | } 285 | 286 | /** Initializes a new Option that has only a long flag. */ 287 | public convenience init(longFlag: String, required: Bool = false, helpMessage: String) { 288 | self.init(nil, longFlag as String?, required, helpMessage) 289 | } 290 | 291 | override func setValue(_ values: [String]) -> Bool { 292 | if values.count == 0 { 293 | return false 294 | } 295 | 296 | if let v = T(rawValue: values[0]) { 297 | _value = v 298 | return true 299 | } 300 | 301 | return false 302 | } 303 | 304 | } 305 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/CommandLineKit/StringExtensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * StringExtensions.swift 3 | * Copyright (c) 2014 Ben Gollmer. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /* Required for localeconv(3) */ 19 | #if os(OSX) 20 | import Darwin 21 | #elseif os(Linux) 22 | import Glibc 23 | #endif 24 | 25 | internal extension String { 26 | /* Retrieves locale-specified decimal separator from the environment 27 | * using localeconv(3). 28 | */ 29 | private func _localDecimalPoint() -> Character { 30 | let locale = localeconv() 31 | if locale != nil { 32 | if let decimalPoint = locale?.pointee.decimal_point { 33 | return Character(UnicodeScalar(UInt32(decimalPoint.pointee))!) 34 | } 35 | } 36 | 37 | return "." 38 | } 39 | 40 | /** 41 | * Attempts to parse the string value into a Double. 42 | * 43 | * - returns: A Double if the string can be parsed, nil otherwise. 44 | */ 45 | func toDouble() -> Double? { 46 | var characteristic: String = "0" 47 | var mantissa: String = "0" 48 | var inMantissa: Bool = false 49 | var isNegative: Bool = false 50 | let decimalPoint = self._localDecimalPoint() 51 | 52 | let charactersEnumerator = self.characters.enumerated() 53 | for (i, c) in charactersEnumerator { 54 | if i == 0 && c == "-" { 55 | isNegative = true 56 | continue 57 | } 58 | 59 | if c == decimalPoint { 60 | inMantissa = true 61 | continue 62 | } 63 | 64 | if Int(String(c)) != nil { 65 | if !inMantissa { 66 | characteristic.append(c) 67 | } else { 68 | mantissa.append(c) 69 | } 70 | } else { 71 | /* Non-numeric character found, bail */ 72 | return nil 73 | } 74 | } 75 | 76 | let doubleCharacteristic = Double(Int(characteristic)!) 77 | return (doubleCharacteristic + 78 | Double(Int(mantissa)!) / pow(Double(10), Double(mantissa.characters.count - 1))) * 79 | (isNegative ? -1 : 1) 80 | } 81 | 82 | /** 83 | * Splits a string into an array of string components. 84 | * 85 | * - parameter by: The character to split on. 86 | * - parameter maxSplits: The maximum number of splits to perform. If 0, all possible splits are made. 87 | * 88 | * - returns: An array of string components. 89 | */ 90 | func split(by: Character, maxSplits: Int = 0) -> [String] { 91 | var s = [String]() 92 | var numSplits = 0 93 | 94 | var curIdx = self.startIndex 95 | for i in self.characters.indices { 96 | let c = self[i] 97 | if c == by && (maxSplits == 0 || numSplits < maxSplits) { 98 | s.append(String(self[curIdx.. String { 120 | var s = self 121 | var currentLength = self.characters.count 122 | 123 | while currentLength < width { 124 | s.append(padChar) 125 | currentLength += 1 126 | } 127 | 128 | return s 129 | } 130 | 131 | /** 132 | * Wraps a string to the specified width. 133 | * 134 | * This just does simple greedy word-packing, it doesn't go full Knuth-Plass. 135 | * If a single word is longer than the line width, it will be placed (unsplit) 136 | * on a line by itself. 137 | * 138 | * - parameter atWidth: The maximum length of a line. 139 | * - parameter wrapBy: The line break character to use. 140 | * - parameter splitBy: The character to use when splitting the string into words. 141 | * 142 | * - returns: A new string, wrapped at the given width. 143 | */ 144 | func wrapped(atWidth width: Int, wrapBy: Character = "\n", splitBy: Character = " ") -> String { 145 | var s = "" 146 | var currentLineWidth = 0 147 | 148 | for word in self.split(by: splitBy) { 149 | let wordLength = word.characters.count 150 | 151 | if currentLineWidth + wordLength + 1 > width { 152 | /* Word length is greater than line length, can't wrap */ 153 | if wordLength >= width { 154 | s += word 155 | } 156 | 157 | s.append(wrapBy) 158 | currentLineWidth = 0 159 | } 160 | 161 | currentLineWidth += wordLength + 1 162 | s += word 163 | s.append(splitBy) 164 | } 165 | 166 | return s 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/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 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/Package.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Package.swift 3 | * Copyright (c) 2015 Ben Gollmer. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import PackageDescription 19 | 20 | let package = Package(name: "CommandLine") 21 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/README.md: -------------------------------------------------------------------------------- 1 | # CommandLineKit [![Build Status](https://travis-ci.org/jatoben/CommandLine.svg?branch=master)](https://travis-ci.org/jatoben/CommandLine) 2 | 3 | A pure Swift library for creating command-line interfaces. 4 | 5 | *Note: CommandLineKit `master` requires Xcode 8 / Swift 3.0. If you're using older versions of Swift, please check out the [earlier releases](https://github.com/jatoben/CommandLine/releases).* 6 | 7 | ## Usage 8 | 9 | CommandLine aims to have a simple and self-explanatory API. 10 | 11 | ```swift 12 | import CommandLineKit 13 | 14 | let cli = CommandLineKit.CommandLine() 15 | 16 | let filePath = StringOption(shortFlag: "f", longFlag: "file", required: true, 17 | helpMessage: "Path to the output file.") 18 | let compress = BoolOption(shortFlag: "c", longFlag: "compress", 19 | helpMessage: "Use data compression.") 20 | let help = BoolOption(shortFlag: "h", longFlag: "help", 21 | helpMessage: "Prints a help message.") 22 | let verbosity = CounterOption(shortFlag: "v", longFlag: "verbose", 23 | helpMessage: "Print verbose messages. Specify multiple times to increase verbosity.") 24 | 25 | cli.addOptions(filePath, compress, help, verbosity) 26 | 27 | do { 28 | try cli.parse() 29 | } catch { 30 | cli.printUsage(error) 31 | exit(EX_USAGE) 32 | } 33 | 34 | print("File path is \(filePath.value!)") 35 | print("Compress is \(compress.value)") 36 | print("Verbosity is \(verbosity.value)") 37 | ``` 38 | 39 | See `Option.swift` for additional Option types. 40 | 41 | To use CommandLineKit in your project, add it to your workspace, then add CommandLineKit.framework to the __Build Phases / Link Binary With Libraries__ setting of your target. 42 | 43 | If you are building a command-line tool and need to embed this and other frameworks to it, follow the steps in http://colemancda.github.io/programming/2015/02/12/embedded-swift-frameworks-osx-command-line-tools/ to link Swift frameworks to your command-line tool. 44 | 45 | If you are building a standalone command-line tool, you'll need to add the CommandLineKit source files directly to your target, because Xcode [can't yet build static libraries that contain Swift code](https://github.com/ksm/SwiftInFlux#static-libraries). 46 | 47 | 48 | ## Features 49 | 50 | ### Automatically generated usage messages 51 | 52 | ``` 53 | Usage: example [options] 54 | -f, --file: 55 | Path to the output file. 56 | -c, --compress: 57 | Use data compression. 58 | -h, --help: 59 | Prints a help message. 60 | -v, --verbose: 61 | Print verbose messages. Specify multiple times to increase verbosity. 62 | ``` 63 | 64 | You can fully customize the usage message by supplying a `formatOutput` function. For example, [Rainbow](https://github.com/onevcat/Rainbow) provides a handy way to generate colorized output: 65 | 66 | ```swift 67 | import Rainbow 68 | 69 | cli.formatOutput = { s, type in 70 | var str: String 71 | switch(type) { 72 | case .Error: 73 | str = s.red.bold 74 | case .OptionFlag: 75 | str = s.green.underline 76 | case .OptionHelp: 77 | str = s.blue 78 | default: 79 | str = s 80 | } 81 | 82 | return cli.defaultFormat(str, type: type) 83 | } 84 | ``` 85 | 86 | ![](https://cloud.githubusercontent.com/assets/318083/12108437/1e3ec25c-b335-11e5-9cc9-d45ad3ab3dc7.png) 87 | 88 | ### Supports all common flag styles 89 | 90 | These command-lines are equivalent: 91 | 92 | ```bash 93 | $ ./example -c -v -f /path/to/file 94 | $ ./example -cvf /path/to/file 95 | $ ./example -c --verbose --file /path/to/file 96 | $ ./example -cv --file /path/to/file 97 | $ ./example --compress -v --file=/path/to/file 98 | ``` 99 | 100 | Option processing can be stopped with '--', [as in getopt(3)](https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html). 101 | 102 | ### Intelligent handling of negative int & float arguments 103 | 104 | This will pass negative 42 to the int option, and negative 3.1419 to the float option: 105 | 106 | ```bash 107 | $ ./example2 -i -42 --float -3.1419 108 | ``` 109 | 110 | ### Locale-aware float parsing 111 | 112 | Floats will be handled correctly when in a locale that uses an alternate decimal point character: 113 | 114 | ```bash 115 | $ LC_NUMERIC=sv_SE.UTF-8 ./example2 --float 3,1419 116 | ``` 117 | 118 | ### Type-safe Enum options 119 | 120 | ```swift 121 | enum Operation: String { 122 | case create = "c" 123 | case extract = "x" 124 | case list = "l" 125 | case verify = "v" 126 | } 127 | 128 | let cli = CommandLineKit.CommandLine() 129 | let op = EnumOption(shortFlag: "o", longFlag: "operation", required: true, 130 | helpMessage: "File operation - c for create, x for extract, l for list, or v for verify.") 131 | cli.setOptions(op) 132 | 133 | do { 134 | try cli.parse() 135 | } catch { 136 | cli.printUsage(error) 137 | exit(EX_USAGE) 138 | } 139 | 140 | switch op.value { 141 | case Operation.Create: 142 | // Create file 143 | 144 | case Operation.Extract: 145 | // Extract file 146 | 147 | // Remainder of cases 148 | } 149 | ``` 150 | 151 | Note: Enums must be initalizable from a String value. 152 | 153 | ### Fully emoji-compliant 154 | 155 | ```bash 156 | $ ./example3 -👍 --👻 157 | ``` 158 | 159 | *(please don't actually do this)* 160 | 161 | License 162 | ------- 163 | Copyright (c) 2014 Ben Gollmer. 164 | 165 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 166 | 167 | http://www.apache.org/licenses/LICENSE-2.0 168 | 169 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 170 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/Tests/CommandLineKitTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | NSHumanReadableCopyright 24 | Copyright © 2014 Ben Gollmer. Licensed under the Apache License, Version 2.0. 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/Tests/CommandLineKitTests/StringExtensionTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * StringExtensionTests.swift 3 | * Copyright (c) 2014 Ben Gollmer. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import XCTest 19 | @testable import CommandLineKit 20 | #if os(OSX) 21 | import Darwin 22 | #elseif os(Linux) 23 | import Glibc 24 | #endif 25 | 26 | class StringExtensionTests: XCTestCase { 27 | static var allTests : [(String, (StringExtensionTests) -> () throws -> Void)] { 28 | return [ 29 | ("testToDouble", testToDouble), 30 | ("testSplit", testSplit), 31 | ("testPadded", testPadded), 32 | ("testWrapped", testWrapped), 33 | ] 34 | } 35 | 36 | override func setUp() { 37 | /* set locale to "C" to start with '.' as the decimal separator */ 38 | setlocale(LC_ALL, "C") 39 | } 40 | 41 | func testToDouble() { 42 | /* Regular ol' double */ 43 | let a = "3.14159".toDouble() 44 | XCTAssertEqual(a, 3.14159, "Failed to parse pi as double") 45 | 46 | let b = "-98.23".toDouble() 47 | XCTAssertEqual(b, -98.23, "Failed to parse negative double") 48 | 49 | /* Ints should be parsable as doubles */ 50 | let c = "5".toDouble() 51 | XCTAssertEqual(c, 5, "Failed to parse int as double") 52 | 53 | let d = "-2099".toDouble() 54 | XCTAssertEqual(d, -2099, "Failed to parse negative int as double") 55 | 56 | 57 | /* Zero handling */ 58 | let e = "0.0".toDouble() 59 | XCTAssertEqual(e, 0, "Failed to parse zero double") 60 | 61 | let f = "0".toDouble() 62 | XCTAssertEqual(f, 0, "Failed to parse zero int as double") 63 | 64 | let g = "0.0000000000000000".toDouble() 65 | XCTAssertEqual(g, 0, "Failed to parse very long zero double") 66 | 67 | let h = "-0.0".toDouble() 68 | XCTAssertEqual(h, 0, "Failed to parse negative zero double") 69 | 70 | let i = "-0".toDouble() 71 | XCTAssertEqual(i, 0, "Failed to parse negative zero int as double") 72 | 73 | let j = "-0.000000000000000".toDouble() 74 | XCTAssertEqual(j, 0, "Failed to parse very long negative zero double") 75 | 76 | 77 | /* Various extraneous chars */ 78 | let k = "+42.3".toDouble() 79 | XCTAssertNil(k, "Parsed double with extraneous +") 80 | 81 | let l = " 827.2".toDouble() 82 | XCTAssertNil(l, "Parsed double with extraneous space") 83 | 84 | let m = "283_3".toDouble() 85 | XCTAssertNil(m, "Parsed double with extraneous _") 86 | 87 | let n = "💩".toDouble() 88 | XCTAssertNil(n, "Parsed poo") 89 | 90 | /* Locale handling */ 91 | setlocale(LC_NUMERIC, "sv_SE.UTF-8") 92 | 93 | let o = "888,8".toDouble() 94 | XCTAssertEqual(o!, 888.8, "Failed to parse double in alternate locale") 95 | 96 | let p = "888.8".toDouble() 97 | XCTAssertNil(p, "Parsed double in alternate locale with wrong decimal point") 98 | 99 | /* Set locale back so as not to perturb any other tests */ 100 | setlocale(LC_NUMERIC, "") 101 | } 102 | 103 | func testSplit() { 104 | let a = "1,2,3".split(by: ",") 105 | XCTAssertEqual(a.count, 3, "Failed to split into correct number of components") 106 | 107 | let b = "123".split(by: ",") 108 | XCTAssertEqual(b.count, 1, "Failed to split when separator not found") 109 | 110 | let c = "".split(by: ",") 111 | XCTAssertEqual(c.count, 0, "Splitting empty string should return empty array") 112 | 113 | let e = "a-b-c-d".split(by: "-", maxSplits: 2) 114 | XCTAssertEqual(e.count, 3, "Failed to limit splits") 115 | XCTAssertEqual(e[0], "a", "Invalid value for split 1") 116 | XCTAssertEqual(e[1], "b", "Invalid value for split 2") 117 | XCTAssertEqual(e[2], "c-d", "Invalid value for last split") 118 | } 119 | 120 | func testPadded() { 121 | let a = "this is a test" 122 | 123 | XCTAssertEqual(a.padded(toWidth: 80).characters.count, 124 | 80, "Failed to pad to correct width") 125 | XCTAssertEqual(a.padded(toWidth: 5).characters.count, 126 | a.characters.count, "Bad padding when pad width is less than string width") 127 | XCTAssertEqual(a.padded(toWidth: -2).characters.count, 128 | a.characters.count, "Bad padding with negative pad width") 129 | 130 | let b = a.padded(toWidth: 80) 131 | let lastBCharIndex = b.index(before: b.endIndex) 132 | XCTAssertEqual(b[lastBCharIndex], " " as Character, "Failed to pad with default character") 133 | 134 | let c = a.padded(toWidth: 80, with: "+") 135 | let lastCCharIndex = c.index(before: b.endIndex) 136 | XCTAssertEqual(c[lastCCharIndex], "+" as Character, "Failed to pad with specified character") 137 | } 138 | 139 | func testWrapped() { 140 | let lipsum = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." 141 | for line in lipsum.wrapped(atWidth: 80).split(by: "\n") { 142 | XCTAssertLessThanOrEqual(line.characters.count, 80, "Failed to wrap long line: \(line)") 143 | } 144 | 145 | /* Words longer than the wrap width should not be split */ 146 | let longWords = "Lorem ipsum consectetur adipisicing eiusmod tempor incididunt" 147 | let lines = longWords.wrapped(atWidth: 3).split(by: "\n") 148 | XCTAssertEqual(lines.count, 8, "Failed to wrap long words") 149 | for line in lines { 150 | XCTAssertGreaterThan(line.characters.count, 3, "Bad long word wrapping on line: \(line)") 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * LinuxMain.swift 3 | * Copyright (c) 2015 Ben Gollmer. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import XCTest 19 | @testable import CommandLineKitTests 20 | 21 | XCTMain([testCase(CommandLineTests.allTests), testCase(StringExtensionTests.allTests)]) 22 | -------------------------------------------------------------------------------- /XCAssetPacker/CommandLine/install-linux-swift.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | SWIFT_SNAPSHOT="swift-3.0.1-RELEASE" 4 | XCTEST_SNAPSHOT="swift-3.0.1-RELEASE" 5 | 6 | echo "Installing ${SWIFT_SNAPSHOT}..." 7 | if [ ! -f "${SWIFT_SNAPSHOT}-ubuntu14.04.tar.gz" ]; then 8 | curl -s -L -O "https://swift.org/builds/$(echo $SWIFT_SNAPSHOT | tr A-Z a-z)/ubuntu1404/${SWIFT_SNAPSHOT}/${SWIFT_SNAPSHOT}-ubuntu14.04.tar.gz" 9 | fi 10 | 11 | tar -zxf "${SWIFT_SNAPSHOT}-ubuntu14.04.tar.gz" 12 | sudo rm -rf /swift 13 | sudo mv "${SWIFT_SNAPSHOT}-ubuntu14.04" /swift 14 | 15 | # Force the use of the gold linker 16 | # See https://bugs.swift.org/browse/SR-1023 and https://github.com/apple/swift/pull/2609 17 | sudo rm /usr/bin/ld 18 | sudo ln -s /usr/bin/ld.gold /usr/bin/ld 19 | 20 | echo "Installing XCTest..." 21 | if [ ! -f "${XCTEST_SNAPSHOT}.tar.gz" ]; then 22 | curl -s -L -O "https://github.com/apple/swift-corelibs-xctest/archive/${XCTEST_SNAPSHOT}.tar.gz" 23 | fi 24 | tar -zxvf "${XCTEST_SNAPSHOT}.tar.gz" 25 | cd "swift-corelibs-xctest-${XCTEST_SNAPSHOT}" 26 | sudo ./build_script.py --swiftc="/swift/usr/bin/swiftc" --build-dir="/tmp/XCTest_build" --foundation-build-dir="/swift/usr/lib/swift/linux" --library-install-path="/swift/usr/lib/swift/linux" --module-install-path="/swift/usr/lib/swift/linux/x86_64" 27 | cd .. 28 | rm -rf "swift-corelibs-xctest-${XCTEST_SNAPSHOT}" 29 | -------------------------------------------------------------------------------- /XCAssetPacker/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // XCAssetPacker 4 | // 5 | // Created by Harry Jordan on 16/04/2017. 6 | // Copyright © 2017 Inquisitive Software. All rights reserved. 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 | import Foundation 22 | 23 | 24 | enum Configuration: String { 25 | case appIcon = "app-icon" 26 | case pattern = "pattern" 27 | case multiplePatterns = "patterns" 28 | case includeImages = "include-images" 29 | case skipImages = "skip-images" 30 | case images = "images" 31 | case filename = "filename" 32 | case idiom = "idiom" 33 | case scale = "scale" 34 | case size = "size" 35 | case screenWidth = "screen-width" 36 | case prerendered = "pre-rendered" 37 | case info = "info" 38 | case base = "base" 39 | case devices = "devices" 40 | case deviceType = "device-type" 41 | case properties = "properties" 42 | case custom = "custom" 43 | case universal = "universal" 44 | case validImageExtensions = "valid-image-extensions" 45 | } 46 | 47 | 48 | enum FileExtension: String { 49 | case imageSet = "imageset" 50 | case appIconSet = "appiconset" 51 | case json = "json" 52 | case assetPackage = "xcassets" 53 | case swift = "swift" 54 | } 55 | 56 | 57 | enum FileName: String { 58 | case appIcon = "AppIcon" 59 | case contents = "Contents.json" 60 | } 61 | 62 | 63 | enum ImageScales: String { 64 | case appIcon = "AppIcon" 65 | case contents = "Contents.json" 66 | } 67 | 68 | 69 | extension Dictionary where Key == String { 70 | 71 | func value(for key: Configuration) -> Value? { 72 | return self[key.rawValue] 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /XCAssetPacker/DeviceIdiom.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceIdiom.swift 3 | // XCAssetPacker 4 | // 5 | // Created by Harry Jordan on 04/09/2017. 6 | // Copyright © 2017 Inquisitive Software. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public enum DeviceIdiom: String { 13 | case watch, iPhone, iPad, iOSMarketing 14 | 15 | static var all: [DeviceIdiom] = [watch, iPhone, iPad, iOSMarketing] 16 | 17 | 18 | init?(_ idiomString: String) { 19 | let idiom = DeviceIdiom.all.first(where: { 20 | $0.idiomString.caseInsensitiveCompare(idiomString) == .orderedSame 21 | }) 22 | 23 | if let idiom = idiom { 24 | self = idiom 25 | } else { 26 | return nil 27 | } 28 | } 29 | 30 | 31 | var idiomString: String { 32 | // Keys used for xcasset properties 33 | switch self { 34 | case .watch: 35 | return "watch" 36 | 37 | case .iPhone: 38 | return "iphone" 39 | 40 | case .iPad: 41 | return "ipad" 42 | 43 | case .iOSMarketing: 44 | return "ios-marketing" 45 | } 46 | } 47 | 48 | 49 | var configurationKey: String { 50 | // Keys used to match strings in the configuration .json 51 | return self.rawValue 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /XCAssetPacker/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // XCAssetPacker 4 | // 5 | // Created by Harry Jordan on 23/11/2016. 6 | // Copyright © 2016 Inquisitive Software. All rights reserved. 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 | import Foundation 22 | 23 | 24 | extension String { 25 | 26 | func isMatchedBy(_ patterns: [String]) -> Bool { 27 | for pattern in patterns { 28 | if isMatchedBy(pattern) { 29 | return true 30 | } 31 | } 32 | 33 | return false 34 | } 35 | 36 | 37 | func isMatchedBy(_ pattern: String) -> Bool { 38 | if let regex = try? NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) { 39 | return regex.numberOfMatches(in: self, options: [], range: self.asNSRange) > 0 40 | } 41 | 42 | return false 43 | } 44 | 45 | 46 | var asNSRange: NSRange { 47 | return NSRange(location: 0, length: (self as NSString).length) 48 | } 49 | 50 | } 51 | 52 | 53 | extension URL { 54 | 55 | func appending(pathComponents: [String]) -> URL { 56 | var url = self 57 | 58 | for pathComponent in pathComponents { 59 | url = url.appendingPathComponent(pathComponent) 60 | } 61 | 62 | return url 63 | } 64 | 65 | } 66 | 67 | 68 | // The MIT License (MIT) 69 | // 70 | // Copyright (c) 2014 Ankur Patel 71 | // 72 | // Permission is hereby granted, free of charge, to any person obtaining a copy 73 | // of this software and associated documentation files (the "Software"), to deal 74 | // in the Software without restriction, including without limitation the rights 75 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 76 | // copies of the Software, and to permit persons to whom the Software is 77 | // furnished to do so, subject to the following conditions: 78 | // 79 | // The above copyright notice and this permission notice shall be included in all 80 | // copies or substantial portions of the Software. 81 | // 82 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 83 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 84 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 85 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 86 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 87 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 88 | // SOFTWARE. 89 | // 90 | // https://github.com/ankurp/Cent/blob/master/Sources/Dictionary.swift 91 | 92 | 93 | extension Dictionary { 94 | 95 | mutating func merge(dict: [K: V]) { 96 | for (k, v) in dict { 97 | self.updateValue(v as! Value, forKey: k as! Key) 98 | } 99 | } 100 | 101 | } 102 | 103 | -------------------------------------------------------------------------------- /XCAssetPacker/ImageProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageProperties.swift 3 | // XCAssetPacker 4 | // 5 | // Created by Harry Jordan on 23/11/2016. 6 | // Copyright © 2016 Inquisitive Software. All rights reserved. 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 | import Foundation 22 | 23 | 24 | struct ImageProperties { 25 | let name: String 26 | let type: ImageType 27 | let scale: ImageScale 28 | let prerendered: Bool? 29 | let configuration: [String: Any] 30 | 31 | 32 | init(from fileName: String, isAppIcon: Bool, configuration: [String: Any]) { 33 | let sourceImageName = (fileName as NSString).deletingPathExtension 34 | 35 | var adaptedName: String = sourceImageName 36 | var type: ImageType = .universal 37 | var scale: ImageScale = .universal 38 | 39 | // Look for an image scale e.g. @2x or @3x 40 | if let match = ImageProperties.scaleRegularExpression.firstMatch(in: adaptedName, range: adaptedName.asNSRange), match.numberOfRanges >= 2 { 41 | let rangeOfDigits = match.range(at: 1) 42 | let digitString = (adaptedName as NSString).substring(with: rangeOfDigits) 43 | 44 | if let scaleNumber = Int(digitString) { 45 | adaptedName = (sourceImageName as NSString).substring(to: match.range.location) 46 | scale = .scale(scaleNumber) 47 | } 48 | } 49 | 50 | // Parse standardized image extensions 51 | for (suffix, detectedImageType) in ImageProperties.typesForSuffixes { 52 | if let range = adaptedName.range(of: suffix, options: [.anchored, .backwards]) { 53 | type = detectedImageType 54 | adaptedName = sourceImageName.substring(to: range.lowerBound) 55 | break 56 | } 57 | } 58 | 59 | if isAppIcon { 60 | if let appIcon = configuration.value(for: .appIcon) as? [String: Any], 61 | let prerendered = appIcon.value(for: .prerendered) as? Bool { 62 | self.prerendered = prerendered 63 | } else { 64 | // Default to prerendered 65 | self.prerendered = true 66 | } 67 | } else { 68 | self.prerendered = nil 69 | } 70 | 71 | self.name = adaptedName 72 | self.type = type 73 | self.scale = scale 74 | self.configuration = configuration 75 | } 76 | 77 | 78 | static var scaleRegularExpression: NSRegularExpression = { 79 | let regularExpression = try! NSRegularExpression(pattern: "@(\\d)[xX]$", options: []) 80 | return regularExpression 81 | }() 82 | 83 | 84 | static var typesForSuffixes: [(String, ImageType)] { 85 | return ImageType.all.flatMap { (type) in 86 | if let suffix = type.suffix { 87 | return (suffix, type) 88 | } else { 89 | return nil 90 | } 91 | } 92 | } 93 | 94 | 95 | var scaleString: String? { 96 | switch type { 97 | case .watch, .watch38, .watch42: 98 | return "2x" 99 | 100 | default: 101 | if let scaleString = scale.scaleString { 102 | // See if the scale is identifiable from the filename 103 | return scaleString 104 | } else if let base = configuration.value(for: .base) as? [String: Any], 105 | let scaleString = base.value(for: .scale) as? String { 106 | // Look for a base scale in the configuration file 107 | return scaleString 108 | } else { 109 | // Otherwise default to 1x 110 | return "1x" 111 | } 112 | } 113 | } 114 | 115 | 116 | var idiom: String? { 117 | if let idiom = type.idiom { 118 | return idiom 119 | } 120 | 121 | if let base = configuration.value(for: .base) as? [String: Any], 122 | let idiomString = base.value(for: .idiom) as? String, 123 | let deviceIdiom = DeviceIdiom(idiomString) { 124 | return deviceIdiom.idiomString 125 | } 126 | 127 | return Configuration.universal.rawValue 128 | } 129 | 130 | } 131 | 132 | 133 | enum ImageType { 134 | case watch, watch38, watch42, universal, notification, settings, spotlight, iPhoneAppIcon, iPadAppIcon, iPadProAppIcon, iTunesPreview 135 | 136 | 137 | static var all: [ImageType] { 138 | return [watch, watch38, watch42, universal, notification, settings, spotlight, iPhoneAppIcon, iPadAppIcon, iPadProAppIcon, iTunesPreview] 139 | } 140 | 141 | 142 | var idiom: String? { 143 | let idiom: DeviceIdiom? 144 | 145 | switch self { 146 | case .watch, .watch38, .watch42: 147 | idiom = .watch 148 | 149 | case .iPhoneAppIcon: 150 | idiom = .iPhone 151 | 152 | case .iPadAppIcon, .iPadProAppIcon: 153 | idiom = .iPad 154 | 155 | case .iTunesPreview: 156 | idiom = .iOSMarketing 157 | 158 | default: 159 | idiom = nil 160 | } 161 | 162 | return idiom?.idiomString 163 | } 164 | 165 | 166 | var screenWidth: String? { 167 | switch self { 168 | case .watch: 169 | return nil 170 | 171 | case .watch38: 172 | return "<=145" 173 | 174 | case .watch42: 175 | return ">145" 176 | 177 | default: 178 | return nil 179 | } 180 | } 181 | 182 | 183 | var deviceType: String { 184 | // Used for matching with the configuration file 185 | switch self { 186 | case .watch, .watch38, .watch42: 187 | return DeviceIdiom.watch.configurationKey 188 | 189 | case .iPhoneAppIcon: 190 | return DeviceIdiom.iPhone.configurationKey 191 | 192 | case .iPadAppIcon, .iPadProAppIcon: 193 | return DeviceIdiom.iPad.configurationKey 194 | 195 | default: 196 | return "universal" 197 | } 198 | } 199 | 200 | 201 | var suffix: String? { 202 | switch self { 203 | // Watch types 204 | case .watch: 205 | return nil 206 | 207 | case .watch42: 208 | return "-42" 209 | 210 | case .watch38: 211 | return "-38" 212 | 213 | case .notification: 214 | return "-20" 215 | 216 | case .settings: 217 | return "-29" 218 | 219 | case .spotlight: 220 | return "-40" 221 | 222 | case .iPhoneAppIcon: 223 | return "-60" 224 | 225 | case .iPadAppIcon: 226 | return "-76" 227 | 228 | case .iPadProAppIcon: 229 | return "-83.5" 230 | 231 | case .iTunesPreview: 232 | return "-1024" 233 | 234 | case .universal: 235 | return nil 236 | } 237 | } 238 | 239 | 240 | var sizeString: String? { 241 | switch self { 242 | case .notification: 243 | return "20x20" 244 | 245 | case .settings: 246 | return "29x29" 247 | 248 | case .spotlight: 249 | return "40x40" 250 | 251 | case .iPhoneAppIcon: 252 | return "60x60" 253 | 254 | case .iPadAppIcon: 255 | return "76x76" 256 | 257 | case .iPadProAppIcon: 258 | return "83.5x83.5" 259 | 260 | case .iTunesPreview: 261 | return "1024x1024" 262 | 263 | default: 264 | return nil 265 | } 266 | } 267 | 268 | } 269 | 270 | 271 | enum ImageScale { 272 | case scale(Int), universal 273 | 274 | var scaleString: String? { 275 | switch self { 276 | case .scale(let scale): 277 | return "\(scale)x" 278 | 279 | case .universal: 280 | return nil 281 | } 282 | } 283 | } 284 | 285 | -------------------------------------------------------------------------------- /XCAssetPacker/SwiftGeneration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftGeneration.swift 3 | // XCAssetPacker 4 | // 5 | // Created by Harry Jordan on 23/11/2016. 6 | // Copyright © 2016 Inquisitive Software. All rights reserved. 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 | import Foundation 22 | 23 | 24 | enum SwiftGenerationError: Error { 25 | case duplicateProperty(name: String, originalFileName: String) 26 | } 27 | 28 | 29 | 30 | enum SwiftTarget { 31 | case cocoa, iOS, watch 32 | 33 | var libraryName: String { 34 | switch self { 35 | case .cocoa: 36 | return "Cocoa" 37 | 38 | case .iOS: 39 | return "UIKit" 40 | 41 | case .watch: 42 | return "ClockKit" 43 | } 44 | } 45 | 46 | var imageClassName: String { 47 | switch self { 48 | case .cocoa: 49 | return "NSImage" 50 | 51 | case .iOS, .watch: 52 | return "UIImage" 53 | } 54 | } 55 | 56 | } 57 | 58 | 59 | extension AssetCatalogGenerator { 60 | 61 | func swiftCode(forTarget target: SwiftTarget, rootNode node: Node) throws -> String { 62 | let result = try generateSwiftCode(forTarget: target, node: node, depth: 0) 63 | 64 | return header(forTarget: target) + result.code 65 | } 66 | 67 | 68 | func generateSwiftCode(forTarget target: SwiftTarget, node: Node, depth: Int) throws -> (code: String, propertyNames: [String]) { 69 | // Code Indents 70 | let topLevel = String(repeating: " ", count: depth) 71 | let firstIndent = String(repeating: " ", count: depth + 1) 72 | let secondIndent = String(repeating: " ", count: depth + 2) 73 | 74 | // Group name 75 | let structName: String 76 | let groupPropertyName: String? 77 | 78 | if depth == 0 { 79 | structName = "ImageAssetCatalog" 80 | groupPropertyName = nil 81 | } else { 82 | structName = "ImageAssetCatalog" + node.name 83 | 84 | let groupName = node.name.llamaCase() 85 | groupPropertyName = groupName 86 | } 87 | 88 | var generatedCode = "" 89 | var propertyNames: [String] = [] 90 | 91 | if let groupPropertyName = groupPropertyName { 92 | // Add a var to the struct to accesss the images in this group 93 | generatedCode += topLevel + "var \(groupPropertyName) = \(structName)()\n" 94 | } 95 | 96 | generatedCode += topLevel + "struct \(structName) {\n" 97 | 98 | if depth == 0 { 99 | generatedCode += firstIndent + "private func image(named name: String) -> \(target.imageClassName) {\n" + 100 | secondIndent + "// Force unwrapping here as it seems reasonable to assume the image exists\n" + 101 | secondIndent + "// since the .xcassets package was generated in tandem with this code\n" + 102 | secondIndent + "return \(target.imageClassName)(named: name)!\n" + 103 | firstIndent + "}\n\n" 104 | } 105 | 106 | // Sort using the numeric option, so that numbered image sets appear in a logical order 107 | let childNodes = node.children.sorted(by: { (firstNode, secondNode) -> Bool in 108 | let firstProperty = firstNode.swiftPropertyName(withinGroup: groupPropertyName) 109 | let secondProperty = secondNode.swiftPropertyName(withinGroup: groupPropertyName) 110 | 111 | let comparison = firstProperty.compare(secondProperty, options: [.caseInsensitive, .numeric, .widthInsensitive, .forcedOrdering], locale: Locale.current) 112 | return comparison == .orderedAscending 113 | }) 114 | 115 | for child in childNodes where child.isDirectory && child.isImageSet && !child.isAppIcon { 116 | let propertyName = child.swiftPropertyName(withinGroup: groupPropertyName) 117 | 118 | if propertyNames.contains(propertyName) { 119 | throw SwiftGenerationError.duplicateProperty(name: propertyName, originalFileName: child.name) 120 | } else { 121 | propertyNames.append(propertyName) 122 | 123 | generatedCode += firstIndent + "var \(propertyName): \(target.imageClassName) { return image(named: \"\(child.swiftCatalogName)\") }\n" 124 | } 125 | } 126 | 127 | for child in node.children where child.isDirectory && !child.isImageSet && !child.isAppIcon { 128 | let result = try self.generateSwiftCode(forTarget: target, node: child, depth: depth + 1) 129 | generatedCode += result.code 130 | 131 | propertyNames.append(contentsOf: result.propertyNames) 132 | } 133 | 134 | generatedCode += topLevel + "}\n\n" 135 | 136 | return (generatedCode, propertyNames) 137 | } 138 | 139 | 140 | func header(forTarget target: SwiftTarget) -> String { 141 | let dateFormatter = DateFormatter() 142 | dateFormatter.dateFormat = "YYYY-MM-dd" 143 | 144 | let dateString = dateFormatter.string(from: Date()) 145 | 146 | let header = "// *********************************************************\n" + 147 | "// *********************************************************\n" + 148 | "// ***\n" + 149 | "// *** DO NOT EDIT\n" + 150 | "// *** This file is machine-generated by XCAssetPacker\n" + 151 | "// *** and is intended to be overwritten regularly\n" + 152 | "// ***\n" + 153 | "// *** Last updated: \(dateString)\n" + 154 | "// ***\n" + 155 | "// *********************************************************\n" + 156 | "// *********************************************************\n\n" + 157 | "import \(target.libraryName)\n\n" + 158 | "extension \(target.imageClassName) {\n" + 159 | " static let assets = ImageAssetCatalog()\n" + 160 | " static let r = ImageAssetCatalog()\n" + 161 | "}\n\n" 162 | 163 | return header 164 | } 165 | 166 | } 167 | 168 | 169 | extension Node { 170 | 171 | func swiftPropertyName(withinGroup groupName: String?) -> String { 172 | var name = swiftCatalogName 173 | 174 | if let groupName = groupName, 175 | let matchingRange = name.range(of: groupName, options: [.anchored, .caseInsensitive]) { 176 | let uniqueSubstring = name.substring(from: matchingRange.upperBound) 177 | name = uniqueSubstring 178 | } 179 | 180 | name = name.llamaCase() 181 | 182 | // Append a prefix as digits aren't allowed as the fist character of a variable 183 | if let firstCharacter = name.unicodeScalars.first, CharacterSet.decimalDigits.contains(firstCharacter) { 184 | let prefix = "i" 185 | name = prefix + name 186 | } 187 | 188 | return name 189 | } 190 | 191 | 192 | var swiftCatalogName: String { 193 | return (name as NSString).deletingPathExtension 194 | } 195 | 196 | } 197 | 198 | 199 | extension String { 200 | // Adapted from: https://gist.github.com/AmitaiB/bbfcba3a21411ee6d3f972320bcd1ecd 201 | 202 | func llamaCase() -> String { 203 | // 204 | var adaptedText: String = "" 205 | var isFirstLetter = true 206 | 207 | self.enumerateSubstrings(in: startIndex.. String in 146 | return combined + "/" + pathComponent 147 | } 148 | 149 | print("Created assets package \(lastPathComponents) containing \(log.numberOfImages) images") 150 | exit(EXIT_SUCCESS) 151 | } catch let error as AssetCatalogError { 152 | switch error { 153 | case .ioError(let description): 154 | print(description) 155 | exit(EX_IOERR) 156 | } 157 | } catch let error as SwiftGenerationError { 158 | switch error { 159 | case .duplicateProperty(let name, originalFileName: let originalFileName): 160 | print("Conflicting property '\(name)' for \(originalFileName)") 161 | print("Images need to have distinct llama case representations") 162 | exit(EX_IOERR) 163 | } 164 | } catch let error { 165 | print("Unexpected error: \(String(describing: error))") 166 | exit(EX_IOERR) 167 | } 168 | --------------------------------------------------------------------------------