Skip to content

Commit 0a81d64

Browse files
Allow gallery image picker
1 parent 2b361e6 commit 0a81d64

File tree

4 files changed

+71
-37
lines changed

4 files changed

+71
-37
lines changed

Demo/PowerSyncExample/Components/TodoListRow.swift

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ struct TodoListRow: View {
55
let completeTapped: () -> Void
66
let deletePhotoTapped: () -> Void
77
let capturePhotoTapped: () -> Void
8+
let selectPhotoTapped: () -> Void
89

910
@State private var image: UIImage? = nil
1011

@@ -32,12 +33,20 @@ struct TodoListRow: View {
3233
Spacer()
3334
VStack {
3435
if todo.photoId == nil {
35-
Button {
36-
capturePhotoTapped()
37-
} label: {
38-
Image(systemName: "camera.fill")
36+
HStack {
37+
Button {
38+
capturePhotoTapped()
39+
} label: {
40+
Image(systemName: "camera.fill")
41+
}
42+
.buttonStyle(.plain)
43+
Button {
44+
selectPhotoTapped()
45+
} label: {
46+
Image(systemName: "photo.on.rectangle")
47+
}
48+
.buttonStyle(.plain)
3949
}
40-
.buttonStyle(.plain)
4150
} else {
4251
Button {
4352
deletePhotoTapped()
@@ -85,6 +94,7 @@ struct TodoListRow: View {
8594

8695
),
8796
completeTapped: {},
88-
deletePhotoTapped: {}
97+
deletePhotoTapped: {},
98+
capturePhotoTapped: {}
8999
) {}
90100
}

Demo/PowerSyncExample/Components/TodoListView.swift

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ struct TodoListView: View {
1111
@State private var newTodo: NewTodo?
1212
@State private var editing: Bool = false
1313

14-
@State private var selectedImage: UIImage?
1514
// Called when a photo has been captured. Individual widgets should register the listener
16-
@State private var onPhotoCapture: ((_: Data) async throws -> Void)?
17-
@State private var showCamera = false
15+
@State private var onMediaSelect: ((_: Data) async throws -> Void)?
16+
@State private var pickMediaType: UIImagePickerController.SourceType = .camera
17+
@State private var showMediaPicker = false
1818

1919
var body: some View {
2020
List {
@@ -56,25 +56,14 @@ struct TodoListView: View {
5656

5757
},
5858
capturePhotoTapped: {
59-
showCamera = true
60-
// Register a callback for successful image capture
61-
onPhotoCapture = { (_ fileData: Data) in
62-
guard let attachments = system.attachments
63-
else {
64-
return
65-
}
66-
67-
try await attachments.saveFile(
68-
data: fileData,
69-
mediaType: "image/jpeg",
70-
fileExtension: "jpg"
71-
) { tx, record in
72-
_ = try tx.execute(
73-
sql: "UPDATE \(TODOS_TABLE) SET photo_id = ? WHERE id = ?",
74-
parameters: [record.id, todo.id]
75-
)
76-
}
77-
}
59+
registerMediaCallback(todo: todo)
60+
pickMediaType = .camera
61+
showMediaPicker = true
62+
},
63+
selectPhotoTapped: {
64+
registerMediaCallback(todo: todo)
65+
pickMediaType = .photoLibrary
66+
showMediaPicker = true
7867
}
7968
)
8069
}
@@ -84,8 +73,11 @@ struct TodoListView: View {
8473
}
8574
}
8675
}
87-
.sheet(isPresented: $showCamera) {
88-
CameraView(onPhotoCapture: $onPhotoCapture)
76+
.sheet(isPresented: $showMediaPicker) {
77+
CameraView(
78+
onMediaSelect: $onMediaSelect,
79+
mediaType: $pickMediaType
80+
)
8981
}
9082
.animation(.default, value: todos)
9183
.navigationTitle("Todos")
@@ -143,6 +135,32 @@ struct TodoListView: View {
143135
self.error = error
144136
}
145137
}
138+
139+
/// Registers a callback which saves a photo for the specified Todo item if media is sucessfully loaded.
140+
func registerMediaCallback(todo: Todo) {
141+
// Register a callback for successful image capture
142+
onMediaSelect = { (_ fileData: Data) in
143+
guard let attachments = system.attachments
144+
else {
145+
return
146+
}
147+
148+
do {
149+
try await attachments.saveFile(
150+
data: fileData,
151+
mediaType: "image/jpeg",
152+
fileExtension: "jpg"
153+
) { tx, record in
154+
_ = try tx.execute(
155+
sql: "UPDATE \(TODOS_TABLE) SET photo_id = ? WHERE id = ?",
156+
parameters: [record.id, todo.id]
157+
)
158+
}
159+
} catch {
160+
self.error = error
161+
}
162+
}
163+
}
146164
}
147165

148166
#Preview {
@@ -154,13 +172,15 @@ struct TodoListView: View {
154172
}
155173

156174
struct CameraView: UIViewControllerRepresentable {
157-
@Binding var onPhotoCapture: ((_: Data) async throws -> Void)?
175+
@Binding var onMediaSelect: ((_: Data) async throws -> Void)?
176+
@Binding var mediaType: UIImagePickerController.SourceType
177+
158178
@Environment(\.presentationMode) var presentationMode
159179

160180
func makeUIViewController(context: Context) -> UIImagePickerController {
161181
let picker = UIImagePickerController()
162182
picker.delegate = context.coordinator
163-
picker.sourceType = .camera
183+
picker.sourceType = mediaType
164184
return picker
165185
}
166186

@@ -181,11 +201,12 @@ struct CameraView: UIViewControllerRepresentable {
181201
if let image = info[.originalImage] as? UIImage {
182202
// Convert UIImage to Data
183203
if let jpegData = image.jpegData(compressionQuality: 0.8) {
184-
if let photoCapture = parent.onPhotoCapture {
204+
if let photoCapture = parent.onMediaSelect {
185205
Task {
186206
do {
187207
try await photoCapture(jpegData)
188208
} catch {
209+
// The photoCapture method should handle errors
189210
print("Error saving photo: \(error)")
190211
}
191212
}

Sources/PowerSync/attachments/AttachmentsService.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public class AttachmentService {
238238
hasSynced: attachment.hasSynced,
239239
localUri: attachment.localUri,
240240
mediaType: attachment.mediaType,
241-
size: attachment.size,
241+
size: attachment.size
242242
)
243243

244244
try context.execute(
@@ -256,7 +256,7 @@ public class AttachmentService {
256256
updatedRecord.mediaType ?? NSNull(),
257257
updatedRecord.size ?? NSNull(),
258258
updatedRecord.state,
259-
updatedRecord.hasSynced ?? 0,
259+
updatedRecord.hasSynced ?? 0
260260
]
261261
)
262262

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
/// Creates a PowerSync Schema table for attachment state
12
public func createAttachmentsTable(name: String) -> Table {
23
return Table(
3-
name: name, columns: [
4+
name: name,
5+
columns: [
46
.integer("timestamp"),
57
.integer("state"),
68
.text("filename"),
@@ -9,6 +11,7 @@ public func createAttachmentsTable(name: String) -> Table {
911
.text("media_type"),
1012
.integer("size"),
1113
.text("meta_data"),
12-
], localOnly: true
14+
],
15+
localOnly: true
1316
)
1417
}

0 commit comments

Comments
 (0)