Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I generate a Mongo DB ObjectID in Swift?

Tags:

swift

bson

I need to generate a timestamped object id of BSON format in Swift. The ObjectID is for Mongo DB. How can this be accomplished?

like image 314
Andrew Johnson Avatar asked Apr 08 '16 20:04

Andrew Johnson


2 Answers

One naive solution would look like this:

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
}

The MongoDB docs specifies the following about the ObjectID

  • a 4-byte value representing the seconds since the Unix epoch
  • a 3-byte machine identifier
  • a 2-byte process id
  • a 3-byte counter, starting with a random value

The above will satisfy that requirement. However it will only generate random number characters for parts other than the timestamp. A perfect solution would use apis such as NSProcessInfo and NSUUIDfor machine and pid. It would also have to keep track of a counter.

like image 143
Simon Bengtsson Avatar answered Nov 09 '22 15:11

Simon Bengtsson


From the MongoDB documentation, an ObjectId is generated using:

  • a 4-byte timestamp value, representing the ObjectId’s creation, measured in seconds since the Unix epoch
  • a 5-byte random value
  • a 3-byte incrementing counter, initialized to a random value

You can use this class, which implements the above.

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($0, radix: 16, uppercase: false)
                     .padding(toLength: 2, withPad: "0", startingAt: 0) })
                     .joined()

        return id
    }
}

The following code will generate a new ObjectId string:

ObjectId.shared.generate()
like image 35
mef27 Avatar answered Nov 09 '22 16:11

mef27