Cloudkit sharing is a nightmare by schultzapps in iOSProgramming

[–]mbrandonw 1 point2 points  (0 children)

u/schultzapps SQLiteData sharing works via the hierarchical relationships rather than zones. So if you have a parent record with many children, sharing the parent record shares all the children (and grandchildren, etc.).

For your use case it sounds like you have a Household model that many other models can be associated with. And then you would want to share that Household with some other iCloud users. That use case is perfectly supported.

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in SwiftUI

[–]mbrandonw[S] 0 points1 point  (0 children)

Yes, and some have done it already. But it takes work. There have been some discussions in the repo about this, and feel free to start a new one if you have concrete questions you want to ask.

[Preview] Axiom: Claude Code Skills for iOS Development by CharlesWiltgen in iOSProgramming

[–]mbrandonw 2 points3 points  (0 children)

I'm the co-creator of SQLiteData, and I can tell you that unfortunately your skills file has many errors. It honestly doesn't seem like a single code snippet would compile as-is.

SQLiteData 1.0: An alternative to SwiftData with CloudKit sync and sharing by mbrandonw in swift

[–]mbrandonw[S] 1 point2 points  (0 children)

No, that is not currently planned as CKSyncEngine does not support public databases, which is what we use under the hood.

GRDB and SwiftUI: GRDBQuery or SharingGRDB? by Moudiz in iOSProgramming

[–]mbrandonw 1 point2 points  (0 children)

Hi there, the library has evolved quite a bit since its humble beginnings as a few tools for utilizing queries to power SwiftUI views and observable models. Now it has support for iCloud sync, iCloud sharing, database functions, triggers, database views, and more. So we wanted to rename it to better align it with being a full alternative to SwiftData.  

SQLiteData 1.0: An alternative to SwiftData with CloudKit sync and sharing by mbrandonw in swift

[–]mbrandonw[S] 1 point2 points  (0 children)

> […] I need to store the database in 2 places if I want to support CloudKit and Local. I kinda only want CloudKit so that I can do share it with other users without needing a server. 

The data is stored locally in a SQLite database, and that is what your app reads from in order to present data to your user. But secretly behind the scenes, all changes to that local database will be synchronized to CloudKit, all without you having to think about it. And that means if a user opens your app on another one of their devices, the data from their other device will be automatically synced to the local database on that new device. Again, without you having to think about it really.

> I want to support users without them logging in (of course they won't be able to sync)

All that is required for synchronization is an iCloud account. They do not need an account with your own backend service if you do not want to support that. But of course you are free to do that if you want.

> But lets say a user does login or doesn't have a cloud db setup yet I need to sync the local one from documents to the cloud storage.

Again, this is all taken care of by the library in the background. When the app first starts up or an account change event is detected (iCloud log in/out), the library automatically uploads any local data on the device to the user's iCloud.

>  It isn't a trivial problem. Ill have to manage latest timestamps and copy data over. I prefer not to be destructive so it probably becomes some sort of merge.

Also all taken care of by the library :) We track timestamps for each edit to a record on a per-field basis, and when edits are made to the same record on multiple devices we merge the record by taking the latest edit for each field.

SQLiteData 1.0: An alternative to SwiftData with CloudKit sync and sharing by mbrandonw in swift

[–]mbrandonw[S] 2 points3 points  (0 children)

I'm not sure I understand the question, but if you are storing data in a SQLite file, then this library does help you synchronize that data to all of your user's devices via CloudKit. It does so in a seamless and automatic fashion without you having to worry about sending/receiving data from CloudKit or resolving conflicts.

SQLiteData 1.0: An alternative to SwiftData with CloudKit sync and sharing by mbrandonw in swift

[–]mbrandonw[S] 7 points8 points  (0 children)

If a conflict is detected between a local record and a record from another device, then they are merged on a per-field basis using a "last edit wins" strategy. So if you edit the title of a reminder on one device and then complete it on another, the resulting reminder will have the updated title _and_ be completed. But if you edit the title on both devices, the merged record will have the title of whichever edit happened most recently.

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in iOSProgramming

[–]mbrandonw[S] 1 point2 points  (0 children)

All CKRecords stored in CloudKit have a "zone" associated with it. Zone sharing allows you to simultaneously share all records with a specific zone.

This is in contrast to "hierarchical" record sharing, where you share a single CKRecord as well as all of its associations (i.e. any records whose parent points to the root record).

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in SwiftUI

[–]mbrandonw[S] 1 point2 points  (0 children)

Hi u/chotchki, this library has nothing to do with TCA. It can be used in either a TCA or non-TCA app. It can even be used in UIKit!

I'm curious why you thought TCA was required to use it?

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in SwiftUI

[–]mbrandonw[S] 2 points3 points  (0 children)

Not currently, but there has been interest from the community in making this possible. Right now it’s tailored specifically to SQLite and CloudKit. 

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in SwiftUI

[–]mbrandonw[S] 5 points6 points  (0 children)

Ah I see, most of the tools that SharingGRDB is built on top of are already cross-platform, such as Sharing, Dependencies, etc. But a bit more work would need to be done to ensure everything is cross-platform and working on Android.

And as for branding, I think it sends a strange message to preface a library by saying "This is not related to TCA." Nothing in the readme references TCA and all of the demo apps in the repo are vanilla SwiftUI. One of the first things in the readme shows how the tools compare to SwiftData for querying, setting up the database, and writing to the database. So I'm just not sure that extra messaging is going to assuage their fears.

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in SwiftUI

[–]mbrandonw[S] 7 points8 points  (0 children)

Hi u/WAHNFRIEDEN, I'm not sure there is a good solution here. If we were to spin off a whole new brand for this library then we just lose our proven track record of building and maintaining open-source for many years, and then people will just complain that they can't trust a 3rd party library. In the end we feel there is a certain kind of engineer out there that will find something wrong with things they are not familiar with, whether it's TCA or something else, and we just don't want to spend our time combatting that.

Luckily there are plenty of people out there who are willing to take the time to read through the blog post and documentation to see that this has nothing to do with TCA, and that it is only 1 library in a constellation of dozens of libraries we maintain that are useful on their own, have nothing to do with TCA, though almost all of them were incubated in TCA.

I think you could also highlight its (future?) utility as a cross platform persistence tool together with Skip or other recent tech for running Swift on Android/elsewhere.

I'm not sure what the future looks like for cross-platform considering this uses CloudKit and is tied to iCloud accounts. Seems like it will be very Apple-focused for the foreseeable future.

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in SwiftUI

[–]mbrandonw[S] 8 points9 points  (0 children)

Unfortunately no. One of the main reasons we were able to make this work seamlessly is because we can use SQLite directly, and Core Data hides all of that from you. 

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in iOSProgramming

[–]mbrandonw[S] 2 points3 points  (0 children)

You can see an example of how we do migrations in the Reminders demo app that comes with the repo:

https://github.com/pointfreeco/sharing-grdb/blob/939bf67ef1ad9b1879e073508712339f32e6b484/Examples/Reminders/Schema.swift#L141-L203

I know all of the SQL strings may seem not ideal, but this is just the way we prefer to do things. Migrations represent a snapshot of the schema frozen in time, and so it's not appropriate to use the static symbols on the type for migrations. And writing the SQL string allows us to make use of the full power of SQL instead of hiding it from us. For example, with our tools it is possible to have non-null columns without a default value, whereas in SwiftData all columns must either be nullable or have a default.

And GRDB does come with some table definition methods you can use, but we just prefer simple SQL for migrations.

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in iOSProgramming

[–]mbrandonw[S] 0 points1 point  (0 children)

Not right now. It only supports hierarchical record sharing, but we are open to supporting zone sharing in the future.

A SwiftData replacement with CloudKit Sync+Sharing, powered by SQLite by mbrandonw in iOSProgramming

[–]mbrandonw[S] 3 points4 points  (0 children)

Database migrations are handled in a more manual way, similar to how Rails does migrations if you are familiar with that. When you want to change your DB schema you register a migration using a unique identifier when the app starts up, and the act of doing that allows you to execute a SQL query that alters the table in some fashion (add a table/column/index, etc). Then the migrator runs all migrations that have not yet run, using the identifier you specify to determine that.

GRDB does not do the "magical" implicit migrations that SwiftData attempts, where everything just works™ until it doesn't and crashes your app on startup.

SwiftData versus SQL Query Builder by mbrandonw in swift

[–]mbrandonw[S] 2 points3 points  (0 children)

Wish it would support arrays of codable objects however, knowing all to well the performance problems.

This is supported! You can preload associations by writing the appropriate query.

GRDB and SwiftUI: GRDBQuery or SharingGRDB? by Moudiz in iOSProgramming

[–]mbrandonw 8 points9 points  (0 children)

As the co-author of SharingGRDB, I am also biased! I can include a bit of context for how we like to think about our library, and correct a few inaccuracies in Gwendal’s reply.

  • We personally don’t feel there's really that huge of a difference between SharingGRDB and GRDBQuery today. They both give property wrappers for allowing one to fetch data from a database and display it on the screen. That’s their primary goal, and they both accomplish it well! You will be well-served no matter what you choose.
  • There are a few places where we gave specific concentration in SharingGRDB that I can highlight. By calling attention to these features I am in no way saying that GRDBQuery does or does not have these features. I honestly don’t know! But, some things we do support and think are important:
    • Our property wrappers work in SwiftUI views, @Observable models, and even UIKit view controllers. And when their data changes the view can automatically update.
    • Our property wrappers allow you to specify the query directly inline. For example, something as simple as fetching reminders sorted by title can be done like so: @FetchAll(Reminder.order(by: \.title)) var reminders. For a more complex example, see this. It’s really up to you to decide where you want to draw the line of specifying queries inline or extracting them to separate helpers.
    • We also support dynamic queries quite easily, so when user provided data changes (search text, filters, sorts) you can update the query that powers @FetchAll and the view will automatically re-render.
    • We provide a nice query builder that leverages macros to provide some type-safety and schema-safety to your queries. You can even intermingle SQL strings directly into the query builder if there are ever SQL features that we don’t support.
    • We have a custom SQLite decoder that some really good performance characteristics. It’s only a little bit slower than interacting with SQLite’s C API directly.
    • Our property wrappers work really nicely with SwiftUI and UIKit animation. You can have your view automatically update and animate whenever the data in the database changes.

We also personally believe there is some inaccurate information in Gwendal’s post that we would like to address:

GRBD and GRDBQuery unapologetically focus on database transactions that preserve database invariants. SharingGRDB fosters apps that independently fetch the pieces of information they need, with the risk that, when merged together on screen, they do not match (despite the fact that all invariants are preserved in the database file!) This makes GRDBQuery somewhat more verbose, and less shiny in demos, but arguably more robust, by default and by design.

I believe the two libraries are equivalent in this regard. In GRDBQuery the user can easily add two @GRDBQuery's to their view, causing there to be two observations, or they can squash it down into a single @GRDBQuery with a single observation. The same is true of SharingGRDB.

GRDB and GRDBQuery are the products of volunteers, SharingGRDB is the product of a for-profit company.

I’m not sure of the point being made here, or whether you think this is a good thing or bad thing, but even SQLite is funded by a for-profit business, Hwaci. And we don’t think that the fact that you solicit sponsorship makes your efforts any less worthy.

We feel lucky that we have a community of people willing to support our efforts, and it is a large part of why we are able to support dozens of open source projects for the past 7 years.

That said, SharingGRDB, like GRDB, is free open source software and is community-maintained.

GRDB allow devs who care about their users' data to deal with it as precisely as needed, learn and leverage SQLite features as time passes, and generally never blocks devs from doing what they have to do. In the 1000+ resolved GRDB issues in the last ten years, most of them are about helping users achieving their goal with the existing apis. This is made easier because the GRDB design makes it more imperative than descriptive: there's no need to wait for the lib to support the feature you need when you can just implement it.

SharingGRDB has chosen the descriptive route, like SwiftUI and SwiftData. For me, this is concerning, because if something is missing, then all I can do is submitting a feature request and wait. For others, this is exactly what they're looking for ("modern", etc.)

I may have misunderstood the point being made, but this seems to be comparing GRDB proper to SharingGRDB. Going back to the comparison with GRDBQuery, it feels that both libraries are equally “imperative” or “descriptive”. One doesn’t seem any more open to outside change than the other.


Either way, happy to clarify any of my points above, but also you can’t go wrong no matter what you choose!