如何使NSAttributedString符合可编码标准?

6

问题是什么?

目前,我正在构建一个应用程序扩展,它通过 JSON 与我的主应用程序通信。主题和数据位于 JSON 中,并通过 Apple 的可编码协议进行解析。我现在遇到的问题是使 NSAttributedString 可编码兼容。我知道它不是内置的,但我知道它可以转换为数据,然后再转换回

到目前为止我有什么?

将 NSAttributedString 转换为数据以便通过 JSON 共享。

if let attributedText = something.attributedText {
    do {
        let htmlData = try attributedText.data(from: NSRange(location: 0, length: attributedText.length), documentAttributes: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType])
        let htmlString = String(data: htmlData, encoding: .utf8) ?? "" 
    } catch {
        print(error)
    }
}

将一个HTML格式的JSON字符串转换为NSAttributedString:
do {
    return try NSAttributedString(data: self, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch {
    print("error:", error)
    return  nil
}

我的问题?

如何创建一个具有属性nsAttributedTitle的结构,该属性是NSAttributedString类型,并使其符合自定义编码器解码器的可编码规范?

结构示例(不考虑可编码合规性):

struct attributedTitle: Codable {
    var title: NSAttributedString

    enum CodingKeys: String, CodingKey {
        case title
    }

    public func encode(to encoder: Encoder) throws {}
    public init(from decoder: Decoder) throws {}
}
1个回答

13

NSAttributedString符合NSCoding,因此您可以使用NSKeyedArchiver来获取一个Data对象。

这是一个可能的解决方案。

class AttributedString : Codable {
    
    let attributedString : NSAttributedString
    
    init(nsAttributedString : NSAttributedString) {
        self.attributedString = nsAttributedString
    }
    
    public required init(from decoder: Decoder) throws {
        let singleContainer = try decoder.singleValueContainer()
        guard let attributedString = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSAttributedString.self, from: singleContainer.decode(Data.self)) else {
            throw DecodingError.dataCorruptedError(in: singleContainer, debugDescription: "Data is corrupted")
        }
        self.attributedString = attributedString
    }
    
    public func encode(to encoder: Encoder) throws {
        var singleContainer = encoder.singleValueContainer()
        try singleContainer.encode(NSKeyedArchiver.archivedData(withRootObject: attributedString, requiringSecureCoding: false))
    }
}

更新:
在Swift 5.5中引入了符合Codable协议的本地AttributedString。

有道理,但我们最终得到的是一个类而不是结构体。这会带来一些影响。 - Kevin Vugts
1
你也可以使用结构体。然后你需要删除 required - vadian
我今晚会尝试,尽快接受你的答案。 - Kevin Vugts
Data符合Encodable&Decodable。不需要自己对其进行base64编码。https://stackoverflow.com/questions/58976895/conforming-nsattributedstring-to-codable-throws-error/58978478#58978478 - Leo Dabus
public required init(from decoder: Decoder) throws { let singleContainer = try decoder.singleValueContainer() guard let attributedString = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(singleContainer.decode(Data.self)) as? NSAttributedString else { throw DecodingError.dataCorruptedError(in: singleContainer, debugDescription: "Data is corrupted") } self.attributedString = attributedString } - Leo Dabus
func encode(to encoder: Encoder) throws { var singleContainer = encoder.singleValueContainer() try singleContainer.encode(NSKeyedArchiver.archivedData(withRootObject: attributedString, requiringSecureCoding: false)) } - Leo Dabus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接