Swift 5で進化するNSAttributedStringの扱い方

Swift5がリリースされましたね。
Swift5にはABIの安定化やResult型のStandard Library追加など様々な追加・変更がありました。

次のURLにまとまった神Playgroundがあるので、みたことない人はおさらいしましょう。
https://github.com/twostraws/whats-new-in-swift-5-0

String Interpolationを使ってNSAttributedStringをラップする
---
この中でString Interpolationという機能があります。
String Interpolationの使い方は上記Playgroundを参照してください。

このString Interpolationを使うとめちゃめちゃNSAttributedStringが便利になります。

下記にNSAttributedStringをラップしたものを作りました。

```AttributedString.swift
import UIKit

public struct AttributedString: ExpressibleByStringLiteral, ExpressibleByStringInterpolation, CustomStringConvertible {
    public struct StringInterpolation: StringInterpolationProtocol {
        public var attributedString: NSMutableAttributedString
        public init(literalCapacity: Int, interpolationCount: Int) {
            attributedString = NSMutableAttributedString()
        }

        public func appendLiteral(_ literal: String) {
            attributedString.append(NSAttributedString(string: literal))
        }

        public func appendInterpolation(_ linkText: String? = nil, URL url: URL) {
            let text = linkText ?? url.absoluteString
            attributedString.append(NSAttributedString(string: text, attributes: [.link: url]))
        }

        public func appendInterpolation(_ image: UIImage, bounds: CGRect? = nil) {
            let textAttachment = NSTextAttachment()
            textAttachment.image = image
            if let bounds = bounds {
                textAttachment.bounds = bounds
            }
            attributedString.append(NSAttributedString(attachment: textAttachment))
        }

        func appendInterpolation(_ text: String, attributes: [NSAttributedString.Key: Any]) {
            attributedString.append(NSAttributedString(string: text, attributes: attributes))
        }
    }
    public var attributedString: NSAttributedString
    public init(stringLiteral value: String) {
        attributedString = NSAttributedString(string: value)
    }

    public init(stringInterpolation: StringInterpolation) {
        attributedString = NSAttributedString(attributedString: stringInterpolation.attributedString)
    }

    public init(attributedString: NSAttributedString) {
        self.attributedString = attributedString
    }

    public var description: String {
        return attributedString.description
    }

}
```

このようにするとほとんど文字列と同じようにAttributedStringを作ることができます。

```swift
let attrStr: AttributedString = "新規登録は\("こちら", URL: URL(string: "http://example.com/signin")!)"
print(attrStr)
```

```
新規登録は{
}こちら{
    NSLink = "http://example.com/signin";
}
```

Textにimageを追加するのも簡単です！

```swift
let image = UIImage(named: "sun")!
let attrStr: AttributedString = "今日はいい天気だ\(image)"
```

```
今日はいい天気だ{
}￼{
    NSAttachment = "<NSTextAttachment: 0x60000050d490>";
}
```

もし自前で表現を追加したい場合はAttributedString.StringInterpolationを拡張してappendInterpolation methodを追加してください。

```swift
extension AttributedString.StringInterpolation {
    // 任意のサイズのスペースを追加
    public func appendInterpolation(space: CGFloat) {
        let textAttachment = NSTextAttachment()
        textAttachment.bounds.size.width = space
        attributedString.append(NSAttributedString(attachment: textAttachment))
    }
}

let attrStr: AttributedString = "ここから\(space: 100)ここまで"
```

UIで表示したい場合はattributedStringプロパティがあるのでそれを利用すれば表示できます。

```swift
let label = UILabel()
label.attributedText = attrStr.attributedString
label.sizeToFit()
```


まとめ
---
最初Swift5でこの機能を見たときは、ちょっと便利そうだけど使う機会あるかなと思っていました。
しかし使ってみるとかなりの神機能でNSAttributedStringをここまで進化させてくれるものとは思いませんでした。
上記AttributedString.swiftはgistにもおいておくので色々試してみてください！

https://gist.github.com/fmtonakai/4e3f6011a4a85498478047a0e742f3ec