Issue with Swift Closures - swift

I'm having an issue retrieving data from within an closure. I'm calling function called getWallImages which is supposed to return an array. I can print the contents of the array from within the closure, but outside of it the array is empty.
import Foundation
import Parse
class WallPostQuery {
var result = [WallPost]()
func getWallImages() -> [WallPost] {
let query = WallPost.query()!
query.findObjectsInBackgroundWithBlock { objects, error in
if error == nil {
if let objects = objects as? [WallPost] {
self.result = objects
//This line will print the three PFObjects I have
println(self.result)
}
}
}
//this line prints [] ...empty array?
println(result)
return self.result
}
}
Question
How do I get values out of a closure?

That is because println(result) is executed BEFORE self.results = objects. The closure is executed asynchronously so it executes afterwards. Try making a function that uses results which can be called form the closure:
var result = [WallPost]()
func getWallImages() {
let query = WallPost.query()!
query.findObjectsInBackgroundWithBlock { objects, error in
if error == nil {
if let objects = objects as? [WallPost] {
self.result = objects
//This line will print the three PFObjects I have
println(self.result)
self.useResults(self.result)
}
}
}
}
func useResults(wallPosts: [WallPost]) {
println(wallPosts)
}
}
Another solution to your problem, so that you can return it from that function is to create your own closure:
var result = [WallPost]()
func getWallImages(completion: (wallPosts: [WallPost]?) -> ()) {
let query = WallPost.query()!
query.findObjectsInBackgroundWithBlock { objects, error in
if error == nil {
if let objects = objects as? [WallPost] {
self.result = objects
//This line will print the three PFObjects I have
println(self.result)
completion(wallPosts: self.result)
} else {
completion(wallPosts: nil)
}
} else {
completion(wallPosts: nil)
}
}
}
func useResults(wallPosts: [WallPost]) {
println(wallPosts)
}
}

What is happening is that the method is returning before the closure executes.
Fundamentally, you're running into a problem with they way you are managing asynchronous callbacks.
Asynchronous vs synchronous execution, what does it really mean?
You need to create a way of notifying your caller from within your closure. You can achieve this by: requiring your own closure as an input parameters; using a delegate pattern; using a notification.
https://codereview.stackexchange.com/questions/87016/swift-ios-call-back-functions
Each has their benefits/drawbacks, and it depends on your particular situation. The simplest way to get started with async data fetches, is to pass in your own closure. From there, you can jump to another pattern such as the delegate pattern if the need arises.

I think the latter of println(result) is called before because findObjectsInBackgroundWithBlock is executed on background as its name suggests.
So, you can confirm result in the following way,
import Foundation
import Parse
class WallPostQuery {
var result = [WallPost]() {
didSet {
println(result)
}
}
func getWallImages() {
let query = WallPost.query()!
query.findObjectsInBackgroundWithBlock { objects, error in
if error == nil {
if let objects = objects as? [WallPost] {
self.result = objects
//This line will print the three PFObjects I have
println(self.result)
}
}
}
}
}

Related

Closure var not capture string

This code is not working, how do I repair it?
I try to handle the string and return it, but no result. printing does not output anything to the console
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let x = "hello"
let viewm = viewmodel()
viewm.handler(x)
viewm.handler = { item in
print(item)
}
viewm.execute { (tt) in
print(tt)
}
}
class viewmodel {
var handler:(String) -> Void = {
(data: String) in
}
func execute(complete: #escaping (String) -> Void) {
self.handler = complete
}
}
It is not clear what you are trying to achieve here, but if your issue is that print(item) is not printing anything, that is happening because this code by itself is not going to print anything:
viewm.handler = { item in
print(item)
}
handler property is a closure that receives a String parameter and returns Void. On your viewDidLoad method, you are assigning a closure that complies with those characteristics and, in addition to that, is going to print the passed parameter.
Given that closures are self-contained blocks of functionality, you need to call it in order to execute its block of functionality; but you need to call it after it has been defined; otherwise, it is not going to print anything.
If you write your code in this order, "hello" is going to be printed.
viewm.handler = { item in
print(item)
}
viewm.handler(x)

Accessing Firestore data outside of Function [duplicate]

