Closed sobri909 closed 3 months ago
Oh, the ForeignKey(["timelineItemId"], to: ["id"])
on the toMany()
was a failed attempt to work around the problem (if I'm even identifying the problem correctly). It achieved nothing, with identical results to the plain public static let samples = hasMany(LocomotionSample.self).forKey("samples")
declaration.
Oh I missed out the row decoding:
@Observable
public class LocomotionSample: Record, Identifiable, Codable {
...
required init(row: Row) throws {
id = row["id"]
date = row["date"]
secondsFromGMT = row["secondsFromGMT"]
source = row["source"]
movingState = MovingState(rawValue: row["movingState"])!
recordingState = RecordingState(rawValue: row["recordingState"])!
timelineItemId = row["timelineItemId"]
latitude = row["latitude"]
longitude = row["longitude"]
altitude = row["altitude"]
horizontalAccuracy = row["horizontalAccuracy"]
verticalAccuracy = row["verticalAccuracy"]
speed = row["speed"]
course = row["course"]
classifiedActivityType = row["classifiedActivityType"]
confirmedActivityType = row["confirmedActivityType"]
try super.init(row: row)
}
...
}
@Observable
public class TimelineItemBase: Record, Identifiable, Codable {
...
required init(row: Row) throws {
id = row["id"]
source = row["source"]
isVisit = row["isVisit"]
startDate = row["startDate"]
endDate = row["endDate"]
deleted = row["deleted"]
previousItemId = row["previousItemId"]
nextItemId = row["nextItemId"]
stepCount = row["stepCount"]
floorsAscended = row["floorsAscended"]
floorsDescended = row["floorsDescended"]
averageAltitude = row["averageAltitude"]
activeEnergyBurned = row["activeEnergyBurned"]
averageHeartRate = row["averageHeartRate"]
maxHeartRate = row["maxHeartRate"]
try super.init(row: row)
}
...
}
I'm currently fiddling about in the debugger, trying to breakpoint in row decoding to see if I can find my whoopsie there. No luck so far. Feels like it's got to be some dumb typo, or silly misunderstanding of API, but I just can't see it yet 😑
Hello @sobri909,
Thanks for sponsoring the project! 💝
The mention of an _id table is mysterious
Agreed.
I suspect @Observable
.
I find a mention of _id
in https://callistaenterprise.se/blogg/teknik/2023/08/14/new-swift-observation/.
This article mentions underscore-prefixed keys as well: https://www.hackingwithswift.com/books/ios-swiftui/adding-codable-conformance-to-an-observable-class.
Maybe the best technique is to remove Codable
conformance, and provide an explicit implementation of encode(to:)
on the side of init(row:)
.
That's a lot of boilerplate 😬
Maybe detaching the data layer from SwiftUI could help (i.e. have some UI observable objects feed from immutable record structs). Codable structs don't need explicit encode(to:)
and init(row:)
. And given the bad interaction of @Observable
and Codable
, insisting on fighting the ~immaturity~ limits of Apple code might be the wrong way.
Ooh, you got it! Thanks!
Commenting out @Obserable
fixes it! 🎉
In retrospect, I should've been clued by the underscore. But I was too busy being convinced it was something to do with me using GRDB wrong. Heh.
For structs vs classes, yeah I initially went that way with the rewrite. But as I ported over more of the complexity from the old codebase it became clear structs weren't going to be up to challenge. I still have to cut corners for efficiency reasons in some places, so copying/refetching structs is still going to be too costly. Can't win 'em all.
Hopefully I'll be able to get away with only using Record classes for the core high-pressure models, and can rely on struct models for the rest. Fingers crossed.
Anyway, thanks again! It would've taken me forever to get to realising it might be to do with @Observable
.
Great, @sobri909 👍 I agree that structs are not a panacea, especially large ones!
Shall we close this issue? You can always open a new one if you have another question.
Yep, problem conclusively solved 👍🏼 Thanks again!
Hi! Long time! Hope you're doing well 😄
So I'm doing a rewrite of LocoKit, this time using GRDB's model classes and structs more directly, in the hope that I can get away with not using my custom caching tricks this time around.
Anyway, as part of the rebuild process I'm running into a weirdness that I can't seem to debug. I'm attempting to fetch an array of
TimelineItemBase
rows, with the associatesLocomotionSample
rows hanging off of each.The test query is:
Which is producing the error:
I'm really struggling to figure it out. When I prepare the statement and print it all I see is the base query of "SELECT * FROM TimelineItemBase", so I don't get to see the subquery / join details, and I've so far failed to step through to their generation in the debugger.
In the debugger it looks to me that it throws the exception when preparing the subquery/join for
LocomotionSample
, but I can't yet make sense of why. The mention of an_id
table is mysterious, and also curious that there's bothtimelineItemId
and a generatedgrdb_timelineItemId
duplicating it.I'm kind of stuck for where to look next! Any minor help would be greatly appreciated! Thanks 😄
The table schemas are thus (with [hopefully] irrelevant bits trimmed out):
And models thus: