download and save a PFFile (UIImage) to show in a UIImageView : SWIFT - swift

I have a large class called "Goal" in parse. This class has multiple elements, one of which is a PFFile, that is always a UIImage.
When I perform my query for the "Goal" class, I cannot figure out how to take the PFFile, and change it to a UIImage for use.
var query = PFQuery(className:"Goal")
let currentUser = PFUser.currentUser()!.username
query.whereKey("creator", equalTo: currentUser!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects?.count) goals for the TableView.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
let goalType = object["type"] as! String
let goalPeriod = object["period"] as! String
let goalCategory = object["category"] as! String
let goalShortDescription = object["shortDescription"] as! String
let goalLongDescription = object["longDescription"] as! String
let goalPointvalue = object["pointValue"] as! Int
let goalSharedSetting = object["shared"] as! Bool
let goalAdoptionCount = object["adoptionCount"] as! Int
let goalIsComplete = object["isComplete"] as! Bool
let goalSuccessImageData = object["image"] as! PFFile
goalSuccessImageData.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.imageQuery = image
}
}
}
let goalSuccessImage : UIImage = self.imageQuery
let goalObjectID = object.objectId
let goalSpreadCount = object["spreadCount"] as! Int
let goalSpreadTotal = object["spreadTotal"] as! Int
let goalTotalCompletions = object["totalCompletions"] as! Int
let thisGoal = GoalModel(period: goalPeriod, type: goalType, category: goalCategory, shortDescription: goalShortDescription, longDescription: goalLongDescription, pointValue: goalPointvalue, shared: goalSharedSetting, adoptionCount: goalAdoptionCount, isComplete: goalIsComplete, successImage: goalSuccessImage, goalID: goalObjectID!, spreadCount: goalSpreadCount, spreadTotal: goalSpreadTotal, totalCompletions: goalTotalCompletions ) as GoalModel
any tips on how to modify the "success image" part? I added a space before and after to make it easier to find.
Thank you in advance!

I'm using this way in my projects, if it help's you :
func performSave(sender: UIBarButtonItem){
affichageActivityIndicator()
let qos = Int(QOS_CLASS_USER_INITIATED.value)
dispatch_async(dispatch_get_global_queue(qos,0)) { () -> Void in
dispatch_async(dispatch_get_main_queue()){
if let updateObject = self.currentObject as PFObject? {
let imageData = UIImageJPEGRepresentation(imageToSave, 0.1)
let imageFile = PFFile(name:"image.png", data:imageData)
updateObject["imageFile"] = imageFile
// Save the data back to the server in a background task
updateObject.saveInBackgroundWithBlock{(success: Bool, error: NSError!) -> Void in
UIApplication.sharedApplication().endIgnoringInteractionEvents()
if success == false {
println("Error")
}
}
}
}
}
}

Related

Use of unresolved identifier 'self' (CoreData)