This question already has an answer here:
Assign value of a Firestore document to a variable
(1 answer)
Closed 3 years ago.
I have a FireStore function in my FirestoreService file as below;
func retrieveDiscounts() -> [Discount] {
var discounts = [Discount]()
reference(to: .discounts).getDocuments { (snapshots, error) in
if error != nil {
print(error as Any)
return
} else {
guard let snapshot = snapshots else { return }
discounts = snapshot.documents.compactMap({Discount(dictionary: $0.data())})
}
}
return discounts
}
how do I get returned values to populate my private var discounts = [Discount]() variable in my viewController
Many thanks as always...
Your functions will get your UI to freeze until its operation is complete. The function which may take long duration to complete should be done asyncronous using escaping closures. The function should be like below :
func retrieveDiscounts(success: #escaping([Discount]) -> ()) {
var discounts = [Discount]()
reference(to: .discounts).getDocuments { (snapshots, error) in
if error != nil {
print(error as Any)
success([])
return
} else {
guard let snapshot = snapshots else { return }
discounts = snapshot.documents.compactMap({Discount(dictionary: $0.data())})
success(discounts)
}
}
}
Note: The data returns empty if error. Please handle error case if you need.
We first need an instance of FirestoreService class. Then the instance should call the retrieveDiscounts() function and populate it to our instance i.e. discounts.
Code:
class ViewController: UIViewController {
private var discounts = [Discount]() {
didSet {
self.tableView.reloadData()
}
}
func viewDidLoad() {
super.viewDidLoad()
FirestoreService().retrieveDiscounts { discounts in
self.discounts = discounts
}
}
}

Swift: Weak referenced stored & nested blocks / closures

I'm looking to nest a block / closure whilst another process completes off of the main thread like so
typealias FirstBlock = (jsonDictionary:NSDictionary?,errorCode:NSString?) -> Void
typealias SecondBlock = (complete:Bool?,errorCode:NSString?,dictionary:NSDictionary?) -> Void
Controller
func startPoint {
SomeNetworkManager.sharedInstance.firstProcess(self.someDictionary) { (complete, errorCode, dictionary) -> Void in
// I want to get here with a strong reference to these objects in this class only
print(complete,errorCode,dictionary)
}
}
SomeNetworkManager
func firstProcess(dictionary:NSDictionary?, completion:SecondBlock?) {
let request = HTTPRequest.init(requestWithPath:"path", httpMethod: .post) { (jsonDictionary, errorCode) -> Void in
let organisedDictionary:NSMutableDictionary = NSMutableDictionary()
// Some processing of the json into a new dictionary
dispatch_async(dispatch_get_main_queue()) {
if errorCode == nil {
completion!(complete:true,errorCode:nil,dictionary:organisedDictionary)
}
else {
completion!(complete:false,errorCode:errorCode,dictionary:nil)
}
}
}
request.postDataDictionary = refinementsDictionary as! NSMutableDictionary
request.request()
}
HTTPRequest
var processBlock:FirstBlock?
init(requestWithPath path:NSString, httpMethod method:HTTPMethod, andProcessBlock block:FirstBlock) {
super.init()
self.requestURL = NSURL(string:path as String);
self.responseData = NSMutableData()
self.processBlock = block
switch (method) {
case .post:
self.httpMethod = kPost
break;
case .put:
self.httpMethod = kPut
break;
default:
self.httpMethod = kGet
break;
}
}
// An NSURLConnection goes off, completes, I serialise the json and then...
func completeWithJSONDictionary(jsonDictionary:NSDictionary) {
self.processBlock!(jsonDictionary:jsonDictionary,errorCode:nil)
self.processBlock = nil
}
I'm missing something fundamental regarding ARC retain cycles because every time one of these is called I'm getting a memory leak.. I've had a look at
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
with no joy.. I think Defining a Capture List is the right area, but as for storing a block and how to define it I have no idea what I'm doing wrong.
In all likelihood, you're getting retain cycles because the completion block references the HttpRequest (probably via the calling object), references the completion block, something like:
class HttpReference {
let completion : ()->()
init(completion:()->()) {
self.completion = completion
}
}
class Owner {
var httpReference : HttpReference?
func someFunction() {
httpReference = HttpReference() {
print(self.httpReference)
}
}
}
There are two ways to break the cycle, either by using an unowned reference or a by using a weak reference, both are fairly similar, in this case, the norm would be to use an unowned reference to self by changing:
func someFunction() {
httpReference = HttpReference() { [unowned self] in
print(self.httpReference)
}
}
Now, self isn't retained, thus breaking the retain cycle.

Swift.... Class method vs. Instance method

