Accessing Data Outside of Closure - ios

I have an array in my class that I'm trying to fill within a closure. However, when I try to access/print the array contents, it seems to be empty outside of the closure. How do I store the data for use outside of the closure?
for index in 0..<6 {
let picNumber = index + 1
if let pica = currentuser.objectForKey("pic\(picNumber)") as? PFFile {
pica.getDataInBackgroundWithBlock({ (data:NSData!, error:NSError!) -> Void in
if error == nil {
self.pic1 = UIImage(data: data)
var imageS = scaleImage(self.pic1!, and: 200)
self.imagesForSection0.append(imageS)
}
})
println(self.imagesForSection0)
}
}

It is not empty outside the Closure
The method getDataInBackgroundWithBlock is async,it means that it will return first.So,you see nothing in print function.
Document
Asynchronously gets the data from cache if available or fetches its contents from the network.
Edit
for index in 0..<6 {
let picNumber = index + 1
if let pica = currentuser.objectForKey("pic\(picNumber)") as? PFFile {
pica.getDataInBackgroundWithBlock({ (data:NSData!, error:NSError!) -> Void in
if error == nil {
self.pic1 = UIImage(data: data)
var imageS = scaleImage(self.pic1!, and: 200)
self.imagesForSection0.append(imageS)
}
println(self.imagesForSection0)
//Then call reload data
})
}
}

Related

Can't pass variable value from firebase firestore to another class SWIFT

So I have this function in class Functions :
struct Prices {
var standardPrice: Int!
}
// FUNC PRICING
class Functions {
private var PricingRef: CollectionReference!
var price = Prices()
func getPrice() -> Prices {
PricingRef = Firestore.firestore().collection("ProductXYZ")
PricingRef.getDocuments { (snapshot, error) in
if let err = error {
debugPrint("Error fetching data \(err)")
}
else {
guard let snap = snapshot else { return }
for document in snap.documents {
let data = document.data()
let std = data["standard"] as! String
self.price.standardPrice = Int(std)!
print(self.price.standardPrice!) // This print the intended result
}
}
}
return price
}
}
Then I want to pass the standardPrice value to this class, called PriceList :
class PriceList: UITableViewController {
var price = Prices()
var newStandardPrice = 0
func Price() {
price = Functions().getPrice()
newStandardPrice = price.standardPrice // always error with value nil
}
I always have that error where newStandardPrice is nil.
but the print(self.price.standardPrice!) shows number of result I want.
So as far as I know, the problem here is because it takes time for the firebase firestore to get the data from database.
How do I get the value of standardPrice after its assigned with the new price from firebase database?
Any help will be appreciated
Thankyou
you need to use completion handler because its async function
func getPrice(completion:#escaping (Prices?,Error?)-> Void) {
PricingRef = Firestore.firestore().collection("ProductXYZ")
PricingRef.getDocuments { (snapshot, error) in
if let err = error {
debugPrint("Error fetching data \(err)")
completion(nil,err)
}
else {
guard let snap = snapshot else { return }
for document in snap.documents {
let data = document.data()
let std = data["standard"] as! String
self.price.standardPrice = Int(std)!
print(self.price.standardPrice!) // This print the intended result
completion(self.price.standardPrice,nil)
}
}
}
}
How to use
Functions().getPrice { (price, error) in
if let err = error {
// do something if you get error
} else if let getPrice = price {
// use price
self.price = getPriice
}

observeNext called more than once after insert element to MutableObservableArray in Bond Swift Framework

I have a MutableObservableArray object which has a binding with observeNext function of Bond framework. At the first opening of the app, I fetch array from user defaults and insert it to this empty array.
My problem is that after I insert element to array, observeNext function called three times not once. What can be the problem?
var list = MutableObservableArray<Task>([])
_ = self.list.observeNext(with: { element in
if element.diff.count != 0 {
if element.diff.deletes.count >= 1 && element.collection.count == 0 {
self.restoreUserDefaults(with: false)
} else {
self.saveListToUserDefaults(list: element.collection)
}
}
})
Insert Function:
if let savedTask = userDef.object(forKey: self.userDefaultsKeyForList) as? Data {
let decoder = JSONDecoder()
if let loadedList = try? decoder.decode([Task].self, from: savedTask) {
self.list.batchUpdate({ (a) in
a.insert(contentsOf: loadedList, at: 0)
})
}
}
EDIT: I think when It first initialize array with [] It also gets into observeNext. Is it normal?

altering a variable outside a closure

I am currently encountering a problem. I have a function with an array which has items needing appending to. The items are appended in a closure inside the function and I can see the items in the array only inside the closure. Since the function has a return I need the appended items to be viewed by the function as a whole and not just the array. What can I do to solve this?
var trueOrFalse: Bool = false
var tempArray:[String] = []
let reference_message = reference(.Append).whereField("delay", isEqualTo: 0)
reference_message.getDocuments { (snapshot, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let snapshot = snapshot else { return }
let documents = snapshot.documents
if documents != nil {
for document in documents {
let messageID = document[kMESSAGEID] as? String
tempArray.append(messageID!)
//print(trueOrFalse)
}
}
if trueOrFalse {
if opened && trueOrFalse {
print("Successful Walloping")
}
} else if !trueOrFalse {
if !opened || !trueOrFalse {
decryptedText = placeholderText
}
}
return JSQMessage(senderId: userId, senderDisplayName: name, date: date, text: decryptedText)

Unable to infer closure type in the current context

On the 3rd line in the function below I get the following error:
Unable to infer closure type in the current context
How do I fix this?
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in //error here
var images: [URL] = []
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
if let imageURL = URL(string: value) {
print(imageURL, "image url here")
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
self.tableView.reloadData()
}
}
}
})
}
The reason why it is not inferring the closure type is because the try statement is not handled. This means that the closure expected to "catch" the error, but in your case, you forgot the do-try-catch rule.
Therefore you can try the following answer which will catch your errors:
do {
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
} catch {
print("imageURL was not able to be converted into data") // Assert or add an alert
}
You can then assert an error (for testing), or what I would personally do, is set up an alert.
This way the app wouldn't crash, but instead, notify the user. I find this very helpful when on the go and my device isn't plugged in - so I can see the error messages instead of a blank crash with no idea what happened.
This error can also happen if you have a non related compilation error in your closure body. For example, you may be trying to compare two or more non-boolean types.
extension Array where Element == Resistance {
init(_ points: [Point]) {
let peaks = points.beforeAndAfter { (before, current, after) -> Bool in
before < current && current > after
}
self = []
}
}
will produce Unable to infer closure type in the current context.
The correct code:
extension Array where Element == Resistance {
init(_ points: [Point]) {
let peaks = points.beforeAndAfter { (before, current, after) -> Bool in
before.value < current.value && current.value > after.value
}
self = []
}
}
In addition to ScottyBlades answer, I'd like to add two data points to the "experience". It looks like referencing a non-existent property using self inside the block is not handled nicely by the compiler.
Nice error inside the block:
// Setting a handler for an NWListener instance:
self.nwListener?.newConnectionHandler = { (_ connection: NWConnection) -> Void in
// Results in "Cannot find 'nonExistentProperty' in scope"
// in the line below:
guard let delegate = nonExistentProperty else { return }
}
Weird "Type of expression is ambiguous without more context" error: (note the new self in front of nonExistentProperty)
// Setting a handler for an NWListener instance:
// Results in "Type of expression is ambiguous without more context"
// at the equals sign below:
self.nwListener?.newConnectionHandler = { (_ connection: NWConnection) -> Void in
guard let delegate = self.nonExistentProperty else { return }
}