I am using this line below :
self.present(activityViewController, animated: true, completion: nil)
And I am getting an error of - Use of unresolved identifier 'self'. Any ideas about how to resolve this? To me it looks as it it is subordinate to the class, but clearly doing something wrong. Any help would be appreciated.
import UIKit
import CoreData
class CoreDataViewController: UIViewController {
#IBOutlet weak var CoreDataView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var items:[Checkins]?
var btnnames = [""]
override func viewDidLoad() {
super.viewDidLoad()
// CoreDataView.dataSource = self
// CoreDataView.delegate = self
storeTranscription()
// Loads the current data
getTranscriptions()
// fetchCheckins()
let btn1name = btnnames[0]
let btn2name = btnnames[1]
let btn3name = btnnames[2]
let btn4name = btnnames[3]
let btn5name = btnnames[4]
let btn6name = btnnames[5]
// print(btnnames)
print(btn1name, btn2name, btn3name, btn4name, btn5name, btn6name)
}
#IBAction func export(_ sender: Any) {
exportDatabase()
}
#IBOutlet weak var Table_label: UILabel!
}
var CheckinDate: Date? = Date()
var fetchedStatsArray: [NSManagedObject] = []
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func storeTranscription() {
//retrieve the entity that we just created
let entity = NSEntityDescription.entity(forEntityName: "Checkins", in: context)
let transc = NSManagedObject(entity: entity!, insertInto: context) as! Checkins
//set the entity values
transc.who = "Who"
transc.reason = "Reason for visit"
transc.date = CheckinDate
//save the object
do {
try context.save()
print("saved!")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
}
}
func getTranscriptions () {
//create a fetch request, telling it about the entity
let fetchRequest: NSFetchRequest<Checkins> = Checkins.fetchRequest()
do {
//go get the results
let searchResults = try context.fetch(fetchRequest)
fetchedStatsArray = searchResults as [NSManagedObject]
//I like to check the size of the returned results!
print ("num of results = \(searchResults.count)")
//You need to convert to NSManagedObject to use 'for' loops
for trans in searchResults as [NSManagedObject] {
//get the Key Value pairs (although there may be a better way to do that...
print("\(trans.value(forKey: "who")!)")
let mdate = trans.value(forKey: "CheckinDate") as! Date
print(mdate)
}
} catch {
print("Error with request: \(error)")
}
}
func exportDatabase() {
let exportString = createExportString()
saveAndExport(exportString: exportString)
}
func saveAndExport(exportString: String) {
let exportFilePath = NSTemporaryDirectory() + "Checkins.csv"
let exportFileURL = NSURL(fileURLWithPath: exportFilePath)
FileManager.default.createFile(atPath: exportFilePath, contents: NSData() as Data, attributes: nil)
//var fileHandleError: NSError? = nil
var fileHandle: FileHandle? = nil
do {
fileHandle = try FileHandle(forWritingTo: exportFileURL as URL)
} catch {
print("Error with fileHandle")
}
if fileHandle != nil {
fileHandle!.seekToEndOfFile()
let csvData = exportString.data(using: String.Encoding.utf8, allowLossyConversion: false)
fileHandle!.write(csvData!)
fileHandle!.closeFile()
let firstActivityItem = NSURL(fileURLWithPath: exportFilePath)
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem], applicationActivities: nil)
activityViewController.excludedActivityTypes = [
UIActivity.ActivityType.assignToContact,
UIActivity.ActivityType.saveToCameraRoll,
UIActivity.ActivityType.postToFlickr,
UIActivity.ActivityType.postToVimeo,
UIActivity.ActivityType.postToTencentWeibo
]
self.present(activityViewController, animated: true, completion: nil)
}
}
func createExportString() -> String {
var checkinwho: String?
var checkinreason: String?
var export: String = NSLocalizedString("who, reason, date \n", comment: "")
for (index, itemList) in fetchedStatsArray.enumerated() {
if index <= fetchedStatsArray.count - 1 {
checkinwho = Checkins.value(forKey: "who") as! String?
checkinreason = itemList.value(forKey: "reason") as! String?
let Datevar = Checkins.value(forKey: "date") as! Date
let whostring = checkinwho
let reasonstring = checkinreason
let DateSting = "\(Datevar)"
export += "\(whostring!),\(reasonstring!),\(DateSting) \n"
}
}
print("This is what the app will export: \(export)")
return export
}
Remove the } on this line
#IBOutlet weak var Table_label: UILabel!
}
and put another } at the end of this file.

how to show an image from URL from AFnetworking with Swift?

