Skip to content

Commit

Permalink
Adds support for Xcode 9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
inquisitiveSoft committed Sep 5, 2017
1 parent 21a4289 commit 64bdc52
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 40 deletions.
42 changes: 42 additions & 0 deletions Examples/Annotated Configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
/* Skip images */
"skip-images" : {
"patterns" : ["Watch App .*", "Bed.*"]
},


/* Set which images to include. Defaults to ["png"] */
"valid-image-extensions" : ["png", "jpeg", "jpg", "tiff"],

// Set the base rendering options
"base" : {
"template-rendering-intent" : "template"
},

/* Apply properties based on the target device */
"devices" : [
{
"device-type" : "watch",
"properties" : {
"template-rendering-intent" : "template"
}
}
],

/* By default images following the AppIcon-{size}.png naming convention
will be treated as prerendered app icons and won't be exposed to Swift */
"app-icon" : {
"pattern" : "Custom App Icon Name.*",
"prerendered" : false /* Defaults to true */
},

/* Apply custom sets of properties to images matched by the given patterns */
"custom" : [
{
"patterns" : [".*Preview.*", "SleepDuration.*", "WakeTime.*"],
"properties" : {
"template-rendering-intent" : "original"
}
}
]
}
18 changes: 9 additions & 9 deletions XCAssetPacker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
4AD71E911EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD71E8F1EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift */; };
4AD71E951EA4DD2700016A1A /* Complication Rules.json in Resources */ = {isa = PBXBuildFile; fileRef = 4AD71E941EA4DD2700016A1A /* Complication Rules.json */; };
4AD71E971EA4EEEC00016A1A /* Skip Circles Rules.json in Resources */ = {isa = PBXBuildFile; fileRef = 4AD71E961EA4EEEC00016A1A /* Skip Circles Rules.json */; };
4AFFC5911F5D8E35000FDCFD /* DeviceIdiom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AFFC5901F5D8E35000FDCFD /* DeviceIdiom.swift */; };
4AFFC5931F5D8E83000FDCFD /* DeviceIdiom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AFFC5921F5D8E83000FDCFD /* DeviceIdiom.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -62,6 +64,8 @@
4AD71E8F1EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AssetCatalogGenerator+Creation.swift"; sourceTree = "<group>"; };
4AD71E941EA4DD2700016A1A /* Complication Rules.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Complication Rules.json"; sourceTree = "<group>"; };
4AD71E961EA4EEEC00016A1A /* Skip Circles Rules.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Skip Circles Rules.json"; sourceTree = "<group>"; };
4AFFC5901F5D8E35000FDCFD /* DeviceIdiom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceIdiom.swift; sourceTree = "<group>"; };
4AFFC5921F5D8E83000FDCFD /* DeviceIdiom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DeviceIdiom.swift; path = XCAssetPacker/DeviceIdiom.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -85,9 +89,9 @@
4A56D13E1DE5B573000B40D8 = {
isa = PBXGroup;
children = (
4AFFC5921F5D8E83000FDCFD /* DeviceIdiom.swift */,
4A56D1491DE5B573000B40D8 /* XCAssetPacker */,
4A56D1571DE5B5AB000B40D8 /* CommandLine */,
4AD71E771EA4CC7300016A1A /* Tests */,
4AD71E7D1EA4CCBA00016A1A /* XCAssetPacker Tests */,
4A56D1481DE5B573000B40D8 /* Products */,
);
Expand All @@ -110,6 +114,7 @@
4AD71E8F1EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift */,
4A56D1581DE5BA85000B40D8 /* AssetCatalogGenerator.swift */,
4A07727D1DF963C70068A929 /* ImageProperties.swift */,
4AFFC5901F5D8E35000FDCFD /* DeviceIdiom.swift */,
4A07727B1DF9634A0068A929 /* Extensions.swift */,
4AD71E731EA4899500016A1A /* SwiftGeneration.swift */,
);
Expand All @@ -127,14 +132,6 @@
path = XCAssetPacker;
sourceTree = "<group>";
};
4AD71E771EA4CC7300016A1A /* Tests */ = {
isa = PBXGroup;
children = (
);
name = Tests;
path = XCAssetPacker;
sourceTree = "<group>";
};
4AD71E7D1EA4CCBA00016A1A /* XCAssetPacker Tests */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -243,6 +240,7 @@
buildActionMask = 2147483647;
files = (
4A0772821DF9731B0068A929 /* CommandLine.swift in Sources */,
4AFFC5911F5D8E35000FDCFD /* DeviceIdiom.swift in Sources */,
4A5F178F1EA3EDE8007C37DE /* Constants.swift in Sources */,
4A07727C1DF9634A0068A929 /* Extensions.swift in Sources */,
4A07727E1DF963C70068A929 /* ImageProperties.swift in Sources */,
Expand All @@ -266,6 +264,7 @@
4AD71E871EA4CD0800016A1A /* Extensions.swift in Sources */,
4AD71E881EA4CD0800016A1A /* SwiftGeneration.swift in Sources */,
4AD71E8A1EA4CD0800016A1A /* CommandLine.swift in Sources */,
4AFFC5931F5D8E83000FDCFD /* DeviceIdiom.swift in Sources */,
4AD71E8B1EA4CD0800016A1A /* Option.swift in Sources */,
4AD71E8C1EA4CD0800016A1A /* StringExtensions.swift in Sources */,
4AD71E911EA4D13400016A1A /* AssetCatalogGenerator+Creation.swift in Sources */,
Expand Down Expand Up @@ -445,6 +444,7 @@
4AD71E831EA4CCBA00016A1A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
51 changes: 34 additions & 17 deletions XCAssetPacker/AssetCatalogGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class Node {
return sourceURL == nil
}


var pathComponents: [String] {
var currentNode: Node? = self
var pathComponents: [String] = []
Expand All @@ -110,19 +111,6 @@ class Node {
}


// var folderPathComponents: [String] {
// var currentNode: Node? = self.parent
// var pathComponents: [String] = []
//
// while let node = currentNode, node.parent != nil {
// pathComponents.append(node.name)
// currentNode = node.parent
// }
//
// return pathComponents
// }


func printTree(depth: Int = 0) {
let depthPadding = (0...depth).reduce("") { (existing, _) -> String in
return existing + " "
Expand All @@ -144,6 +132,7 @@ class AssetCatalogGenerator {
let swiftFileURL: URL?
let swiftTarget: SwiftTarget
let shouldOverwrite: Bool
let baseIdiom: DeviceIdiom?
let rootNode: Node

let numberOfRootPathComponents: Int
Expand All @@ -162,6 +151,13 @@ class AssetCatalogGenerator {

self.shouldOverwrite = shouldOverwrite
self.configuration = configuration

if let base = configuration.value(for: .base) as? [String: Any],
let idiom = base.value(for: .idiom) as? String {
baseIdiom = DeviceIdiom(idiom)
} else {
baseIdiom = nil
}
}


Expand Down Expand Up @@ -272,7 +268,26 @@ class AssetCatalogGenerator {
let imageFileName = sourceURL.lastPathComponent
let assetDestinationURL = destinationURL.appending(pathComponents: node.pathComponents).appendingPathComponent(imageFileName)

images.append(imageDictionary(for: imageFileName, properties: properties))
// Xcode 9 seperates Notification, Settings and Spotlight images for iPhone and iPad
// if no base idiom is supplied then add both images
//
// This is rather messy, so would ideally be tidied up
switch properties.type {
case .notification, .settings, .spotlight:
if let idiom = baseIdiom {
images.append(imageDictionary(for: imageFileName, properties: properties, customIdiom: idiom.idiomString))
} else {
let targetIdioms: [DeviceIdiom] = [.iPhone, .iPad]

for targetIdiom in targetIdioms {
images.append(imageDictionary(for: imageFileName, properties: properties, customIdiom: targetIdiom.idiomString))
}
}

default:
images.append(imageDictionary(for: imageFileName, properties: properties))
}

copies.append((sourceURL, assetDestinationURL))
}
}
Expand Down Expand Up @@ -320,12 +335,14 @@ class AssetCatalogGenerator {

// MARK: Generate json for each component within the .xcassets package

func imageDictionary(for imageName: String, properties: ImageProperties) -> [String: Any] {

func imageDictionary(for imageName: String, properties: ImageProperties, customIdiom: String? = nil) -> [String: Any] {
var imageDictionary: [String: Any] = [:]

imageDictionary[Configuration.filename.rawValue] = imageName

if let idiom = properties.idiom {
if let idiom = customIdiom {
imageDictionary[Configuration.idiom.rawValue] = idiom
} else if let idiom = properties.idiom {
imageDictionary[Configuration.idiom.rawValue] = idiom
}

Expand Down
51 changes: 51 additions & 0 deletions XCAssetPacker/DeviceIdiom.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// DeviceIdiom.swift
// XCAssetPacker
//
// Created by Harry Jordan on 04/09/2017.
// Copyright © 2017 Inquisitive Software. All rights reserved.
//

import Foundation


public enum DeviceIdiom: String {
case watch, iPhone, iPad

static var all: [DeviceIdiom] = [watch, iPhone, iPad]


init?(_ idiomString: String) {
let idiom = DeviceIdiom.all.first(where: {
$0.idiomString.caseInsensitiveCompare(idiomString) == .orderedSame
})

if let idiom = idiom {
self = idiom
} else {
return nil
}
}


var idiomString: String {
// Keys used for xcasset properties
switch self {
case .watch:
return "watch"

case .iPhone:
return "iphone"

case .iPad:
return "ipad"
}
}


var configurationKey: String {
// Keys used to match strings in the configuration .json
return self.rawValue
}

}
23 changes: 14 additions & 9 deletions XCAssetPacker/ImageProperties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ struct ImageProperties {
}

if let base = configuration.value(for: .base) as? [String: Any],
let idiomString = base.value(for: .idiom) as? String {
return idiomString
let idiomString = base.value(for: .idiom) as? String,
let deviceIdiom = DeviceIdiom(idiomString) {
return deviceIdiom.idiomString
}

return Configuration.universal.rawValue
Expand All @@ -139,19 +140,23 @@ enum ImageType {


var idiom: String? {
let idiom: DeviceIdiom?

switch self {
case .watch, .watch38, .watch42:
return "watch"
idiom = .watch

case .iPhoneAppIcon:
return "iphone"
idiom = .iPhone

case .iPadAppIcon, .iPadProAppIcon:
return "ipad"
idiom = .iPad

default:
return nil
idiom = nil
}

return idiom?.idiomString
}


Expand All @@ -176,13 +181,13 @@ enum ImageType {
// Used for matching with the configuration file
switch self {
case .watch, .watch38, .watch42:
return "watch"
return DeviceIdiom.watch.configurationKey

case .iPhoneAppIcon:
return "iPhone"
return DeviceIdiom.iPhone.configurationKey

case .iPadAppIcon, .iPadProAppIcon:
return "iPad"
return DeviceIdiom.iPad.configurationKey

default:
return "universal"
Expand Down
10 changes: 5 additions & 5 deletions XCAssetPacker/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import Cocoa

// Setup command line options
let inputPathOption = StringOption(shortFlag: "i", longFlag: "input", helpMessage: "Path to the input folder.")
let configurationOption = StringOption(shortFlag: "c", longFlag: "config", required: false, helpMessage: "The location of a json configuration file. If none is specified then uses sensible defaults.")
let outputPathOption = StringOption(shortFlag: "o", longFlag: "output", helpMessage: "Path to the output file or folder. If a folder is given then an Assets.xcassets package will be created inside it.")
let swiftDestinationOption = StringOption(longFlag: "swift", helpMessage: "Path to the output swift file or folder. If a folder is given then an Images.swift package will be created inside it.")
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.")
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.")
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.")
let overwriteOption = BoolOption(shortFlag: "f", longFlag: "force", helpMessage: "Overwrite any existing .xcassets package or Swift file.")

// Target
Expand Down Expand Up @@ -146,7 +146,7 @@ do {
print(description)
exit(EX_IOERR)
}
} catch {
print("Unexpected error")
} catch let error {
print("Unexpected error: \(String(describing: error))")
exit(EX_IOERR)
}

0 comments on commit 64bdc52

Please sign in to comment.