Since Xcode8 does not support plugins, it is impossible to use Xcode to generate AppIcon with one click. I have not found a good solution, so I decided to write a script to generate AppIcon. The following is the text.

The source address

preparation

Viewing the Swift Version

The first thing you need to make sure is the Swift version on your Mac:

swift --versionCopy the code

The result on my computer looks like this:

Apple Swift version 4.0 (swiftlang-900.0.65 clang-900.0.37)
Target: x86_64-apple-macosx10.9Copy the code

And then you can use Xcode to create a Swift file to write swift scripts, but to create a separate Swift file, Xcode is very unfriendly to edit, so my solution is to create a Command Line Tool project that runs on the Mac, so it has code prompts, otherwise it’s too painful to write, If the bosses have a better idea, they can guide the younger brother.

Swift scripting tips

Terminal input and output

The first thing we should know about scripts is how to input and output on the terminal. Here is how to input and output:

The output

The input is simple and familiar: print. Here is a code example:

print("Hello world!")Copy the code

You can then try the following (test.swift is your filename) :

swift test.swiftCopy the code

After executing, you will see a line on the terminal: Hello World!

This completes our first Swift script.

The input

Once we know how to output, we also need to know how to input. Input is also very simple. Here is a code example:

print("Please enter the text:")
if let input = readLine() {
    print("The text you type:\(input)")}Copy the code

The result is displayed after execution:

Please enter the text: Hello world! The text you type: Hello world!Copy the code

Now that the input is complete, we are ready for swift scripting.

Invoke additional commands in the Swift script

Echo, mkdir, CD, etc., can be directly called in swift, the answer is yes, let’s use a simple example to understand, if you want to further explore the portal:

import Foundation

func execute(path: String, arguments: [String]? = nil) -> Int {
    let process = Process()
    process.launchPath = path
    ifarguments ! =nil {
        process.arguments = arguments!
    }
    process.launch()
    process.waitUntilExit()
    return Int(process.terminationStatus)

}

let status = execute(path: "/bin/ls")
print("Status = \(status)")Copy the code

The above script is equivalent to executing the ls command in the terminal. If you do not know the command path, you can use where to find, for example:

where lsCopy the code

This is the result of the implementation:

ls: aliased to ls -G
/bin/lsCopy the code

/bin/ls is the path of the ls command.

Start scripting

Read input. PNG

First of all, we need to read out the image that needs to be transformed. Here is the main code:

import Foundation

let inputPath = "input.png"
let inoutData = try Data(contentsOf: url)
print("Image size:\(inoutData.count / 1024) kb")
let dataProvider = CGDataProvider(data: inoutData as CFData)
if let inputImage = CGImage(pngDataProviderSource: dataProvider! , decode:nil, shouldInterpolate: true, intent: .defaultIntent) {
    // inputImage is the image that needs to be converted
}else {
    print("Conversion failed, image must be PNG")}Copy the code

Generate appicon.appiconset and contents.json

Here is the design to the file operation, with FileManager on the line, I believe we have been familiar with the road, I will post some main code, we see the complete version to my Github source code to see the line:

import Foundation

/ / / AppIcon model
struct AppIconImageItem: Codable {
    let size: String
    let idiom: String
    let filename: String
    let scale: String
    let role: String?
    let subtype: String?
}

struct AppIconInfo: Codable {
    let version: Int
    let author: String
}

struct AppIcon: Codable {
    var images: [AppIconImageItem]
    let info: AppIconInfo
}


Create contentsJson / / /
///
/// -parameter appIcon: indicates the appIcon passed in
func createAppIconContentsJson(appIcon: AppIcon) {
    print("\n starts generating contentsJson\n")
    let encoder = JSONEncoder(a)do {
        encoder.outputFormatting = .prettyPrinted
        let appIconData = try encoder.encode(appIcon)
        if let appIconStr  = String.init(data: appIconData, encoding: .utf8) {
            let contentJsonPath = "AppIcon.appiconset/Contents.json"
            let contentJsonUrl = URL(fileURLWithPath: contentJsonPath)
            try appIconStr.write(to: contentJsonUrl, atomically: true, encoding: .utf8)
            print("ContentsJson generated successfully \n")}else {
            print("ContentsJson generation failed")}}catch {
        print(error.localizedDescription)
    }
}

// create the appicon file
///
/// - Parameter appIcon: appicon
func createFile(appIcon: AppIcon, image: CGImage) {
    let fileManager = FileManager.default
    let filePath = "AppIcon.appiconset"
    do {
        if fileManager.fileExists(atPath: filePath) {
            try fileManager.removeItem(atPath: filePath)
        }
        try fileManager.createDirectory(atPath: filePath, withIntermediateDirectories: true, attributes: nil)
        createAppIconContentsJson(appIcon: appIcon)
        print("~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ do ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~")}catch {
        print("File directory\(filePath)Failed to create")
        print(error.localizedDescription)
    }
}Copy the code

Generate images of different sizes

To generate the image, we use the Core Graphics framework within the Foundation framework. Here is the main code:

import Foundation


/// Generate a single image
///
/// - Parameters:
/// -size: the size of the image
/// -scale: multiple, e.g. @2x is 2 times
/// -filename: indicates a filename
func createImage(size: CGSize, scale: CGFloat, image: CGImage, filename: String) {
    print("Start generating images:\(filename)")
    let width  = Int(size.width * scale)
    let height = Int(size.height * scale)
    let bitsPerComponent = image.bitsPerComponent
    let bytesPerRow = image.bytesPerRow
    let colorSpace  = image.colorSpace

    if let context = CGContext.init(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace! , bitmapInfo:CGImageAlphaInfo.premultipliedLast.rawValue) {
        context.interpolationQuality = .high
        context.draw(image, in:.init(origin: .zero, size: .init(width: width, height: height)))
        if let inputImage = context.makeImage() {
            let outputImagePath = "AppIcon.appiconset/\(filename)"
            let outputUrl = URL(fileURLWithPath: outputImagePath) as CFURL
            let destination = CGImageDestinationCreateWithURL(outputUrl, kUTTypePNG, 1.nil)
            if let destination = destination {
                CGImageDestinationAddImage(destination, inputImage, nil)
                if CGImageDestinationFinalize(destination) {
                    print("The image:\(filename)Generated successfully \n")}else {
                    print("The image:\(filename)Generate failed \n")}}}else {
            print("The image:\(filename)Generate failed \n")}}}Copy the code

Finally, I will post the following screenshots:

The above is only a part of the main code, the complete code is too much, you can go to my Github address to download the following try, if there is something wrong, welcome your advice ~~