如何在 Swift 中生成 Mongo DB ObjectID?

How do I generate a Mongo DB ObjectID in Swift?

我需要在 Swift 中生成一个带时间戳的 BSON 格式的对象 ID。 ObjectID 用于 Mongo 数据库。如何实现?

一个天真的解决方案如下所示:

func objectId() -> String {
    let time = String(Int(Date().timeIntervalSince1970), radix: 16, uppercase: false)
    let machine = String(Int.random(in: 100000 ..< 999999))
    let pid = String(Int.random(in: 1000 ..< 9999))
    let counter = String(Int.random(in: 100000 ..< 999999))
    return time + machine + pid + counter
}

MongoDB docs 指定以下关于ObjectID

  • 表示自 Unix 纪元以来的秒数的 4 字节值
  • 一个 3 字节的机器标识符
  • 一个 2 字节的进程 ID
  • 一个 3 字节的计数器,以一个随机值开始

以上将满足该要求。但是,它只会为时间戳以外的部分生成随机数字字符。一个完美的解决方案是为机器和 pid 使用诸如 NSProcessInfoNSUUID 之类的 api。它还必须跟踪计数器。

根据 MongoDB 文档,ObjectId 是使用以下方法生成的:

  • 一个 4 字节的时间戳值,表示 ObjectId 的创建,以自 Unix 纪元以来的秒数为单位
  • 一个 5 字节的随机值
  • 一个 3 字节递增计数器,初始化为一个随机值

您可以使用此 class,它实现了上述内容。

class ObjectId {
    private init() {}
    static let shared = ObjectId()

    private var counter = Int.random(in: 0...0xffffff)

    private func incrementCounter() {
        if (counter >= 0xffffff) {
            counter = 0
        } else {
            counter += 1
        }
    }

    func generate() -> String {
        let time = ~(~Int(NSDate().timeIntervalSince1970))
        let random = Int.random(in: 0...0xffffffffff)
        let i = counter
        incrementCounter()

        var byteArray = Array<UInt8>.init(repeating: 0, count: 12)

        byteArray[0] = UInt8((time >> 24) & 0xff)
        byteArray[1] = UInt8((time >> 16) & 0xff)
        byteArray[2] = UInt8((time >> 8) & 0xff)
        byteArray[3] = UInt8(time & 0xff)
        byteArray[4] = UInt8((random >> 32) & 0xff)
        byteArray[5] = UInt8((random >> 24) & 0xff)
        byteArray[6] = UInt8((random >> 16) & 0xff)
        byteArray[7] = UInt8((random >> 8) & 0xff)
        byteArray[8] = UInt8(random & 0xff)
        byteArray[9] = UInt8((i >> 16) & 0xff)
        byteArray[10] = UInt8((i >> 8) & 0xff)
        byteArray[11] = UInt8(i & 0xff)

        let id = byteArray
                     .map({ String([=10=], radix: 16, uppercase: false)
                     .padding(toLength: 2, withPad: "0", startingAt: 0) })
                     .joined()

        return id
    }
}

以下代码将生成一个新的 ObjectId 字符串:

ObjectId.shared.generate()