I am new to CloudKit.
I would like to know how I can retrieve from the database only one image. I created a new record type and added a Asset type in it with the name EventsImage. Now I want to retrieve this image and place it in an ImageView I have on my View controller. This is an image of what the View looks like (it is not a tableViewController).
(There is an ImageView in the middle)
Here is a snippet in which I recover an image from my database. I fetch CKRecord using the record ID.
self.publicDatabase.fetch(withRecordID: recordID, completionHandler: { (record: CKRecord?, error: Error?) -> (Void) in
guard let record = record else
{
if let error = error
{
DispatchQueue.main.async
{
completionHandler(SeeYuuResult.error(message: error.localizedDescription))
}
}
return
}
// Here is where the image is recovered
if let asset = record["avatar"] as? CKAsset, let data = try? Data(contentsOf: asset.fileURL)
{
DispatchQueue.main.async
{
avatar_image = UIImage(data: data)
}
}
})
Related
I need to display the images that have been stored on the storage on Firebase. Right now, I only tracked the images using the link generated by function downloadURL:
func UploadImage(imageData: Data, path: String, completion: #escaping (String) -> ()){
let storage = Storage.storage().reference()
let uid = Auth.auth().currentUser?.uid
storage.child(path).child(uid ?? "").putData(imageData, metadata: nil) { (_, err) in
if err != nil{
completion("")
return
}
// Downloading Url And Sending Back...
storage.child(path).child(uid ?? "").downloadURL { (url, err) in
if err != nil{
completion("")
return
}
completion("\(url!)")
}
}
}
So all I can get is a hyperlink that is like: https://firebasestorage.googleapis.com/v0/b/getting-started-20f2f.appspot.com/o/profile_Photos%2FGQ1KR9H1mLZl2NAw9KQcRe7d72N2?alt=media&token=473ce86c-52ba-42ec-be71-32cc7dc895d7.
I refer to the official documentation, it seems that only when I have the name of the image file can I download it to an ImageView or UIImageView object. However, the link does not make any sense to me, so what can I do?
EDIT
I actually tried a solution provided by the official documentation:
func imageDownloader(_ imageURL: String) {
let store = Database.database().reference()
let uid = Auth.auth().currentUser?.uid
let imageRef = store.child(imageURL)
var myImageView = UIImageView()
imageRef.getData(completion: { (error, data) in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Data for "images/island.jpg" is returned
let image = UIImage(data: data!)
}
})
}
But it suggests that I need to change something because Cannot convert value of type 'DataSnapshot' to expected argument type 'Data'.
If you're storing the image paths in Firestore, actually the exact file name does not matter if there is only one file available under the fork. So you just need to specify the path.
To then download the image from Storage, construct the path and download:
let uid = Auth.auth().currentUser?.uid
Storage.storage().reference().child("the\path\to\your\uid\collection").child(uid).getData(maxSize: 1048576, completion: { (data, error) in
if let data = data,
let img = UIImage(data: data) {
// do something with your image
} else {
if let error = error {
print(error)
}
// handle errors
}
})
You are uploading to Storage.storage(), but then in your imageDownloader, you're attempting to use Database.database(), which has a similar-looking API, but is, in fact, different.
Make sure to use Storage.storage() and that the closure parameters are in the order data, error in.
Finally, right now in your imageDownloader, it doesn't look like you're doing anything yet with var myImageView = UIImageView(), but keep in mind that you won't have access to the UIImage until the async getData completes.
Store your images at Firebase Storage & then retrieve using this code.
Storage.storage().reference.child("ProfilePhotos").child("ImageName").downloadURL {(url, _) in
DispatchQueue.main.async {
guard let url = url else { return }
imageView.setImage(with: url, placeholder: UIImage(named: "dummyImage"))
}
}
In my project, I show a UITableView, which currently has text describing a show's name and genre loading from a remote JSON file.
That all works. What I want next is to use the URL from the JSON file and load a thumbnail next to each show.
Using a tutorial, I have added a function to download the remote image with a print to test if it's successful.
if let shows_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let shows_obj = shows_list[i] as? NSDictionary
{
let show_name = shows_obj["show"] as? String
let show_genre = shows_obj["genre"] as? String
let show_image = shows_obj["thumbnail"] as? String
TableData.append(show_name! + " | " + show_genre!)
let testPictureURL = URL(string: show_image!)!
let session = URLSession(configuration: .default)
// Here's the download task where I'm grabbing the image
let downloadPicTask = session.dataTask(with: testPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
if let res = response as? HTTPURLResponse {
print("Downloaded picture with response code \(res.statusCode)")
if let imageData = data {
// Now I know I have data, so I think I can use UIImage to convert it into an image
let image = UIImage(data: imageData)
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
downloadPicTask.resume()
}
There are three items in the JSON array, and I get three printed statements that the picture was download: but the image does not appear.
My theory: since this is a table, maybe I have to add this as an accessory, but there isn't an image accessory subclass.
I am new to Swift -- do you have any ideas about how I should append this uploaded image to the table.
This is probably being caused by the asynchronous behavior of URLSession so when the requested image returns the view is already loaded.
To solve that, you can use a callback, for instance:
func myFunction(completion: (returnedImage -> UIIMage) -> Void){
//...
let downloadPicTask = session.dataTask(with: testPictureURL) { (data, response, error) in
//...
let image = UIImage(data: imageData)
completion(returnedImage: image)
//...
}
downloadPicTask.resume()
}
}
By using a callback, let's say that you have a method called myFunction(completion:), so now when you call the method you can handle whatever comes back from completion:
myFunction { (image) in
DispatchQueue.main.async { cell.imageView.image = image }
}
Firebase Storage Image Not Downloading in Tableview. If I replace the line let tempImageRef = storage.child("myFiles/sample.jpg"), it's showing the single image. But if try to grab all the images inside 'myFiles' , it doesn't work. Please help
func fetchPhotos()
{
//let database = FIRDatabase.database().reference()
let storage = FIRStorage.storage().reference(forURL: "gs://fir-test-bafca.appspot.com")
let tempImageRef = storage.child("myFiles/")
tempImageRef.data(withMaxSize: (1*1024*1024)) { (data, error) in
if error != nil{
print(error!)
}else{
print(data!)
let pic = UIImage(data: data!)
self.picArray.append(pic!)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
You are referencing an entire folder (myFiles/). That wont work. You need to reference each image individually.
One way to do this is to write the image metadata to your realtime database when you upload an image. Then read the metadata (more specifically the path) and then reference the image that way.
If you have 3 images you want to upload and store, you could write the metadata as follows:
/images
/image1key
/metdata
/path
/image2key
/metdata
/path
/image3key
/metdata
/path
Then you can query your database for your images path's like so
let ref = Firebase.database().ref("images")
ref.observe(.value, withCompletion: { snapshot in
if let values = snapshot.value as? [String : Any] {
if let metadata = values["metadata"] as? [String: Any] {
let imagePath = metadata["path"]
//Now download your image from storage.
}
}
})
This isn't the cleanest code and you can definitely improve, but it will work :)
I'm downloading a picture from internet and storing its data locally then saving the path in my CoreData, this way:
getDataFromUrl(url!) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else { return }
print(response?.suggestedFilename ?? "")
print("Download Finished")
let filename = self.getDocumentsDirectory().stringByAppendingPathComponent(userKey as! String + ".png")
data.writeToFile(filename, atomically: true)
user.setValue(filename, forKey: "avatar")
do {
try managedContext.save()
}
catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
}
The save does seem to work (I debugged by printing the data received and the data inside the file once copied and I don't have any managedContext error).
On the next view, I do use a UITableView and on cellForRowAtIndexPath
let path = authorArray.objectAtIndex(indexPath.row).objectAtIndex(0).objectForKey("avatar")! as! String
let name = authorArray.objectAtIndex(indexPath.row).objectAtIndex(0).objectForKey("name")
do {
let data = try NSData(contentsOfFile: path, options: NSDataReadingOptions())
let image = UIImage(data: data)
cell.profilePicture.image = image
cell.profilePicture.layer.cornerRadius = cell.profilePicture.layer.cornerRadius / 2;
cell.profilePicture.layer.masksToBounds = true;
}
catch {
print("failed pictures")
}
The thing is I get the photo on my cell.profilePicture but as soon as I do any modification elsewhere and relaunch my application from xCode, I get the error message. The pictures path did not change but the datas obtained from it are nil. I can't find a reason why it does work until I update the code. Any solutions to make it work everytime ?
As pbasdf stated on comments, I was storing the whole Document directory path instead of just the filename + extension. Documents directory changes on every build.
I'm building app and I need to populate a TableView with some users infos, like username and the profile picture. The following code works for downloading and displaying the image.
if let imageFile = usersArr[indexPath.row]["profileImage"] as? PFFile {
let data = try? imageFile.getData()
cell?.imageView?.image = UIImage(data: data!)
}
But this is synchronously and I want it to be asynchronously. So I tried this.
if let imageFile = usersArr[indexPath.row]["profileImage"] as? PFFile {
imageFile.getDataInBackgroundWithBlock({ (data, error) -> Void in
if let imageData = data where error == nil {
cell?.imageView!.image = UIImage(data: imageData)
} else {
cell?.imageView!.image = UIImage(named: "defaultProfile")
}
})
}
But it's not working and I can't figure out why. Not even the defaultProfile image is appearing.
Any tips?
The issue is that the view is not being refreshed once the data has been fetched and image has been set.
I recommend using PFImageView from the ParseUI framework. It is a Parse-made image view which will automatically handle displaying a placeholder while loading, updating the data once available, and refreshing the view once the download is complete. Here's an example usage.
let imageView = PFImageView()
// Set placeholder image
imageView.image = UIImage(named: "placeholder")
// Set remote image
imageView.file = profilePicture
// Once the download completes, the remote image will be displayed
imageView.loadInBackground { (image: UIImage?, error: NSError?) -> Void in
if (error != nil) {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
} else {
println("image loaded")
}
}