0

I have a UIPickerView I want to fill with an array of values. The array of values is coming from a function inside of one of my classes (using json to grab the values and then put them into an array). The data is being grabbed successfully, and added to an array inside the function, but it's not returning for some reason.

Here's my class:

class Supplier {

    var supplierId: Int
    var supplierName: String


    init(id: Int, name: String){
        supplierId = id
        supplierName = name
    }

    static func arrayOfSupplierNames() -> [String] {
        let urlString = Constants.Urls.Suppliers.List;
        let session = NSURLSession.sharedSession();
        let url = NSURL(string: urlString)!;
        var suppliers: Array<String> = []

        session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
            if let responseData = data {

                do {
                    let json = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.AllowFragments) as! Dictionary<String, AnyObject>;

                    if let suppliersDictionary = json["suppliers"] as? [Dictionary<String, AnyObject>] {

                        for aSupplier in suppliersDictionary {

                            if let id = aSupplier["id"] as? Int, let name = aSupplier["supplierName"] as? String {
                                let supplier = Supplier(id: id, name: name)
                                suppliers.append(supplier.supplierName)
                            }

                        }
                    }
                }catch {
                    print("Could not serialize");
                }
            }
            }.resume()

        return suppliers
    }
}

This seems to work because when I debug I can see the values being added to the array. I have another function in my ViewController that runs this function and adds it to a local array but the array returned from the function doesn't seem to get added to the array in the view controller:

func populateSuppliersArray() {

        let sup:Array = Supplier.arrayOfSupplierNames()

        for s in sup {
            supplierArray.append(s) //supplierArray is at the top scope of view controller. 
        }
    }

I even made the class function static so I wouldn't have to initialize the class just to use the function. I'm not sure this is the correct way. When I look at the sup variable while debugging it has zero values.

1
  • 1
    Your function returns an empty array because it contains an asynchronous task whose completion handler fills a local array after your function has exited. Commented Jan 26, 2016 at 18:15

1 Answer 1

1

the json data is received inside an asynchronous block. your function returns as soon as you call resume on dataTaskWithURL. you should pass a completion block as an argument to your arrayOfSupplierNames and pass the array to that completion block instead. You could modify your function like this:

// take a completion block as an argument
func arrayOfSupplierNames(completion: (([String]) -> Void)) -> Void {
    let urlString = Constants.Urls.Suppliers.List;
    let session = NSURLSession.sharedSession();
    let url = NSURL(string: urlString)!;
    var suppliers: Array<String> = []

    session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
        if let responseData = data {

            do {
                let json = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.AllowFragments) as! Dictionary<String, AnyObject>;

                if let suppliersDictionary = json["suppliers"] as? [Dictionary<String, AnyObject>] {

                    for aSupplier in suppliersDictionary {

                        if let id = aSupplier["id"] as? Int, let name = aSupplier["supplierName"] as? String {
                            let supplier = Supplier(id: id, name: name)
                            suppliers.append(supplier.supplierName)
                        }

                    }
                    completion(suppliers) // pass the array to completion block
                }
            }catch {
                print("Could not serialize");
            }
        }
        }.resume()
}

You'll call it like this:

Supplier.arrayOfSupplierNames { (suppliers) -> Void in
    // use suppliers as appropriate
}

please note that completion block is called asynchronously (some time in future).

Sign up to request clarification or add additional context in comments.

4 Comments

Then what would I pass into my function when I call it from my view controller? It's expecting a parameter: let sup:Array = Supplier.arrayOfSupplierNames()
you would pass it a completion block, I am updating my answer to add it.
I've never used completion blocks but have heard of them. I'm a little more confused but it seems to be working (except I think I need to move the function from my viewDidLoad to somewhere else, not filling picker). So basically I'm passing my empty Suppliers array INTO the function from my view controller, and once the callback is completed in the function I'm filling it and passing it back to my view controller to use?
no, you are passing arrayOfSupplierNames a block of code that can accept an array of strings. Basically, arrayOfSupplierNames says, "Hey pass me a block of code that can accept an array of strings, I'll call this block completion and I'll execute it when I have something." iOS Concurrency Programming Guide will be useful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.