Thanks in advance for help!!
I'm trying to call a func from within my Class and I keep getting an error saying that:
Missing parameter for argument #1.............Read a few posts saying it's an instance vs class problem? I don't get it..I'm calling the method from within the Class??? There has to be an instance of the class if the method is being called????? right? Here is my code...Thanks
import Foundation
import Parse
class TestViewController {
let photos = getWallImages() //-----This is the line requesting an argument
func getWallImages() -> [WallPost] {
let query = WallPost.query()!
query.findObjectsInBackgroundWithBlock { objects, error in
if error == nil {
if let objects = objects as? [WallPost] {
return objects
println("We have \(objects.count)")
}
} else if let error = error {
println(error)
}
}
}
}
So the "crime" you are committing is the fact that the method is applied in an instance of the class and not as a class method. The function is expecting a self parameter (a reference to the instance). That explains the error message.
Now to fix that you have two quick options:
1. Make it a class function and call it that way too:
class TestViewController {
let photos = TestViewController.getWallImages()
class func getWallImages() -> [WallPost] {
// mumbo jumbo
}
}
This approach is problematic in case you would want to do some instance specific operations, because class func is static method and doesn't provide you with some of the object benefits.
2. Instantiate the object you are calling the method on:
class TestViewController {
let photos = TestViewController().getWallImages()
func getWallImages() -> [WallPost] {
// mumbo jumbo
}
}
This approach isn't correct with your given structure - it doesn't make sense to instantiate another view controller, but if you take the method and put it in a separate class, maybe it would then make sense.
Then of course you have multiple other ways of changing your code to make it work. Maybe you could initialize it with lazy parameter, maybe you could initialize it in the init method. Whatever suits you best. My answer is simply explaining where you've gone wrong.
There are a few ways you can set your property appropriately. You can make getWallImages() a type method:
class TestViewController {
let photos = TestViewController.getWallImages()
class func getWallImages() -> [WallPost] {
....
}
}
Or, you can keep your method an instance method and set your property upon initialization:
class TestViewController {
let photos: [WallPost]!
init() {
super.init()
photos = getWallImages()
}
func getWallImages() -> [WallPost] {
....
}
}
If you're asking a question you should reduce your code to a minimum, discarding unnecessary details.
You probably want something like this:
class MyClass {
let x = MyClass.getStuff()
static func getStuff() -> Int {
return 0
}
}
However your method getWallImages() can't do something like this, because it's returning the result asynchronous, which means you get the result much later after the function has returned.
You could do something like this though (this is how I'd be doing it):
class MyClass {
var x : Int? {
didSet {
if let x = x {
// Do something with x, here it's assigned
} else {
// x was set to nil, something failed
}
}
}
init() {
getStuffAsynchronous()
}
func getStuffAsynchronous() {
// Do your query stuff here, assign x to your objects to invoke the didSet
x = 0
// If it fails somehow, assign set x to nil
// if fail {
// x = nil
// }
}
}

Get data off completionhandler - FBRequestConnection

I found a lot of information concerning completionHandlers. However, I don't yet get how to handle it in this case. Maybe you can help me.
I'm trying to get a list of all Facebook friends and store it in an array.
getFacebookFriends
func getFacebookFriends() -> NSMutableArray
{
var retFriendIDs:NSMutableArray = []
FBRequestConnection.startForMyFriendsWithCompletionHandler {
(connection:FBRequestConnection!, result: AnyObject!, error:NSError!) -> Void in
if (error == nil){
var resultdict = result as! NSDictionary
self.data = resultdict.objectForKey("data") as! NSArray
var friendIDs:NSMutableArray = []
for (var i = 0; i < self.data.count; i++) {
let valueDict : NSDictionary = self.data[i] as! NSDictionary
let id = valueDict.objectForKey("id") as! String
friendIDs.addObject(id as String)
}
retFriendIDs = friendIDs
}
}
return retFriendIDs
}
As this is a completionHandler I understand that the return fires before completion of the block. But how do I achieve a return to use the list in this function for example?
findFacebookFriendsInBackend
func findFacebookFriendsInBackend() -> [AnyObject]{
println("findFacebookFriendsInBackend")
var retFriends:[AnyObject] = []
let fbFriends:NSMutableArray = getFacebookFriends()
var friendQuery = PFUser.query()
// look for friends in Parse
friendQuery!.whereKey("fbId", containedIn: fbFriends as [AnyObject])
friendQuery!.findObjectsInBackgroundWithBlock{
(friends, error) -> Void in
if error == nil {
retFriends = friends as [AnyObject]!
}
}
return retFriends
}
And then use it in a simple function like this:
iterateOverFriends
func iterateOverFriends(friends:[AnyObject]!){
for i in friends {
doSomething(i)
}
}
To call
iterateOverFriends(findFacebookFriendsInBackend())
Short answer: You don't. Not possible. With an async method, the result doesn't exist at the time when you return.
You have to adjust your thinking.
Rewrite this method:
func findFacebookFriendsInBackend() -> [AnyObject]{
As something like this instead:
func findFacebookFriendsInBackend(#completion:
(friendsArray: [AnyObject]) -> ())
In the new form it takes a block as a parameter. Write the function so that it invokes the block once it has built the array of friends.
The new call would look like this
findFacebookFriendsInBackend()
{
(friendsArray) -> () in
iterateOverFriends(friendsArray)
}
(When you have a function that takes a closure as the final parameter, you can call it with the closure outside the parenthesis.)
I wrote a long answer, including a working example project, in this thread.