NSURL 扩展采用 StringLiteralConvertible

NSURL extension to adopt StringLiteralConvertible

根据来自 NSHipster 的 post,我们扩展了 NSURL class 以使用如下文字初始化 NSURL 对象:

// the following is a full fledged NSURL object
let url: NSURL = "http://nshipster.com/"

不幸的是,post 是在 Swift 首次公布时编写的,它不再编译。

我能够让自己的自定义对象符合 StringLiteralConvertible,它看起来像这样:

final class Dog {
  let name: String

  init(name: String) {
    self.name = name
  }
}

// MARK: - StringLiteralConvertible
extension Dog: StringLiteralConvertible {
  typealias UnicodeScalarLiteralType = StringLiteralType
  typealias ExtendedGraphemeClusterLiteralType = StringLiteralType

  convenience init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
    self.init(stringLiteral: value)
  }

  convenience init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
    self.init(stringLiteral: value)
  }

  convenience init(stringLiteral value: StringLiteralType) {
    self.init(name: value)
  }
}

效果很好。例如,以下两行代码将创建一个 Dog 对象:

let dog = Dog(name: "Bob")
let dog: Dog = "Bob"

不幸的是,通过扩展 NSURL 使用此策略遇到了错误:

extension NSURL: StringLiteralConvertible {
  public typealias UnicodeScalarLiteralType = StringLiteralType
  public typealias ExtendedGraphemeClusterLiteralType = StringLiteralType

  convenience public init?(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
    self.init(stringLiteral: value)
  }

  convenience public init?(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
    self.init(stringLiteral: value)
  }

  convenience public init?(stringLiteral value: StringLiteralType) {
    self.init(string: value)
  }
}

我一直在研究编译器错误,一次解决 1 个猜测。但是,我无法克服每个初始化程序发生的以下错误:

Initializer requirement 'init(...)' can only be satisfied by a 'required' initializer in the definition of non-final class 'NSURL'

添加 required 关键字会导致您可能未在扩展中声明必需的初始值设定项。

正在寻找方向:|

StringLiteralConvertible 支持

不幸的是,StringLiteralConvertible 在当前的 Swift 版本 (2.2) 中似乎无法支持 NSURL。我能得到的最接近的是:

extension NSURL: StringLiteralConvertible {

    public convenience init(stringLiteral value: String) {
        self.init(string: value)!
    }

    public convenience init(extendedGraphemeClusterLiteral value: String) {
        self.init(string: value)!
    }

    public convenience init(unicodeScalarLiteral value: String) {
        self.init(string: value)!
    }

}

但是编译器抱怨:

Playground execution failed: OS X Playground.playground:5:24: error: initializer requirement 'init(stringLiteral:)' can only be satisfied by a `required` initializer in the definition of non-final class 'NSURL'
    public convenience init(stringLiteral value: String) {
                       ^
OS X Playground.playground:3:24: error: initializer requirement 'init(extendedGraphemeClusterLiteral:)' can only be satisfied by a `required` initializer in the definition of non-final class 'NSURL'
    public convenience init(extendedGraphemeClusterLiteral value: String) {
                       ^
OS X Playground.playground:7:24: error: initializer requirement 'init(unicodeScalarLiteral:)' can only be satisfied by a `required` initializer in the definition of non-final class 'NSURL'
    public convenience init(unicodeScalarLiteral value: String) {

并且 required 初始化器不能在扩展中实现。

备选方案

我们可以从另一端简化字符串到URL的转换!

extension String {

    var url: NSURL? {
        return NSURL(string: self)
    }

}

var url = "http://google.coom/".url
print(url?.scheme) // Optional("http")

另一种选择是子类化 NSURL 并符合那里的 StringLiteralConvertible

class URL: NSURL, StringLiteralConvertible {

    <#...#>
}