Skip to content

Attachments Helper #35

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

Merged
merged 43 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
baafffe
wip attachments
stevensJourney Apr 6, 2025
282ed06
wip: Add unit tests
stevensJourney Apr 6, 2025
18bcd4c
cleanup docs
stevensJourney Apr 8, 2025
7055b08
Add image preview to demo
stevensJourney Apr 8, 2025
671761b
format
stevensJourney Apr 8, 2025
4843623
update demo. Allow deleting attachments.
stevensJourney Apr 8, 2025
528f8c8
camera
stevensJourney Apr 9, 2025
1c12ddf
update secrets
stevensJourney Apr 14, 2025
74d3688
Merge remote-tracking branch 'origin/main' into attachmentshelper
stevensJourney Apr 14, 2025
52096f4
Add logger
stevensJourney Apr 14, 2025
aa60917
remove comma
stevensJourney Apr 14, 2025
71c1ad0
cleanup
stevensJourney Apr 14, 2025
2b361e6
Add readme and photo capability.
stevensJourney Apr 14, 2025
0a81d64
Allow gallery image picker
stevensJourney Apr 14, 2025
ec1eba1
fix test
stevensJourney Apr 14, 2025
f5e791d
improve locking for attachment syncing
stevensJourney Apr 15, 2025
4787b8c
Cleanup Attachment State enums. Better Errors.
stevensJourney Apr 15, 2025
a64555a
cleanup
stevensJourney Apr 15, 2025
156b31f
cleanup
stevensJourney Apr 15, 2025
9825bc8
Merge remote-tracking branch 'origin/main' into attachmentshelper
stevensJourney Apr 15, 2025
8c0e08d
fix build
stevensJourney Apr 15, 2025
2b4fc91
add changelog
stevensJourney Apr 15, 2025
4765bb2
cleanup
stevensJourney Apr 15, 2025
22c96e9
improve concurrency and closing of queues
stevensJourney Apr 15, 2025
495ce97
cleanup
stevensJourney Apr 15, 2025
d405ddd
Cleanup demo. Verify local storage.
stevensJourney Apr 15, 2025
2bdef94
Improve demo
stevensJourney Apr 16, 2025
7d7d9a9
update locks
stevensJourney Apr 16, 2025
88dbb16
update readme
stevensJourney Apr 16, 2025
75eb7ea
update locks in syncing service
stevensJourney Apr 16, 2025
bdf5444
update test directory
stevensJourney Apr 16, 2025
1cf4042
Merge remote-tracking branch 'origin/main' into attachmentshelper
stevensJourney Apr 16, 2025
e5d3aec
improve cancellations
stevensJourney Apr 16, 2025
ba03b37
test
stevensJourney Apr 16, 2025
f801ec1
Offload Image loading from main thread
stevensJourney Apr 16, 2025
e124ce3
improve camera detection. Better support for cancellations.
stevensJourney Apr 16, 2025
0cad9a4
updated changelog
stevensJourney Apr 16, 2025
f9a3d10
Attachments readme polish
benitav Apr 17, 2025
330d4bf
cleanup
stevensJourney Apr 17, 2025
ad7423c
Merge branch 'attachmentshelper' of github.com:powersync-ja/powersync…
stevensJourney Apr 17, 2025
f8e3d61
skip archiving attachments pendingUpload
stevensJourney Apr 22, 2025
5206c16
code cleanup. Update archiving logid
stevensJourney Apr 22, 2025
5821dff
update template
stevensJourney Apr 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 37 additions & 31 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Changelog

# 1.0.0-Beta.11

- Added attachment sync helpers

# 1.0.0-Beta.10

* Added the ability to specify a custom logging implementation
- Added the ability to specify a custom logging implementation

```swift
let db = PowerSyncDatabase(
schema: Schema(
Expand All @@ -19,69 +24,70 @@
logger: DefaultLogger(minSeverity: .debug)
)
```
* added `.close()` method on `PowerSyncDatabaseProtocol`
* Update `powersync-kotlin` dependency to version `1.0.0-BETA29`, which fixes these issues:
* Fix potential race condition between jobs in `connect()` and `disconnect()`.
* Fix race condition causing data received during uploads not to be applied.
* Fixed issue where automatic driver migrations would fail with the error:

