├── XCAssetPacker Tests ├── Watch │ ├── Source │ │ ├── Bed-38.png │ │ ├── Bed-42.png │ │ ├── Moon-38.png │ │ ├── Moon-42.png │ │ ├── Sun-38.png │ │ ├── Sun-42.png │ │ ├── Icons.sketch │ │ ├── App Icon.sketch │ │ ├── Watch App Icon.png │ │ ├── ReminderSelected-38.png │ │ ├── ReminderSelected-42.png │ │ ├── ReminderUnselected-38.png │ │ ├── ReminderUnselected-42.png │ │ ├── ColorComplicationIndicator-38.png │ │ ├── ColorComplicationIndicator-42.png │ │ ├── Watch App Short Look Icon-38.png │ │ ├── Watch App Short Look Icon-42.png │ │ ├── Watch App Notification Icon-38.png │ │ ├── Watch App Notification Icon-42.png │ │ ├── GrayscaleComplicationIndicator-38.png │ │ ├── GrayscaleComplicationIndicator-42.png │ │ ├── Watch App Rules.json │ │ └── Complication Rules.json │ ├── Watch │ │ └── Images │ │ │ ├── WakeTime │ │ │ ├── WakeTime0.png │ │ │ ├── WakeTime1.png │ │ │ ├── WakeTime2.png │ │ │ ├── WakeTime3.png │ │ │ ├── WakeTime4.png │ │ │ ├── WakeTime5.png │ │ │ ├── WakeTime6.png │ │ │ ├── WakeTime7.png │ │ │ ├── WakeTime8.png │ │ │ ├── WakeTime9.png │ │ │ ├── WakeTime10.png │ │ │ ├── WakeTime11.png │ │ │ ├── WakeTime12.png │ │ │ ├── WakeTime13.png │ │ │ ├── WakeTime14.png │ │ │ ├── WakeTime15.png │ │ │ ├── WakeTime16.png │ │ │ ├── WakeTime17.png │ │ │ ├── WakeTime18.png │ │ │ ├── WakeTime19.png │ │ │ ├── WakeTime20.png │ │ │ ├── WakeTime21.png │ │ │ ├── WakeTime22.png │ │ │ ├── WakeTime23.png │ │ │ ├── WakeTime24.png │ │ │ ├── WakeTime25.png │ │ │ ├── WakeTime26.png │ │ │ ├── WakeTime27.png │ │ │ ├── WakeTime28.png │ │ │ ├── WakeTime29.png │ │ │ ├── WakeTime30.png │ │ │ ├── WakeTime31.png │ │ │ ├── WakeTime32.png │ │ │ ├── WakeTime33.png │ │ │ ├── WakeTime34.png │ │ │ ├── WakeTime35.png │ │ │ ├── WakeTime36.png │ │ │ ├── WakeTime37.png │ │ │ ├── WakeTime38.png │ │ │ ├── WakeTime39.png │ │ │ ├── WakeTime40.png │ │ │ ├── WakeTime41.png │ │ │ ├── WakeTime42.png │ │ │ ├── WakeTime43.png │ │ │ ├── WakeTime44.png │ │ │ ├── WakeTime45.png │ │ │ ├── WakeTime46.png │ │ │ ├── WakeTime47.png │ │ │ ├── WakeTime48.png │ │ │ ├── WakeTime49.png │ │ │ ├── WakeTime50.png │ │ │ ├── WakeTime51.png │ │ │ └── WakeTime52.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 │ │ │ ├── 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 │ │ │ ├── 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 │ │ │ └── UtilitarianExtraLargeCircle │ │ │ ├── UtilitarianExtraLargeCircle0-38.png │ │ │ └── UtilitarianExtraLargeCircle0-42.png │ ├── generate.sh │ └── Image Generation.xcodeproj │ │ └── project.pbxproj ├── Complication Rules.json ├── Skip Circles Rules.json ├── Info.plist └── XCAssetPacker_Tests.swift ├── XCAssetPacker ├── CommandLine │ ├── .gitignore │ ├── .travis.yml │ ├── Package.swift │ ├── Tests │ │ ├── LinuxMain.swift │ │ └── CommandLineKitTests │ │ │ ├── Info.plist │ │ │ └── StringExtensionTests.swift │ ├── CommandLineKit │ │ ├── Info.plist │ │ ├── StringExtensions.swift │ │ ├── Option.swift │ │ └── CommandLine.swift │ ├── install-linux-swift.sh │ ├── CommandLineKit.xcodeproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── CommandLine.xcscheme │ ├── README.md │ └── LICENSE ├── DeviceIdiom.swift ├── Constants.swift ├── AssetCatalogGenerator+Creation.swift ├── Extensions.swift ├── main.swift ├── ImageProperties.swift ├── SwiftGeneration.swift └── AssetCatalogGenerator.swift ├── Examples ├── Example Configuration.json └── Annotated Configuration.json ├── License.txt ├── README.md └── XCAssetPacker.xcodeproj └── project.pbxproj /XCAssetPacker Tests/Watch/Source/Bed-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Bed-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Bed-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Bed-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Moon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Moon-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Moon-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Moon-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Sun-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Sun-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Sun-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Sun-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Icons.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Icons.sketch -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/App Icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/App Icon.sketch -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/Watch App Icon.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderSelected-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/ReminderSelected-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderSelected-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/ReminderSelected-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderUnselected-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/ReminderUnselected-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ReminderUnselected-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/ReminderUnselected-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime0.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime1.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime2.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime3.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime4.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime5.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime6.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime7.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime8.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime9.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime10.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime11.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime12.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime13.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime14.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime15.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime16.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime17.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime18.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime19.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime20.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime21.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime22.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime23.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime24.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime25.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime26.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime27.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime28.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime29.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime30.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime31.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime32.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime33.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime34.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime35.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime36.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime37.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime39.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime40.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime41.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime43.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime44.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime45.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime46.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime47.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime48.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime49.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime50.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime51.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/WakeTime/WakeTime52.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/ColorComplicationIndicator-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Short Look Icon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/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/HEAD/XCAssetPacker Tests/Watch/Source/Watch App Short Look Icon-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/Watch App Notification Icon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/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/HEAD/XCAssetPacker Tests/Watch/Source/Watch App Notification Icon-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Source/GrayscaleComplicationIndicator-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/Watch/Images/SleepDuration/SleepDuration0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration2-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration3-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/SleepDuration/SleepDuration4-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle2-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle3-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ModularSmallCircle/ModularSmallCircle4-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/CircularSmallCircle/CircularSmallCircle2-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview0-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/ComplicationPreview/ComplicationPreview1-42.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-38.png -------------------------------------------------------------------------------- /XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inquisitiveSoft/XCAssetPacker/HEAD/XCAssetPacker Tests/Watch/Watch/Images/UtilitarianExtraLargeCircle/UtilitarianExtraLargeCircle0-42.png -------------------------------------------------------------------------------- /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 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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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 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 | -------------------------------------------------------------------------------- /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/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/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/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/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/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.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 | import Foundation 24 | import Cocoa 25 | 26 | 27 | let versionNumber = "1.2" 28 | 29 | // Setup command line options 30 | let inputPathOption = StringOption(shortFlag: "i", longFlag: "input", helpMessage: "Path to the input folder.") 31 | let configurationOption = StringOption(shortFlag: "c", longFlag: "config", required: false, helpMessage: "The location of a json configuration file.\n If none is specified then uses sensible defaults.") 32 | let outputPathOption = StringOption(shortFlag: "o", longFlag: "output", helpMessage: "Path to the output file or folder.\n If a folder is given then an Assets.xcassets package will be created inside it.") 33 | let swiftDestinationOption = StringOption(longFlag: "swift", helpMessage: "Path to the output swift file or folder.\n If a folder is given then an Images.swift file will be created inside it.") 34 | let overwriteOption = BoolOption(shortFlag: "f", longFlag: "force", helpMessage: "Overwrite any existing .xcassets package or Swift file.") 35 | 36 | // Target 37 | let swiftTargetMacOption = BoolOption(longFlag: "mac", helpMessage: "Set the target for generated Swift to use Cocoa.") 38 | let swiftTargetiOSOption = BoolOption(longFlag: "iOS", helpMessage: "Set the target for generated Swift to use UIKit.") 39 | let swiftTargetWatchOption = BoolOption(longFlag: "watch", helpMessage: "Set the target for generated Swift to use WatchKit.") 40 | 41 | let versionOption = BoolOption(shortFlag: "v", longFlag: "version", helpMessage: "Prints the version number.") 42 | let helpOption = BoolOption(shortFlag: "h", longFlag: "help", helpMessage: "Prints a help message.") 43 | 44 | 45 | let cli = CommandLine() 46 | cli.addOptions(inputPathOption, configurationOption, outputPathOption, swiftDestinationOption, swiftTargetMacOption, swiftTargetiOSOption, swiftTargetWatchOption, overwriteOption, versionOption, helpOption) 47 | 48 | 49 | do { 50 | try cli.parse() 51 | } catch { 52 | cli.printUsage(error) 53 | exit(EX_USAGE) 54 | } 55 | 56 | 57 | if helpOption.value { 58 | cli.printUsage() 59 | exit(EX_USAGE) 60 | } 61 | 62 | if versionOption.value { 63 | print("XCAssetPacker version \(versionNumber)") 64 | exit(EX_USAGE) 65 | } 66 | 67 | 68 | 69 | guard inputPathOption.wasSet && outputPathOption.wasSet else { 70 | print("Missing required options: [-i or --input, and -o or --output]\n") 71 | cli.printUsage() 72 | exit(EX_USAGE) 73 | } 74 | 75 | 76 | let inputPath = inputPathOption.value 77 | let outputPath = outputPathOption.value 78 | let shouldOverwrite: Bool = overwriteOption.value 79 | 80 | 81 | // Determine input URL 82 | let fileManager = FileManager() 83 | let sourceDirectoryURL: URL 84 | 85 | if let input = inputPath { 86 | sourceDirectoryURL = URL(fileURLWithPath: input) 87 | } else { 88 | sourceDirectoryURL = URL(fileURLWithPath: fileManager.currentDirectoryPath) 89 | } 90 | 91 | 92 | // Determine output URL 93 | var destinationDirectoryURL: URL 94 | 95 | if let output = outputPath { 96 | destinationDirectoryURL = URL(fileURLWithPath: output).absoluteURL 97 | } else { 98 | destinationDirectoryURL = URL(fileURLWithPath: fileManager.currentDirectoryPath) 99 | } 100 | 101 | 102 | // Validate swift url 103 | var swiftDestinationURL: URL? 104 | 105 | if let swiftDestinationPath = swiftDestinationOption.value { 106 | swiftDestinationURL = URL(fileURLWithPath: swiftDestinationPath).absoluteURL 107 | } else if swiftDestinationOption.wasSet { 108 | swiftDestinationURL = URL(fileURLWithPath: fileManager.currentDirectoryPath) 109 | } 110 | 111 | let swiftTarget: SwiftTarget 112 | 113 | if swiftTargetMacOption.wasSet { 114 | swiftTarget = .cocoa 115 | } else if swiftTargetWatchOption.wasSet { 116 | swiftTarget = .watch 117 | } else { 118 | swiftTarget = .iOS 119 | } 120 | 121 | 122 | // Load a configuration .json 123 | var configuration: [String: Any] = [:] 124 | 125 | if let configurationFilePath = configurationOption.value { 126 | let configurationFileURL = URL(fileURLWithPath: configurationFilePath).absoluteURL 127 | 128 | if let data = try? Data(contentsOf: configurationFileURL), 129 | let json = try? JSONSerialization.jsonObject(with: data, options: []), 130 | let configurationJSON = json as? [String: Any] { 131 | configuration = configurationJSON 132 | } 133 | } 134 | 135 | 136 | do { 137 | // Build a catalog of available images 138 | let assetCatalog = try AssetCatalogGenerator(from: sourceDirectoryURL, to: destinationDirectoryURL, swift: swiftDestinationURL, target: swiftTarget, overwrite: shouldOverwrite, configuration: configuration) 139 | 140 | // Generate the output packages and files 141 | let log = try assetCatalog.applyChanges() 142 | 143 | // Print 144 | let suffix = assetCatalog.destinationURL.pathComponents.suffix(4) 145 | let lastPathComponents = suffix.reduce("") { (combined, pathComponent) -> 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 | -------------------------------------------------------------------------------- /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/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/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.. { 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/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 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.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 | --------------------------------------------------------------------------------