Stack overflow when defining subscript on CKRecord in Swift

我是研究僧i 提交于 2019-12-01 17:53:58

After some testing and debugging (via a subclass), I discovered that, for CKRecord, objectForKey: does indeed call objectForKeyedSubscript:. Also, implementing subscript in a Swift class that is marked @objc implicitly (by descending from NSObject) or explicitly means that subscript is implemented as objectForKeyedSubscript:.

This means that implementing subscript on CKRecord in an extension hides the default implementation, which causes the stack overflow.

Here’s a simple extension to CKRecord to make it easier to subscript with.

extension CKRecord {
    struct Sub {
        let record: CKRecord

        subscript(key: String) -> CKRecordValue? {
            get {
                return record.objectForKey(key) as? CKRecordValue
            }
            set {
                record.setObject(newValue, forKey: key)
            }
        }
    }

    var sub: Sub {
        return Sub(record: self)
    }

    var 👌: Sub {
        return sub
    }
}

Usage:

var sub = record.sub
sub["name"] = name

/* Or */

// One does not simply subscript CKRecord
record.👌["name"] = name

(I’m kidding about the 👌 by the way)

I was able to successfully subscript by piggy-backing off of some code written by an Apple engineer on dev forums.

import CloudKit

protocol MyCKRecordValueType {
    var asObject: CKRecordValue { get }
}

extension CKRecord {
    func set<ValueType>(value: ValueType, forKey key: String) where ValueType : MyCKRecordValueType {
        let object = value.asObject
        self.setObject(object, forKey: key)
    }
    subscript(key : String) -> MyCKRecordValueType? {
        set {
            self.setObject(newValue?.asObject, forKey: key)
        }
        get {
            return object(forKey: key) as? MyCKRecordValueType
        }
    }
}

extension String : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSString }
}
extension Bool : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSNumber }
}
extension Int : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSNumber }
}
extension Data : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSData }
}

you can then call the subscript as you would expect:

let firstRecordID = CKRecordID(recordName: "0")

        let record = CKRecord(recordType: "Foo", recordID: firstRecordID)

        record["title"] = "Hello World"

        record["year_established"] = 2000
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!