- added `.close()` method on `PowerSyncDatabaseProtocol`
- Update `powersync-kotlin` dependency to version `1.0.0-BETA29`, which fixes these issues:
- Fix potential race condition between jobs in `connect()` and `disconnect()`.
- Fix race condition causing data received during uploads not to be applied.
- Fixed issue where automatic driver migrations would fail with the error:

```
Sqlite operation failure database is locked attempted to run migration and failed. closing connection
```

## 1.0.0-Beta.9

* Update PowerSync SQLite core extension to 0.3.12.
* Added queuing protection and warnings when connecting multiple PowerSync clients to the same database file.
* Improved concurrent SQLite connection support. A single write connection and multiple read connections are used for concurrent read queries.
* Internally improved the linking of SQLite.
* Enabled Full Text Search support.
* Added the ability to update the schema for existing PowerSync clients.
* Fixed bug where local only, insert only and view name overrides were not applied for schema tables.
- Update PowerSync SQLite core extension to 0.3.12.
- Added queuing protection and warnings when connecting multiple PowerSync clients to the same database file.
- Improved concurrent SQLite connection support. A single write connection and multiple read connections are used for concurrent read queries.
- Internally improved the linking of SQLite.
- Enabled Full Text Search support.
- Added the ability to update the schema for existing PowerSync clients.
- Fixed bug where local only, insert only and view name overrides were not applied for schema tables.

## 1.0.0-Beta.8

* Improved watch query internals. Added the ability to throttle watched queries.
* Added support for sync bucket priorities.
- Improved watch query internals. Added the ability to throttle watched queries.
- Added support for sync bucket priorities.

## 1.0.0-Beta.7

* Fixed an issue where throwing exceptions in the query `mapper` could cause a runtime crash.
* Internally improved type casting.
- Fixed an issue where throwing exceptions in the query `mapper` could cause a runtime crash.
- Internally improved type casting.

## 1.0.0-Beta.6

* BREAKING CHANGE: `watch` queries are now throwable and therefore will need to be accompanied by a `try` e.g.
- BREAKING CHANGE: `watch` queries are now throwable and therefore will need to be accompanied by a `try` e.g.

```swift
try database.watch()
```

* BREAKING CHANGE: `transaction` functions are now throwable and therefore will need to be accompanied by a `try` e.g.
- BREAKING CHANGE: `transaction` functions are now throwable and therefore will need to be accompanied by a `try` e.g.

```swift
try await database.writeTransaction { transaction in
try transaction.execute(...)
}
```
* Allow `execute` errors to be handled
* `userId` is now set to `nil` by default and therefore it is no longer required to be set to `nil` when instantiating `PowerSyncCredentials` and can therefore be left out.

## 1.0.0-Beta.5
- Allow `execute` errors to be handled
- `userId` is now set to `nil` by default and therefore it is no longer required to be set to `nil` when instantiating `PowerSyncCredentials` and can therefore be left out.

* Implement improvements to errors originating in Kotlin so that they can be handled in Swift
* Improve `__fetchCredentials`to log the error but not cause an app crash on error
## 1.0.0-Beta.5

- Implement improvements to errors originating in Kotlin so that they can be handled in Swift
- Improve `__fetchCredentials`to log the error but not cause an app crash on error

## 1.0.0-Beta.4

* Allow cursor to use column name to get value by including the following functions that accept a column name parameter:
`getBoolean`,`getBooleanOptional`,`getString`,`getStringOptional`, `getLong`,`getLongOptional`, `getDouble`,`getDoubleOptional`
* BREAKING CHANGE: This should not affect anyone but made `KotlinPowerSyncCredentials`, `KotlinPowerSyncDatabase` and `KotlinPowerSyncBackendConnector` private as these should never have been public.