Retrieve multiple images from Parse with getdatainbackground (IOS - Swift)

I try to do a query with Parse containing a few Strings and Images that will be added to an array. The strings in the array are all in the right order but not the images. I think its probably because some images are smaller than the other ones and so they get appended to the array earlier than they are supposed to. Is there any way to "save" space in the array for the images to keep them in the right order? It's probably not that hard to solve that but I am a Newbie :( Thank you!
query.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) -> Void in
for object in objects! {
DispatchQueue.global(qos: .userInteractive).async {
// Async background process
if let imageFile : PFFile = self.bild.append(object.value(forKey: "Bild") as! PFFile) {
imageFile.getDataInBackground(block: { (data, error) in
if error == nil {
DispatchQueue.main.async {
// Async main thread
let image = UIImage(data: data!)
image2.append(image!)
}
} else {
print(error!.localizedDescription)
}
})
}
}
}
})
Your analysis is correct that the requests will complete in non-deterministic order, partly or mostly influenced by the amount of data that must be returned.
Instead of an array to which you append the UIImage (or data), use a mutable dictionary that maps strings to UIImage. A reasonable choice for the string key is the PFFile name.
EDIT I'm not a Swift writer, but I tried to express the idea below (don't depend on it compiling, but I think the idea is sound)
class MyClass {
var objects: [PFObject] = []
var images: [String: UIImage] = [:] // we'll map names to images
fetchObjects() {
// form the query
query.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) -> Void in
self.objects = objects
self.fetchImages()
})
}
fetchImages() {
for object in self.objects! {
if let imageFile : PFFile = object["Bild"] as PFFile {
self.fetchImage(imageFile);
}
}
}
fetchImage(imageFile: PFFile) {
imageFile.getDataInBackground(block: { (data, error) in
if error == nil {
self.images[imageFile.name] = UIImage(data: data!)
// we can do more here: update the UI that with image that has arrived
// determine if we're done by comparing the count of images to the count of objects
} else {
// handle error
}
}
}
}
This will get the images in the background and keep them associated with their filenames using a dictionary. The OP code didn't explain what self.bild is, but I assumed it was an instance array of retrieved PFFiles. I replaced this with the images instance var.
Image file order is maintained by the object collection: to get the Nth image, get the Nth object, get it's "Bild" property, that PFFile's name is the key into your images dictionary.
var n = // some index into objects
var object : PFObject = self.objects[n]
var file : PFFile = object["Bild"]
var name : String = file.name
var nthImage = self.images[name] // is nil before fetch is complete

Resources