-
Notifications
You must be signed in to change notification settings - Fork 134
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
GRDB: Initial setup + migrations #2581
base: grdb-feature-branch
Are you sure you want to change the base?
Conversation
Generated by 🚫 Danger |
@@ -24,6 +55,564 @@ class DatabaseHelper { | |||
} | |||
} | |||
|
|||
// GRDB upgradeIfRequired | |||
private class func upgradeIfRequired(schemaVersion: inout Int32, dbPool: DatabasePool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will have a lot of code added in these branches. These are not new codes, just the same queries adapted to GRDB and FMDB.
I did look at maybe having some "magic" that would not require these changes but GRDB and FMDB works differently and return different results, thus making that quite hard. :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it looks that those changes are needed, could we use this change to create some protocol to abstract the DB operations, and then have FMDB implementation ( the current one) and another GRDB implementation?
Thanks @leandroalonso for setting this up! I really look forward to have the new layer working in our app! Just a few thoughts after my first test:
We need to be careful with this, especially when it's about keeping both data model updated. Right now the app doesn't fully work as the new data model is missing the
Related to this I'm wondering if (as mentioned by @SergioEstevao above) we can take this as an opportunity to refactor a bit the persisted layer and introduce something that can switch between the 2 framework underneath without impacting any other part in future. This should also reduce the duplication, keeping the possibility to delete a whole part we want. |
This was my initial idea. :) Things are not simple though. I'll try to explain why I landed to this solution. FMDB and GRDB are inherently differentThe way the database calls work is different between the two frameworks. Let's take this example from FMDB:
And the exact same code using GRDB:
There are a few fundamental differences there:
Can we create some But then we have another challenge: GRDB and FMDB produce different results when reading. FMDB returns The big challenge here is regarding FMDB, it needs to set types very explicitly: Eg.:
So I wonder if we can know what's the type in advance. It's something I would need to check out because I'm not sure — and if we can't infer the difference, then we need to be explicit, thus not being able to make a generic layer. What's our main goal?Is our goal to refactor the data layer so it's more generic? Or is our goal to move to GRDB as quickly as possible? or maybe is it both? The only thing I'll point out is that we don't know what we don't know. Maybe GRDB won't work for us for a reason that we don't know yet. That's why I landed on this quick and dirty concept: so we could use/test GRDB soon. Please don't get me wrong, I get both your and @SergioEstevao's feedback and I agree with it. Duplication is not good, you already made a case of a property that was missing and we surely don't want to be changing code in two different places. However, I think that for a more smart-generic solution, we'll need more time — thus we need to transform this into a proper project. In the meantime, I'll change this PR to a draft and submit another one (also as a draft) with the same idea presented here. I'll use the rest of my time to see if we can achieve some layer of abstraction. And then we can discuss where to go from there. :) |
@SergioEstevao @danielebogo to summarize the biggest challenge, take a look at this: https://github.com/Automattic/pocket-casts-ios/blob/trunk/Modules/DataModel/Sources/PocketCastsDataModel/Public/Model/Podcast%2BfromDatabase.swift To have a generic layer we need to convert both GRDB and FMDB returns into something a generic layer can work with. The big challenge here is FMDB because
Nevermind, I have an idea. :))) |
From what I see on the codebase We are passing around the DBQueue object and then execute commands like:
My idea was to abstract those methods in a protocol that will do these commands on each specific layer. As you say above we have the issue of converting from ResultSets to the model objects. Could this be handle with another abstraction that implements the reading from the most common base types?
|
@SergioEstevao there are differences though. You mentioned "From what I see on the codebase We are passing around the DBQueue object and then execute commands". Yes, but this is always encapsulated in a So how do we do in the example that I shared which is a write/read/write? Open multiple One thing I don't want to do is to rewrite the database layer in ways that will introduce different behaviors — such as this one I mentioned, opening multiple The return of the database layer is something I've figured out but the main difference between each database framework is the tricky part. |
GRDB author, here. GRDB is a direct descendant of FMDB, and I would not say that they are inherently different. On the contrary, both share a common way to access the database, which is the "database access methods":
Again, not really. You should probably carefully preserve those "blocks" (i.e. database access units) in your translation. At first you can just replace all You may also have a look at (now archived) http://github.com/groue/GRDBObjc, "FMDB-compatible bindings to GRDB.swift". It contains some compatibility notes that may be interesting. |
Important: these changes are targeting a feature branch. I don't like them, but I see no reason for having the GRDB library on the app until we're ready to test it.
This initial PR adds two things:
Additional details
My plan for doing this is as follows:
FMDB and GRDB at the same time: how and why
My plan is basically to duplicate the whole data layer so we have one part working with FMDB and the other with GRDB. Code duplication is not great but in this case, I think it will guarantee us a safe environment for test and flexibility when switching to GRDB.
As with every feature we develop, we have the
grdb
flag, which will control whether GRDB is enabled or not. However, at the beginning of the app GRDB or FMDB will be chosen. And this will persist until the app is killed. Once initializing it again, it will, one more time, use GRDB or FMDB. This allows us to avoid scenarios such as switching the flag remotely and apps changing their database framework in runtime, something that we do not want.Also, duplicating the code and classes will make it easier to delete code in the future. We can delete entire methods and classes as opposed to
ifs
.Once we have the whole data layer in GRDB, what's the plan?
My plan is:
To test
The feature flag is already enabled
.sqlite
file and look at the tables/columns createdChecklist
CHANGELOG.md
if necessary.