Reputation: 85
I'm trying to implement a feature on my iOS app where users can select multiple media files (images and videos) from the app's storage, then save them to the device's Photos gallery and then delete them from the app's storage to free up space with the "Save" button. Initially, the save mechanism worked fine if there was no delete mechanism (the mediaFiles.removeAll {}
block):
struct GalleryView: View {
@ObservedObject var model: CameraModel
@Binding var mediaFiles: [Media]
@State private var isSelecting: Bool = false
@State private var selectedItems: Set<UUID> = Set<UUID>()
@State private var selectedMedia: Media?
@State private var showingDeleteConfirmation: Bool = false
@State private var showingSaveConfirmation: Bool = false
@State private var showToast: Bool = false
@State private var toastMessage: String = ""
var body: some View {
// ...
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
if !selectedItems.isEmpty {
showingSaveConfirmation = true
}
}
.alert(isPresented: $showingSaveConfirmation) {
Alert(title: Text("Save to Device"),
message: Text("Save selected items to your device? They'll be deleted from the app to save space."),
primaryButton: .default(Text("Save")) {
let selectedMediaFiles = mediaFiles.filter { media in
selectedItems.contains(media.id)
}
// Request permission to access the photo library
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
// Save each selected media file to the photo library
for media in selectedMediaFiles {
if let image = media.image {
// Save image
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
} else if let videoURL = media.videoURL {
// Save video
UISaveVideoAtPathToSavedPhotosAlbum(videoURL.path, nil, nil, nil)
}
}
// Delete the selected items from the app's storage
mediaFiles.removeAll { media in
if selectedItems.contains(media.id) {
// Delete the corresponding file from the app's storage
if let image = media.image {
let timestamp = "\(media.creationTime.timeIntervalSince1970)"
model.deleteImageFromAppStorage(withName: timestamp)
} else if let videoURL = media.videoURL {
model.deleteFileFromAppStorage(at: videoURL)
}
return true
}
return false
}
selectedItems.removeAll()
isSelecting = false
// Show toast message
toastMessage = "Selected Items Saved & Deleted"
showToast = true
} else {
// Handle the case where the user denied access
print("User denied access to the photo library.")
}
}
},
secondaryButton: .cancel())
}
}
}
}
}
struct Media: Identifiable {
let id = UUID()
let creationTime: Date
var image: UIImage?
var videoURL: URL?
}
struct MediaFileView: View {
var media: Media
var width: CGFloat
@Binding var isSelecting: Bool
@Binding var selectedItems: Set<UUID>
@Binding var selectedMedia: Media?
var body: some View {
// Display media file thumbnail
// ...
}
}
However, after introducing the delete functionality, the selected images were being saved and deleted correctly, but selected videos were only being deleted and not saved. I tried to address this issue by implementing an integer mechanism called VideosToBeSavedThenDelete
where the idea was to decrement this integer each time a video is saved and only run the delete block once this integer reaches 0:
// Top of GalleryView:
@State private var VideosToBeSavedThenDelete: Int = 0
// Save each selected media file to the photo library
for media in selectedMediaFiles {
if let image = media.image {
// Save image
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
} else if let videoURL = media.videoURL {
// Save video
UISaveVideoAtPathToSavedPhotosAlbum(videoURL.path, nil, nil, nil)
VideosToBeSavedThenDelete -= 1
}
}
// Delete the selected items from the app's storage
if VideosToBeSavedThenDelete == 0 {
mediaFiles.removeAll { media in
if selectedItems.contains(media.id) {
// Delete the corresponding file from the app's storage
if let image = media.image {
let timestamp = "\(media.creationTime.timeIntervalSince1970)"
model.deleteImageFromAppStorage(withName: timestamp)
} else if let videoURL = media.videoURL {
model.deleteFileFromAppStorage(at: videoURL)
}
return true
}
return false
}
}
However, videos were still not being saved to the Gallery and were only being deleted even with this approach. I understand that the saving images line runs synchronously while the saving videos line runs asynchronously. Why didn't the integer approach work as expected? Is there a better way to approach this problem?
Upvotes: 0
Views: 49