├── 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 [](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 | 
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 |
--------------------------------------------------------------------------------