Im tryna to upload some data and Picked Image to Storage , but its uploads like unknown format to Storage . Everything else is fine with the rest of the data.
This is Add Item View , I have form with text fields title, description and author, then I tapped on button "Done" its saves to collection "Items" in Firebase database, but it doesn't save Picked Images , its only saves like "unknown type" to Storage. What I missed?
I cleaned the code as much as possible and left only the relevant lines of code for easier reading.
struct AddItemView: View {
@Environment(\.presentationMode) private var presentationMode
@State var presentActionSheet = false
@State var showPicker: Bool = false
@State var pickedImages: [UIImage] = []
@State var picData : Data = .init(count: 0)
@State var image : UIImage?
// MARK: - State (Initialiser-modifiable)
@ObservedObject var viewModel = NewItemView()
var mode: Mode = .new
var completionHandler: ((Result<Action, Error>) -> Void)?
// MARK: - UI Components
var saveButton: some View {
Button(action: {
self.handleDoneTapped() // - look at the end of the code
}) {
Text(mode == .new ? "Done" : "Save")
}
.disabled(!viewModel.modified)
}
var body: some View {
NavigationView {
Group {
Section(header: Text("New Item")) {
TextField("Title", text: $viewModel.singleitem.title)
TextField("Description", text: $viewModel.singleitem.description)
}
Section(header: Text("Author")) {
TextField("Author", text: $viewModel.singleitem.author)
}
if mode == .edit {
Section {
Button("Delete item") { self.presentActionSheet.toggle() }
.foregroundColor(.red)
}
}
Button {
showPicker.toggle()
} label: {
Image(systemName: "plus")
}
TabView {
ForEach(pickedImages, id: \.self) { image in
GeometryReader { proxy in
let size = proxy.size
Image(uiImage: image)
}
.padding()
}
}
.frame(height: 450)
//.tabViewStyle(.page(indexDisplayMode: pickedImages.isEmpty ? .never : .always))
}
.navigationBarItems(leading: cancelButton, trailing: saveButton)
}
.popupImagePicker(show: $showPicker) { assets in
// MARK: Example
let manager = PHCachingImageManager.default()
let options = PHImageRequestOptions()
options.isSynchronous = true
DispatchQueue.global(qos: .userInteractive).async {
assets.forEach { asset in
manager.requestImage(for: asset, targetSize: .init(), contentMode: .default, options: options) { image, _ in
guard let image = image else { return }
DispatchQueue.main.async {
self.pickedImages.append(image)
}
}
}
}
}
// MARK: Grid Image Content
.navigationTitle(mode == .new ? "Item" : viewModel.singleitem.title)
.navigationBarTitleDisplayMode(mode == .new ? .inline : .large)
.navigationBarItems(
leading: cancelButton,
trailing: saveButton
)
.actionSheet(isPresented: $presentActionSheet) {
ActionSheet(title: Text("Are you sure?"),
buttons: [
.destructive(Text("Delete item"),
action: { self.handleDeleteTapped() }),
.cancel()
])
}
}
// MARK: - Action Handlers
func handleDoneTapped() { // then I press Done it should save all data from the View to database and Storage.
self.viewModel.handleDoneTapped()
self.uploadImage() // This function below (to upload image to storage)
self.dismiss()
}
func uploadImage() { // Not saves images from Picked Images, just unknown files.
let storage = Storage.storage().reference()
let userId = Auth.auth().currentUser?.uid
storage.child("itemImages").child(userId ?? "").putData(self.picData, metadata: nil) {
(_, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
storage.child("itemImages").child(userId ?? "").downloadURL {(url, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
}
}
}
}
Also I have New Item Class with all function to save it into database:
class NewItemView: ObservableObject {
// MARK: - Public properties
@Published var singleitem: SingleItem
@Published var modified = false
// MARK: - Internal properties
private var cancellables = Set<AnyCancellable>()
// MARK: - Constructors
init(singleitem: SingleItem = SingleItem(title: "", author: "", description: "", image: "")) {
self.singleitem = singleitem
self.$singleitem
.dropFirst()
.sink { [weak self] singleitem in
self?.modified = true
}
.store(in: &self.cancellables)
}
// MARK: - Firestore
private var db = Firestore.firestore()
private func addItem(_ singleitem: SingleItem) {
do {
var addedItem = singleitem
addedItem.userId = Auth.auth().currentUser?.uid
_ = try db.collection("items").addDocument(from: addedItem)
}
catch {
print(error)
}
}
and SingleItem with all var's:
struct SingleItem: Identifiable, Codable {
@DocumentID var id: String?
var title : String
var author : String
var description : String
@ServerTimestamp var createdTime: Timestamp?
var userId : String?
var image : String
}
enum CodingKeys: String, CodingKey {
case id
case title
case author
case description = ""
case image
}
In view of your new code/picture. The error tells you that in func uploadImage()
, you are assigning a binding array of pickedImages: [UIImage]
to a single picData
.
Use let picData: Data = pickedImages[0].jpegData(compressionQuality: 1)
, no $
, and change [0]
to whatever image index you want. You will have to check that this index exist, otherwise you will get an out of index error.
Note this let picData..
. in func uploadImage()
is completely different to the @State var picData...
you declare in AddItemView
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.