New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What's the "right" way to use autoIncrementedPrimaryKey
?
#1435
Comments
Hello @OscarApeland, I think you'll find GRBD documentation that is slightly outdated about this, so your question is a perfect opportunity to clarify ideas about SQLite auto-incremented ids. It will be easier to update the doc after that. The recommended type for auto-incremented ids is the optional
|
That clarified everything I was wondering about and more - thank you very much! |
Great answer! Would be great to add to the documentation. In this example it's easy to keep the properties of public protocol Persistable: Codable, FetchableRecord, MutablePersistableRecord {
associatedtype ID: Hashable, DatabaseValueConvertible
static var databaseTableName: String { get }
}
public struct Persisted<V>: Identifiable, FetchableRecord, MutablePersistableRecord where V: Persistable {
static public var databaseTableName: String { V.databaseTableName }
public typealias ID = V.ID
public var id: V.ID
public var value: V
public init(row: GRDB.Row) throws {
id = row[Column("id")]
value = try V(row: row)
}
public func encode(to container: inout GRDB.PersistenceContainer) throws {
container[Column("id")] = id
try value.encode(to: &container)
}
}
struct Player: Persistable {
typealias ID = Tagged<Player, Int64>
var name: String
var score: Int
}
typealias PersistedPlayer = Persisted<Player>
func foo(dbQueue: DatabaseQueue) throws {
try dbQueue.write { db in
// Insert
var player = Player(name: "Arthur", score: 100)
let persistedPlayer = try player.insertAndFetch(db, as: PersistedPlayer.self)!
let persistedName = persistedPlayer.value.name
}
} |
Sure @johankool, go ahead if your app can profit from this For extra convenience, you may want to make it @dynamicMemberLookup
struct Persisted<V> ... {
subscript<T>(dynamicMember keyPath: KeyPath<V, T>) -> T {
value[keyPath: keyPath]
}
subscript<T>(dynamicMember keyPath: WritableKeyPath<V, T>) -> T {
get { value[keyPath: keyPath] }
set { value[keyPath: keyPath] = newValue }
}
}
// No `value` in sight!
let persistedName = persistedPlayer.name |
To clarify, does the try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("player")
} then we could have a different property for the ID. Maybe something like this: struct Player: Identifiable, Codable, PersistableRecord {
var player: Int64?
private let _uuid: UUID
enum Identifier: Codable, Hashable {
case db(Int64)
case temp(UUID)
}
// Use a random UUID as the id until the db creates an auto incrementing primary key
var id: Self.Identifier {
guard let player else { return .temp(self._uuid) }
return .db(player)
}
} Would GRDB/SQLite still have a conflict even if we used an approach like this? I guess my question is, is there a way that we could call |
Hello @DandyLyons, Using a custom identifier type is fruitful idea, yes. I've performed a few similar attempts but did not find anything that could reach the level of a recommended technique. Probably I didn't try hard enough, and fresh ideas are welcome. It's good that you can make something work in your app. Apps are the best place for exploring new techniques :-) I'm not personally found of toying with the schema itself, and by that I mean that I'd try to find a solution that has no impact on the schema (so that people can name their column "id" if that's what they feel like doing). If your And if it provided a custom implementation of
You can call You can also add an extension: // App code
extension Player {
static func find(_ db: Database, id: Int64) throws -> Player {
try Player.find(db, key: rowid)
}
} If you want to share this extension between multiple record types, define a protocol. If you want to discuss this, please open a new discussion. That's because I'd like this issue to remain focused on answers and solutions, rather than unbounded explorations. |
Fresh to GRDB. Loving it so far.
I can't seem to find documentation on the preferred method for creating objects with auto-incrementing IDs. Lets say I'm creating a table like this.
First of all, should this property be
Int
orInt64
? Then, how should the property be defined in the corresponding record?It seems like it needs to be either optional or implicitly-unwrapped-optional, because just initing with
Row(id: 0).insertAndFetch(db)
crashes due to non-unique IDs. Initing the model withRow(id: nil).insertAndFetch(db)
works, but that requires the Optional/IUO.Is using IUO's the intended method for creating objects with auto-incremented IDs?
The text was updated successfully, but these errors were encountered: