Skip to content

Commit 4843623

Browse files
update demo. Allow deleting attachments.
1 parent 671761b commit 4843623

File tree

4 files changed

+187
-57
lines changed

4 files changed

+187
-57
lines changed

Demo/PowerSyncExample.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
B69F7D862C8EE27400565448 /* AnyCodable in Frameworks */ = {isa = PBXBuildFile; productRef = B69F7D852C8EE27400565448 /* AnyCodable */; };
4141
B6B3698A2C64F4B30033C307 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B369892C64F4B30033C307 /* Navigation.swift */; };
4242
B6FFD5322D06DA8000EEE60F /* PowerSync in Frameworks */ = {isa = PBXBuildFile; productRef = B6FFD5312D06DA8000EEE60F /* PowerSync */; };
43+
BE2F26EC2DA54B2F0080F1AE /* SupabaseRemoteStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE2F26EB2DA54B2A0080F1AE /* SupabaseRemoteStorage.swift */; };
44+
BE2F26EE2DA555E10080F1AE /* PhotoPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE2F26ED2DA555DD0080F1AE /* PhotoPicker.swift */; };
4345
/* End PBXBuildFile section */
4446

4547
/* Begin PBXCopyFilesBuildPhase section */
@@ -106,6 +108,8 @@
106108
B6F421372BC42F450005D0D0 /* core.klib */ = {isa = PBXFileReference; lastKnownFileType = file; path = core.klib; sourceTree = "<group>"; };
107109
B6F4213D2BC42F5B0005D0D0 /* sqlite3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sqlite3.c; path = "../powersync-kotlin/core/build/interop/sqlite/sqlite3.c"; sourceTree = "<group>"; };
108110
B6F421402BC430B60005D0D0 /* sqlite3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sqlite3.h; path = "../powersync-kotlin/core/build/interop/sqlite/sqlite3.h"; sourceTree = "<group>"; };
111+
BE2F26EB2DA54B2A0080F1AE /* SupabaseRemoteStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupabaseRemoteStorage.swift; sourceTree = "<group>"; };
112+
BE2F26ED2DA555DD0080F1AE /* PhotoPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPicker.swift; sourceTree = "<group>"; };
109113
/* End PBXFileReference section */
110114

111115
/* Begin PBXFrameworksBuildPhase section */
@@ -193,6 +197,7 @@
193197
B65C4D6B2C60D36700176007 /* Screens */ = {
194198
isa = PBXGroup;
195199
children = (
200+
BE2F26ED2DA555DD0080F1AE /* PhotoPicker.swift */,
196201
6A9669032B9EE6FA00B05DCF /* SignInScreen.swift */,
197202
B65C4D6C2C60D38B00176007 /* HomeScreen.swift */,
198203
B65C4D702C60D7D800176007 /* SignUpScreen.swift */,
@@ -218,6 +223,7 @@
218223
B65C4D6F2C60D58500176007 /* PowerSync */ = {
219224
isa = PBXGroup;
220225
children = (
226+
BE2F26EB2DA54B2A0080F1AE /* SupabaseRemoteStorage.swift */,
221227
6A7315BA2B98BDD30004CB17 /* SystemManager.swift */,
222228
6A4AD3842B9EE763005CBFD4 /* SupabaseConnector.swift */,
223229
6ABD78772B9F2D2800558A41 /* Schema.swift */,
@@ -564,9 +570,11 @@
564570
B65C4D6D2C60D38B00176007 /* HomeScreen.swift in Sources */,
565571
6A7315882B9854220004CB17 /* PowerSyncExampleApp.swift in Sources */,
566572
B666585F2C62115300159A81 /* ListRow.swift in Sources */,
573+
BE2F26EC2DA54B2F0080F1AE /* SupabaseRemoteStorage.swift in Sources */,
567574
B66658632C621CA700159A81 /* AddTodoListView.swift in Sources */,
568575
B666585D2C620E9E00159A81 /* WifiIcon.swift in Sources */,
569576
6A9669042B9EE6FA00B05DCF /* SignInScreen.swift in Sources */,
577+
BE2F26EE2DA555E10080F1AE /* PhotoPicker.swift in Sources */,
570578
6A7315BB2B98BDD30004CB17 /* SystemManager.swift in Sources */,
571579
);
572580
runOnlyForDeploymentPostprocessing = 0;
Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,90 @@
11
import SwiftUI
22

33
struct TodoListRow: View {
4-
let todo: Todo
5-
let completeTapped: () -> Void
4+
let todo: Todo
5+
let completeTapped: () -> Void
6+
let deletePhotoTapped: () -> Void
7+
let capturePhotoTapped: () -> Void
8+
69
@State private var image: UIImage? = nil
710

8-
var body: some View {
9-
HStack {
10-
Text(todo.description)
11-
Group {
12-
if (todo.photoUri == nil) {
13-
// Nothing to display when photoURI is nil
14-
EmptyView()
15-
} else if let image = image {
16-
Image(uiImage: image)
17-
.resizable()
18-
.scaledToFit()
19-
} else if todo.photoUri != nil {
20-
// Only show loading indicator if we have a URL string
21-
ProgressView()
22-
.onAppear {
23-
loadImage()
24-
}}
25-
else {
26-
EmptyView()
27-
}
28-
11+
var body: some View {
12+
HStack {
13+
Text(todo.description)
14+
Group {
15+
if todo.photoUri == nil {
16+
// Nothing to display when photoURI is nil
17+
EmptyView()
18+
} else if let image = image {
19+
Image(uiImage: image)
20+
.resizable()
21+
.scaledToFit()
22+
} else if todo.photoUri != nil {
23+
// Only show loading indicator if we have a URL string
24+
ProgressView()
25+
.onAppear {
26+
loadImage()
27+
}
28+
} else {
29+
EmptyView()
30+
}
31+
}
32+
Spacer()
33+
VStack {
34+
if todo.photoId == nil {
35+
Button {
36+
capturePhotoTapped()
37+
} label: {
38+
Image(systemName: "camera.fill")
39+
}
40+
.buttonStyle(.plain)
41+
} else {
42+
Button {
43+
deletePhotoTapped()
44+
} label: {
45+
Image(systemName: "trash.fill")
46+
}
47+
.buttonStyle(.plain)
2948
}
30-
Spacer()
31-
Button {
32-
completeTapped()
33-
} label: {
34-
Image(systemName: todo.isComplete ? "checkmark.circle.fill" : "circle")
35-
}
36-
.buttonStyle(.plain)
49+
Spacer()
50+
Button {
51+
completeTapped()
52+
} label: {
53+
Image(systemName: todo.isComplete ? "checkmark.circle.fill" : "circle")
54+
}
55+
.buttonStyle(.plain)
56+
}
57+
}
3758
}
38-
}
39-
59+
4060
private func loadImage() {
4161
guard let urlString = todo.photoUri else {
4262
return
4363
}
44-
let url = URL(fileURLWithPath: urlString)
45-
46-
if let imageData = try? Data(contentsOf: url),
47-
let loadedImage = UIImage(data: imageData) {
48-
self.image = loadedImage
64+
65+
if let imageData = try? Data(contentsOf: URL(fileURLWithPath: urlString)),
66+
let loadedImage = UIImage(data: imageData)
67+
{
68+
image = loadedImage
4969
}
5070
}
5171
}
5272

53-
5473
#Preview {
5574
TodoListRow(
56-
todo: .init(
57-
id: UUID().uuidString.lowercased(),
58-
listId: UUID().uuidString.lowercased(),
59-
photoId: nil,
60-
description: "description",
61-
isComplete: false,
62-
createdAt: "",
63-
completedAt: nil,
64-
createdBy: UUID().uuidString.lowercased(),
65-
completedBy: nil
66-
)
75+
todo: .init(
76+
id: UUID().uuidString.lowercased(),
77+
listId: UUID().uuidString.lowercased(),
78+
photoId: nil,
79+
description: "description",
80+
isComplete: false,
81+
createdAt: "",
82+
completedAt: nil,
83+
createdBy: UUID().uuidString.lowercased(),
84+
completedBy: nil,
85+
86+
),
87+
completeTapped: {},
88+
deletePhotoTapped: {}
6789
) {}
6890
}

Demo/PowerSyncExample/Components/TodoListView.swift

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import SwiftUI
21
import IdentifiedCollections
2+
import SwiftUI
33
import SwiftUINavigation
44

55
struct TodoListView: View {
@@ -18,19 +18,40 @@ struct TodoListView: View {
1818
}
1919

2020
IfLet($newTodo) { $newTodo in
21-
AddTodoListView(newTodo: $newTodo, listId: listId) { result in
21+
AddTodoListView(newTodo: $newTodo, listId: listId) { _ in
2222
withAnimation {
2323
self.newTodo = nil
2424
}
2525
}
2626
}
2727

2828
ForEach(todos) { todo in
29-
TodoListRow(todo: todo) {
30-
Task {
31-
try await toggleCompletion(of: todo)
32-
}
33-
}
29+
TodoListRow(
30+
todo: todo,
31+
completeTapped: {
32+
Task {
33+
await toggleCompletion(of: todo)
34+
}
35+
},
36+
deletePhotoTapped: {
37+
guard let attachments = system.attachments,
38+
let attachmentID = todo.photoId
39+
else {
40+
return
41+
}
42+
Task {
43+
do {
44+
_ = try await attachments.deleteFile(attachmentId: attachmentID) { tx, _ in
45+
_ = try tx.execute(sql: "UPDATE \(TODOS_TABLE) SET photo_id = NULL WHERE id = ?", parameters: [todo.id])
46+
}
47+
} catch {
48+
self.error = error
49+
}
50+
}
51+
52+
},
53+
capturePhotoTapped: {}
54+
)
3455
}
3556
.onDelete { indexSet in
3657
Task {
@@ -42,7 +63,7 @@ struct TodoListView: View {
4263
.navigationTitle("Todos")
4364
.toolbar {
4465
ToolbarItem(placement: .primaryAction) {
45-
if (newTodo == nil) {
66+
if newTodo == nil {
4667
Button {
4768
withAnimation {
4869
newTodo = .init(
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import PhotosUI
2+
import SwiftUI
3+
4+
struct PhotoPicker: View {
5+
@State private var selectedImage: UIImage?
6+
@State private var imageData: Data?
7+
@State private var showImagePicker = false
8+
@State private var showCamera = false
9+
10+
var body: some View {
11+
VStack {
12+
if let selectedImage = selectedImage {
13+
Image(uiImage: selectedImage)
14+
.resizable()
15+
.scaledToFit()
16+
.frame(height: 300)
17+
18+
Text("Image data size: \(imageData?.count ?? 0) bytes")
19+
}
20+
21+
HStack {
22+
Button("Camera") {
23+
showCamera = true
24+
}
25+
26+
Button("Photo Library") {
27+
showImagePicker = true
28+
}
29+
}
30+
}
31+
.sheet(isPresented: $showCamera) {
32+
CameraView(image: $selectedImage, imageData: $imageData)
33+
}
34+
}
35+
}
36+
37+
struct CameraView: UIViewControllerRepresentable {
38+
@Binding var image: UIImage?
39+
@Binding var imageData: Data?
40+
@Environment(\.presentationMode) var presentationMode
41+
42+
func makeUIViewController(context: Context) -> UIImagePickerController {
43+
let picker = UIImagePickerController()
44+
picker.delegate = context.coordinator
45+
picker.sourceType = .camera
46+
return picker
47+
}
48+
49+
func updateUIViewController(_: UIImagePickerController, context _: Context) {}
50+
51+
func makeCoordinator() -> Coordinator {
52+
Coordinator(self)
53+
}
54+
55+
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
56+
let parent: CameraView
57+
58+
init(_ parent: CameraView) {
59+
self.parent = parent
60+
}
61+
62+
func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
63+
if let image = info[.originalImage] as? UIImage {
64+
parent.image = image
65+
66+
// Convert UIImage to Data
67+
if let jpegData = image.jpegData(compressionQuality: 0.8) {
68+
parent.imageData = jpegData
69+
}
70+
}
71+
72+
parent.presentationMode.wrappedValue.dismiss()
73+
}
74+
75+
func imagePickerControllerDidCancel(_: UIImagePickerController) {
76+
parent.presentationMode.wrappedValue.dismiss()
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)