Update Swift Quickstart for Ditto v5 + Swift 6 / strict-concurrency readiness #236
Merged
Aaron LaBeau (biozal) merged 15 commits intomainfrom May 5, 2026
Merged
Update Swift Quickstart for Ditto v5 + Swift 6 / strict-concurrency readiness #236Aaron LaBeau (biozal) merged 15 commits intomainfrom
Aaron LaBeau (biozal) merged 15 commits intomainfrom
Conversation
…Is and ran swiftlint
Contributor
There was a problem hiding this comment.
Pull request overview
Updates the Swift Tasks quickstart to use DittoSwift v5 APIs and project settings, aligning the sample app’s initialization/sync flows and documentation with the upcoming major release.
Changes:
- Migrates Ditto initialization and sync calls to the v5 API surface (new
DittoConfig/Ditto.open,ditto.sync.*). - Updates Xcode project/SwiftPM dependency to DittoSwiftPackage
5.0.0and adjusts UI toolbar layout. - Refreshes the Swift quickstart README links and setup instructions for v5 terminology.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| swift/Tasks/TasksListScreen.swift | Updates observer/sync usage for v5 and tweaks toolbar UI (Toggle label / bottom bar button). |
| swift/Tasks/TasksApp.swift | Switches startup to async Ditto initialization via DittoManager.initDitto(). |
| swift/Tasks/TaskModel.swift | Adds Foundation import (for Data, UUID, etc.). |
| swift/Tasks/DittoManager.swift | Reworks Ditto lifecycle to async open + auth refresh handler; makes ditto optional/published. |
| swift/Tasks.xcodeproj/xcshareddata/xcschemes/Tasks.xcscheme | Updates scheme metadata (upgrade version). |
| swift/Tasks.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | Pins DittoSwiftPackage to 5.0.0. |
| swift/Tasks.xcodeproj/project.pbxproj | Removes DittoObjC linkage, updates package requirement, and updates build settings. |
| swift/README.md | Updates prerequisites, API reference link, and setup steps for v5 terms. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
John Cunningham (SausCode)
approved these changes
May 5, 2026
Contributor
John Cunningham (SausCode)
left a comment
There was a problem hiding this comment.
Looks good overall, approved with a few nits
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR migrates the Swift quickstart from Ditto SDK 4.14.3 → 5.0.0 and brings the sample up to Swift 6 / strict-concurrency standards.
1. SDK migration (DittoSwift 4.x → 5.0.0)
Initialization — sync init → async
Ditto.openThe v4 synchronous identity-based initializer is gone. v5 introduces an async config-based open.
Ditto(identity: .onlinePlayground(appID:token:enableDittoCloudSync:customAuthURL:))try await Ditto.open(config: DittoConfig(databaseID:connect: .server(url:)))appIDdatabaseIDcustomAuthURLconnect: .server(url:)enableDittoCloudSync: falsetransportConfig.connect.webSocketURLs.insert(...)try ditto.disableSyncWithV3()Token refresh now uses
auth.expirationHandler+auth.loginDittoManager.initDitto()registers an expiration handler that re-authenticates withauth.login(token:provider: .development)when the token nears expiry.Sync API namespacing
ditto.startSync()ditto.sync.start()ditto.stopSync()ditto.sync.stop()ditto.isSyncActiveditto.sync.isActiveDQL strict mode bootstrap removed
v5 no longer requires
ALTER SYSTEM SET DQL_STRICT_MODE = falsefor unfielded SELECTs against the quickstart schema, soTasksApp.tasknow just callsDittoManager.shared.initDitto().Package dependency
DittoSwiftPackagebumped to5.0.0(upToNextMinor).DittoObjCproduct dependency dropped —DittoSwiftnow ships its ObjC bridge internally.2. Concurrency / Swift 6 strict-concurrency
The PR-review feedback flagged a real data-race bug in the initial v5 port (
@Published dittomutated from a non-MainActor continuation). Audit found and fixed several relatedissues so the sample is Swift 6 strict-concurrency clean and a faithful reference for SDK consumers.
DittoManagernot@MainActor—self.ditto = ...afterawait Ditto.open(...)could resume off-main and publish from a background thread.@MainActoronDittoManager.EditScreenViewModelnot@MainActordespite owning four@Publishedproperties.@MainActor.Task {}blocks inTasksListScreenViewModelcapturedselfstrongly; four were onnonisolatedmethods — Swift 6sending selferrors and unbounded VM lifetime.[weak self]+guard let self;nonisolatedremoved fromsaveEditedTask,saveNewTask,deleteTask,populateTasksCollection(all are called only from@MainActorSwiftUI contexts).deinitaccessed@MainActor-isolatedsubscription,storeObserver,ditto—deinitis alwaysnonisolated, so this was a Swift 6 isolation violation.@MainActor func teardown(), invoked from.onDisappear.private let ditto = DittoManager.shared.dittocaptured the optional at init time — if the VM ever initialized beforeinitDitto()finished it would silently capturenilforever.private var ditto: Ditto? { DittoManager.shared.ditto }(computed).TaskModelhad only implicitSendablesynthesis despite crossing actor boundaries.struct TaskModel: Sendable.UserDefaults.standard.synchronize()call (no-op since iOS 12).Observer callback was already correct
The
DittoStoreObservercallback ininit()already hops back to MainActor viaTask { @MainActor [weak self] in self?.tasks = mappedTasks }— preserved unchanged.3. Project / housekeeping
LastUpgradeCheckbumped (1610→2620).LSApplicationCategoryType = public.app-category.developer-tools.STRING_CATALOG_GENERATE_SYMBOLS = YESfor both build configs.ToolbarItem→ToolbarItemGroup;HStack { Image; Text }→Label("New Task", systemImage: "plus").4. README
4.8.2→5.0.0.DITTO_WEBSOCKET_URLin setup steps (Auth URL covers it in v5).Test plan
ProgressView, thenTasksListScreenonceinitDitto()resolves.EditScreen— all CRUD paths work.ditto.sync.isActivereflects state; subscription is cancelled/recreated.auth.expirationHandlerre-auths without crash.TasksListScreen→teardown()runs (observers cancelled, sync stopped).