从 FengNiao 中学习 Swift (二)

在 从 FengNiao 中学习 Swift (一) 中介绍了 Swift Package Manager 的基本用法,然后创建了FengNiaoCopy 项目。这个系列的第二篇文章主要介绍如何处理命令行的输入参数。

CommandLine 使用

在开发一个功能的时候,我们应该优先考虑是否存在一个库可以满足我们的使用要求,或者考虑是否存在一个库在经我们简单修改之后可以满足我们的使用要求,若是都不存在那么再自己去开发。对于命令行的输入参数处理,找到一个 CommandLine 符合需求。
既然有了第三方库,那么接下来就是使用第三方库提供的 demo 来改进以符合我们的需求。
首先在代码文件中导入库,使用 import 命令

import CommandLineKit
import Rainbow

由于 CommandLine 的第三方库提供一个有样式的格式化功能,但是使用这个功能需要依赖另一个第三方库 Rainbow,所以也导入 Rainbow 。

首先采用有样式的格式化命令行输出

let cli = CommandLineKit.CommandLine()
cli.formatOutput = { s,type in
    var str : String
    switch(type){
    case .error:
        str = s.red.bold
    case .optionFlag:
        str = s.green.underline
    default: str = s
    }
    return cli.defaultFormat(s: str, type: type)
}

这里面的 swift 语法需要留意的就是

  1. switch 中的 case 不再需要添加 break,switch只会执行一个 case,swift 相当于给每个 case 都默认添加 break 。
  2. 关键词 let 用于声明不可变的变量,被 let 声明的变量只能被赋值一次,不可再修改。
  3. 关键词 var 用于声明可变变量。

接下来是一些命令行参数的声明,写法都是参考 CommandLine 的 demo 写的。

  1. projectPathOption 表示项目路径
  2. isForceOption 表示查找之后是否直接清理
  3. excludePathOption 表示哪些路径是不要查找的
  4. resourceExtOption 表示要查找的资源类型是哪些
  5. fileExtOption 表示在哪些对应拓展名的文件查找
let projectPathOption = StringOption(shortFlag: "p", longFlag: "project", helpMessage: "Root path of your Xcode project. Default is current folder.")

let isForceOption = BoolOption(longFlag: "force", helpMessage: "Delete the found unused files without asking.")

let excludePathOption = MultiStringOption(shortFlag: "e", longFlag: "exclude", helpMessage: "Exclude paths from search")

let resourceExtOption = MultiStringOption(shortFlag: "r", longFlag: "resource-extensions", helpMessage: "Resource file extensions need to be searched. Default is 'imageset jpg png gif'")

let fileExtOption = MultiStringOption(
    shortFlag: "f", longFlag: "file-extensions",
    helpMessage: "In which types of files we should search for resource usage. Default is 'm mm swift xib storyboard'")

let skipProjRefereceCleanOption = BoolOption(
    longFlag: "skip-proj-reference",
    helpMessage: "Skip the Project file (.pbxproj) reference cleaning. By skipping it, the project file will be left untouched. You may want to skip ths step if you are trying to build multiple projects with dependency and keep .pbxproj unchanged while compiling."
)

let versionOption = BoolOption(longFlag: "version", helpMessage: "Print version.")
let helpOption = BoolOption(shortFlag: "h", longFlag: "help",
                            helpMessage: "Print this help message.")

cli.addOptions(projectPathOption,isForceOption,excludePathOption,resourceExtOption,fileExtOption,skipProjRefereceCleanOption,versionOption,helpOption)

声明好了命令行参数之后,接下来参考 CommandLine 的 demo 来做参数解析。

do{
    try cli.parse()
}catch{
    cli.printUsage(error)
    exit(EX_USAGE)
}

这个是 swift 语法中的异常处理格式,在可能抛出异常的方法调用增加一个 try 关键字,然后使用 do {} catch {} 将方法调用包裹起来。需要留意的是,在 catch 里面是可以自动获取到 error 这个变量的。

参数解析之后,接下来做命令行参数的容错处理,代码如下

if !cli.unparsedArguments.isEmpty{
    print("Unknow arguments: \(cli.unparsedArguments)".red)
    cli.printUsage()
    exit(EX_OK)
}
if helpOption.value{
    cli.printUsage()
    exit(EX_OK)
}
if versionOption.value{
    print(appVersion)
    exit(EX_OK)
}

if 条件语句在 swift 和 Objective-C 的写法是有差异的,在 Objective-C 中,if 语句中的判断条件是放在 () 里面的,如 if (!cli.unparsedArguments.isEmpty){ } 。在 swift 中判断条件没有用 () 包裹,如 if !cli.unparsedArguments.isEmpty{ }。

swift 中使用 “ \() ” 来进行变量值和字符串的拼接,如 print("Unknow arguments: \(cli.unparsedArguments)".red)

命令行参数都拿到之后,我们希望将这些拿到的参数赋值给变量。若是有些命令行参数为空,我们希望给这些参数合适的默认值。

let projectPath = projectPathOption.value ?? "."
let isForce = isForceOption.value
// [] 表示空数组
let excludePaths = excludePathOption.value ?? []
// [] 用来表示数组
let resourceExtentions = resourceExtOption.value ?? ["imageset","jpg","png","gif"]
let fileExtensions = fileExtOption.value ?? ["m","mm","swift","xib","storyboard","plist"]

在这里,我们看到了一个新的符号 " ?? ", 这个是 swift 的一个操作符,它的含义是先对可选值进行拆包,如果不为 nil 返回操作符前面的值,如果为空返回后者 。举个例子,在 let projectPath = projectPathOption.value ?? "." 中,如果 projectPathOption.value 的值为 nil ,那么 projectPath 取默认值 “.” ,如果projectPathOption.value 的值不是 nil, 那么 projectPath 取 projectPathOption.value 的值。

你可能感兴趣的:(从 FengNiao 中学习 Swift (二))