This would be a super noob question but I have no idea how to convert URL to data and data to UIimage. The text labels are work really beautifully but the UI images are not working
I figured out there are some codes to convert them but don't know how to use them. could you help me out.
override func viewDidLoad() {
super.viewDidLoad()
let manager = AFHTTPSessionManager()
let url = mainURL + "sample.php"
manager.get(url, parameters: nil, progress: nil, success: { (task, res) in
guard let json = res as? [String: Any] else {
print ("not [String: Any]]")
return
}
if let array = json["data"] as? [Any] {
for i in 0 ..< array.count {
if let row = array [ i ] as? [String: Any] {
let model = Model(t: row ["title"] as! String,
n: row ["user"] as! String,
d: row ["regdate"] as! String,
imgUrl: row ["img"] as! String)
self.models.append(model)
}
self.tableView.reloadData()
}
}
}) { (task, error) in
print("error = \(error)")
}
self.tableView.estimatedRowHeight = 500
}
Model
class Model : NSObject {
//?
let data = try? Data(contetsOf: url!)
let img = UIImage(data: data!)
let imgView = UIImageView(image: img)
var title : String
var name : String
var date : String
var image : UIImage!
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = UIImage(named: imgUrl)
}
and the outlets.
mainCell.titleLabel.text = m.title //labels work fine
mainCell.nameLabel.text = m.name
mainCell.mainImgView.image = m.image
Try this
Just store image url in model class not image. Update your model class with below code
class Model : NSObject {
var title : String
var name : String
var date : String
var image : String
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = imgUrl
}
}
// past code inside cellforRowAt
// download image from image URL
let url = URL(string: image)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
mainCell.mainImgView.image = UIImage(data: data!)
}
}).resume()

Function does not return array. Download photos from Firebase Storage matching Firebase Database file names [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I have images stored in FireBase Storage, and matching file name data in FireBase Database, and I want to get those photos and display them (note, there is still some code I need to write because I am not getting EVERY photo from storage. Just those that are returned from a query of the database)
Here is the git repo
This code in DBHandler works, as I can see the print of the image file names
func photoListForLocation() -> [String]{
let file_name:String = String()
var photos = [file_name]
ref.observeSingleEvent(of: .value) { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot]{
for snap in snapshot {
if let data = snap.value as? [String:Any]{
let imageName:String = data["image_name"]! as! String
photos.append(imageName)
print("photos.append - \(imageName)")
}//if let data
}//for
}//snapshot
}//ref.observeSingleEvent
return photos
}//photoListForLocation
BUT the "return photos" never happens.. So the following in my ViewController does nothing..
let dbHandler:DBHandler = DBHandler()
var fileList = [String]()
fileList = dbHandler.photoListForLocation()
fileList.forEach {fileName in
print("\(fileName)")
}
Of course, if there is a better or simpler way of accomplishing my goal, I'm all ears.
for Mr. Tomato... (see comments)
import Foundation
import FirebaseDatabase
import GoogleMaps
class DBHandler {
var ref:DatabaseReference! = Database.database().reference().child("locations")
var imageCount:Int = 0
func addLocation(coordinate:CLLocationCoordinate2D, rating: Double, imageName: String?){
let location = ["latitude": coordinate.latitude,
"longitude": coordinate.longitude,
"rating": rating,
"image_name": imageName!,
"postDate": ServerValue.timestamp()
] as [String : Any]
self.ref.childByAutoId().setValue(location)
}//end setLocation
func getImageListForLocation(lattitude:Double, longitude:Double) -> [String]{
var images = [String]()
self.ref.observeSingleEvent(of: .value) { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot]{
for snap in snapshot {
if let data = snap.value as? [String:Any]{
let thisLattitude = data["latitude"]
let thisLongitude = data["longitude"]
guard let imageName = data["image_name"] else {return}
if lattitude == thisLattitude as! Double && longitude == thisLongitude as! Double {
images.append(imageName as! String)
}//if
}//if
}//for
}//if
}//ref
self.imageCount = images.count
return images //DOES NOT RETURN IMAGES!! (FILE NAMES)
}//getImageListForLocation
}//DBHandler
In order to get the photos and display them, you need to store the Storage URL of the photo location in your database for later use. Here are a couple of functions I created for a project that does this.
This application has a list of Angels that it saves and retrieves. Angels have names, numbers, emails, and images. I store a local array of these angels in a datasource I've defined in PageDataSource.sharedInstance(). The boolean crudIsAvailable is to make sure there is a connection. On verifying that CRUD operations are available I being scrubbing the list of angelsToSave:
func saveAngels(_ completion: #escaping(_ error: Error?) -> Void) {
if PageDataSource.sharedInstance.crudIsAvailable == true {
let angelsRef = ref.child("angels")
let myAngelsRef = angelsRef.child(id)
for item in PageDataSource.sharedInstance.angelsToSave {
let angel = PageDataSource.sharedInstance.angels[item]
let angelNameRef = myAngelsRef.child(angel.name!)
var angelToSave = getAngel(angel)
var jpegRepresentation : UIImage? = nil
if let photo = angel.photo {
jpegRepresentation = photo
} else {
jpegRepresentation = UIImage(named: "Anonymous-Seal")
}
if let photoData = UIImageJPEGRepresentation(jpegRepresentation!, 1.0) {
storePhoto(photoData, angel.name!, completion: { (url, err) in
if err != nil {
print(err?.localizedDescription)
angelToSave["photo"] = nil
myAngelsRef.updateChildValues(angelToSave, withCompletionBlock: { (error, ref) in
if error != nil {
completion(error!)
} else {
completion(nil)
}
})
} else {
angelToSave["photo"] = url?.absoluteString
angelNameRef.updateChildValues(angelToSave)
completion(nil)
}
})
}
}
} else {
completion(NSError(domain: "Unavailable", code: 0, userInfo: nil))
}
}
The important part of saveAngels() is if let photoData..... storePhoto() and here is the storePhoto() function.
func storePhoto(_ photo: Data, _ name: String, completion: #escaping (_ result: URL?, _ error: NSError?) -> Void) {
let storageRef = Storage.storage().reference().child(name)
storageRef.putData(photo, metadata: nil) { (storageMetaData, err) in
if err != nil {
completion(nil, NSError(domain: (err?.localizedDescription)!, code: 0, userInfo: nil))
} else {
completion(storageMetaData?.downloadURL(), nil)
}
}
}
The function storePhoto() returns the value of the URL through a completion handler and the saveAngels() function takes that information and uses it to store the data to the realtime database for future use.
For a better understanding here is my Angel object:
class Angel: NSObject {
var name: String?
var email: [String]?
var phone: [String]?
var photo: UIImage?
var filepath: String?
}
And here is how I download the photo:
First, I retrieve the list of photo URLs to an array of "angels" in a datasource singleton on VC load then I load the images as they appear in a collectionView like this.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! AngelCollectionViewCell
cell.imageView?.contentMode = .scaleAspectFill
cell.activityIndicator.hidesWhenStopped = true
cell.activityIndicator.startAnimating()
if let pic = PageDataSource.sharedInstance.angels[indexPath.row].photo {
cell.imageView?.image = pic
cell.activityIndicator.stopAnimating()
cell.setNeedsLayout()
} else if let imgURL = PageDataSource.sharedInstance.angels[indexPath.row].filepath {
Storage.storage().reference(forURL: imgURL).getData(maxSize: INT64_MAX, completion: { (data, error) in
guard error == nil else {
print("error downloading: \(error!)")
return
}
// render
let img = UIImage.init(data: data!)
// store to datasource
PageDataSource.sharedInstance.angels[indexPath.row].photo = img
// display img
if cell == collectionView.cellForItem(at: indexPath) {
DispatchQueue.main.async {
cell.imageView?.image = img
cell.activityIndicator.stopAnimating()
cell.setNeedsLayout()
}
}
})
} else {
// TODO: TODO: Change Image to a proper placeholder
cell.imageView.image = UIImage(contentsOfFile: "Angels#2x.png")
cell.Label.text = PageDataSource.sharedInstance.angels[indexPath.row].name!
cell.activityIndicator.stopAnimating()
}
return cell
}
The important code really starts with Storage.storage I hope this helps!!