- Allow cursor to use column name to get value by including the following functions that accept a column name parameter:
`getBoolean`,`getBooleanOptional`,`getString`,`getStringOptional`, `getLong`,`getLongOptional`, `getDouble`,`getDoubleOptional`
- BREAKING CHANGE: This should not affect anyone but made `KotlinPowerSyncCredentials`, `KotlinPowerSyncDatabase` and `KotlinPowerSyncBackendConnector` private as these should never have been public.

## 1.0.0-Beta.3

* BREAKING CHANGE: Update underlying powersync-kotlin package to BETA18.0 which requires transactions to become synchronous as opposed to asynchronous.
- BREAKING CHANGE: Update underlying powersync-kotlin package to BETA18.0 which requires transactions to become synchronous as opposed to asynchronous.
```swift
try await database.writeTransaction { transaction in
try await transaction.execute(
Expand All @@ -102,8 +108,8 @@ try await database.writeTransaction { transaction in

## 1.0.0-Beta.2

* Upgrade PowerSyncSqliteCore to 0.3.8
- Upgrade PowerSyncSqliteCore to 0.3.8

## 1.0.0-Beta.1

* Initial Beta release
- Initial Beta release
14 changes: 10 additions & 4 deletions Demo/PowerSyncExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
B69F7D862C8EE27400565448 /* AnyCodable in Frameworks */ = {isa = PBXBuildFile; productRef = B69F7D852C8EE27400565448 /* AnyCodable */; };
B6B3698A2C64F4B30033C307 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B369892C64F4B30033C307 /* Navigation.swift */; };
B6FFD5322D06DA8000EEE60F /* PowerSync in Frameworks */ = {isa = PBXBuildFile; productRef = B6FFD5312D06DA8000EEE60F /* PowerSync */; };
BE2F26EC2DA54B2F0080F1AE /* SupabaseRemoteStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE2F26EB2DA54B2A0080F1AE /* SupabaseRemoteStorage.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -106,6 +107,7 @@
B6F421372BC42F450005D0D0 /* core.klib */ = {isa = PBXFileReference; lastKnownFileType = file; path = core.klib; sourceTree = "<group>"; };
B6F4213D2BC42F5B0005D0D0 /* sqlite3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sqlite3.c; path = "../powersync-kotlin/core/build/interop/sqlite/sqlite3.c"; sourceTree = "<group>"; };
B6F421402BC430B60005D0D0 /* sqlite3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sqlite3.h; path = "../powersync-kotlin/core/build/interop/sqlite/sqlite3.h"; sourceTree = "<group>"; };
BE2F26EB2DA54B2A0080F1AE /* SupabaseRemoteStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupabaseRemoteStorage.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -218,6 +220,7 @@
B65C4D6F2C60D58500176007 /* PowerSync */ = {
isa = PBXGroup;
children = (
BE2F26EB2DA54B2A0080F1AE /* SupabaseRemoteStorage.swift */,
6A7315BA2B98BDD30004CB17 /* SystemManager.swift */,
6A4AD3842B9EE763005CBFD4 /* SupabaseConnector.swift */,
6ABD78772B9F2D2800558A41 /* Schema.swift */,
Expand Down Expand Up @@ -564,6 +567,7 @@
B65C4D6D2C60D38B00176007 /* HomeScreen.swift in Sources */,
6A7315882B9854220004CB17 /* PowerSyncExampleApp.swift in Sources */,
B666585F2C62115300159A81 /* ListRow.swift in Sources */,
BE2F26EC2DA54B2F0080F1AE /* SupabaseRemoteStorage.swift in Sources */,
B66658632C621CA700159A81 /* AddTodoListView.swift in Sources */,
B666585D2C620E9E00159A81 /* WifiIcon.swift in Sources */,
6A9669042B9EE6FA00B05DCF /* SignInScreen.swift in Sources */,
Expand Down Expand Up @@ -705,10 +709,11 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"PowerSyncExample/Preview Content\"";
DEVELOPMENT_TEAM = 6WA62GTJNA;
DEVELOPMENT_TEAM = ZGT7463CVJ;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSCameraUsageDescription = "Take Photos for Todo Completion";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand All @@ -721,7 +726,7 @@
MARKETING_VERSION = 1.0;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.powersync.PowerSyncExample;
PRODUCT_BUNDLE_IDENTIFIER = com.powersync.PowerSyncSwiftExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PowerSyncExample/PowerSyncExample-Bridging-Header.h";
Expand All @@ -742,10 +747,11 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"PowerSyncExample/Preview Content\"";
DEVELOPMENT_TEAM = 6WA62GTJNA;
DEVELOPMENT_TEAM = ZGT7463CVJ;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSCameraUsageDescription = "Take Photos for Todo Completion";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand All @@ -757,7 +763,7 @@
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.powersync.PowerSyncExample;
PRODUCT_BUNDLE_IDENTIFIER = com.powersync.PowerSyncSwiftExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PowerSyncExample/PowerSyncExample-Bridging-Header.h";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
"state" : {
"revision" : "443df078f4b9352de137000b993d564d4ab019b7",
"version" : "1.0.0-BETA28.0"
"revision" : "f306c059580b4c4ee2b36eec3c68f4d5326a454c",
"version" : "1.0.0-BETA29.0"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand Down
126 changes: 100 additions & 26 deletions Demo/PowerSyncExample/Components/TodoListRow.swift
Original file line number Diff line number Diff line change
@@ -1,36 +1,110 @@
import SwiftUI

struct TodoListRow: View {
let todo: Todo
let completeTapped: () -> Void

var body: some View {
HStack {
Text(todo.description)
Spacer()
Button {
completeTapped()
} label: {
Image(systemName: todo.isComplete ? "checkmark.circle.fill" : "circle")
}
.buttonStyle(.plain)
let todo: Todo
let isCameraAvailable: Bool
let completeTapped: () -> Void
let deletePhotoTapped: () -> Void
let capturePhotoTapped: () -> Void
let selectPhotoTapped: () -> Void

@State private var image: UIImage? = nil

var body: some View {
HStack {
Text(todo.description)
Group {
if let image = image {
Image(uiImage: image)
.resizable()
.scaledToFit()

} else if todo.photoUri != nil {
// Show progress while loading the image
ProgressView()
.onAppear {
loadImage()
}
} else if todo.photoId != nil {
// Show progres, wait for a URI to be present
ProgressView()
} else {
EmptyView()
}
}
Spacer()
VStack {
if todo.photoId == nil {
HStack {
if isCameraAvailable {
Button {
capturePhotoTapped()
} label: {
Image(systemName: "camera.fill")
}
.buttonStyle(.plain)
}
Button {
selectPhotoTapped()
} label: {
Image(systemName: "photo.on.rectangle")
}
.buttonStyle(.plain)
}
} else {
Button {
deletePhotoTapped()
} label: {
Image(systemName: "trash.fill")
}
.buttonStyle(.plain)
}
Spacer()
Button {
completeTapped()
} label: {
Image(systemName: todo.isComplete ? "checkmark.circle.fill" : "circle")
}
.buttonStyle(.plain)
}.onChange(of: todo.photoId) { _, newPhotoId in
if newPhotoId == nil {
// Clear the image when photoId becomes nil
image = nil
}
}
}
}
}
}

private func loadImage() {
guard let urlString = todo.photoUri else {
return
}

if let imageData = try? Data(contentsOf: URL(fileURLWithPath: urlString)),
let loadedImage = UIImage(data: imageData)
{
image = loadedImage
}
}
}

#Preview {
TodoListRow(
todo: .init(
id: UUID().uuidString.lowercased(),
listId: UUID().uuidString.lowercased(),
photoId: nil,
description: "description",
isComplete: false,
createdAt: "",
completedAt: nil,
createdBy: UUID().uuidString.lowercased(),
completedBy: nil
)
todo: .init(
id: UUID().uuidString.lowercased(),
listId: UUID().uuidString.lowercased(),
photoId: nil,
description: "description",
isComplete: false,
createdAt: "",
completedAt: nil,
createdBy: UUID().uuidString.lowercased(),
completedBy: nil,

),
isCameraAvailable: true,
completeTapped: {},
deletePhotoTapped: {},
capturePhotoTapped: {}
) {}
}
Loading
Loading