Swift Firebase completion handler not working

My code calls Firebase and gets user information and user images. Once that's loaded, the app populates an image field and a text label. At least, that's what's supposed to happen. But instead, I'm getting fatal error: Index out of range error. What am I missing?
Here's my code:
#IBOutlet weak var userImage: UIImageView!
#IBOutlet weak var instructionsLabel: UILabel!
var currentUserName: String!
func loadExistingUsers(completion: #escaping () -> ()) {
ref.child("members").observe(.childAdded) { (snapshot: FIRDataSnapshot) in
if let dict = snapshot.value as? [String : Any] {
let userPhotoUrl = dict["profileImageUrl"] as! String
let userFirstName = dict["firstName"] as! String
let userBirthday = dict["birthday"] as! Int
let userPasscode = dict["passcode"] as! Int
let userGender = dict["gender"] as! String
let isUserChildOrParent = dict["childParent"] as! String
let storageRef = FIRStorage.storage().reference(forURL: userPhotoUrl)
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
let pic = UIImage(data: data!)
let user = User(profilePhoto: pic!,
userFirstName: userFirstName,
userBirthday: userBirthday,
userPasscode: userPasscode,
userGender: userGender,
isUserChildOrParent: isUserChildOrParent)
self.users.append(user)
self.users.sort(by: {$0.birthday < $1.birthday})
})
completion()
}
}
}
and my ViewDidLoad call:
loadExistingUsers {
self.currentUserName = self.users[0].firstName // error here
self.instructionsLabel.text = "Choose daily and weekly job assignments for \(self.users[0].firstName)."
self.userImage.image = self.users[0].photo
}
What am I doing wrong? I know my base code is good because I've tested it. Is it just the completion handler that's the problem? Any help would be greatly appreciated.

Return from initializer without initializing all stored properties error - yet everything is initialized

I'm running into this weird error. it was working fine until I added the image and imageString values and then this error happened:
Return from initializer without initializing all stored properties
I thought I initialized all the properties, not sure why this error is happening. Here is the custom object class
class JSONObject {
private let baseImageURL = "https://website.com"
var airbnbUS: Int
var airbnbLocal: Int
var imageString: String
var image: URL
init(airbnbUS: Int, airbnbLocal: Int, imageString: String, image: URL ){
self.airbnbUS = airbnbUS
self.airbnbLocal = airbnbLocal
self.imageString = imageString
self.image = image
}
init(resultsDictionary:[String: Any]){
guard let cost = resultsDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String : Any],
let usd = airbnb["USD"] as? Int,
let chf = airbnb["CHF"] as? Int
else {
airbnbUS = 0
airbnbLocal = 0
return
}
airbnbUS = usd
airbnbLocal = chf
guard let media = (resultsDictionary["media"] as? [String: Any]),
let imageDictionary = media["image"] as? [String: Any],
let image1000 = imageDictionary["1000"] as? String
else {
imageString = ""
image = URL(string: "\(baseImageURL)")!
return
}
imageString = image1000
image = URL(string: "\(baseImageURL)\(imageString)")!
}
}
The issue is in your resultsDictionary initializer. The return in your first guard statement could return from the initializer early, and the following guard statement (and the code where you assign the image and imageString properties) might not execute.
One solution is to change the first guard statement to an if-let statement.
init(resultsDictionary:[String: Any]){
if let cost = resultsDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String : Any],
let usd = airbnb["USD"] as? Int,
let chf = airbnb["CHF"] as? Int
{
airbnbUS = usd
airbnbLocal = chf
} else {
airbnbUS = 0
airbnbLocal = 0
}
guard let media = (resultsDictionary["media"] as? [String: Any]),
let imageDictionary = media["image"] as? [String: Any],
let image1000 = imageDictionary["1000"] as? String
else {
imageString = ""
image = URL(string: "\(baseImageURL)")!
return
}
imageString = image1000
image = URL(string: "\(baseImageURL)\(imageString)")!
}