-1

I would like to return an array (arr) from method (with async call). I implement the completionHandler in the method, but I can't use my method to get my array : Cast from '(@escaping ((Array<Any>) -> Void)) -> ()' to unrelated type '[[String : Any]]' always fails

How can I fix this ?

Here is my code :

func dataWithURL(completion: @escaping ((_ result:Array<Any>) -> Void)) {
    let urlString = "https://api.unsplash.com/photos/?client_id=71ad401443f49f22556bb6a31c09d62429323491356d2e829b23f8958fd108c4"
    let url = URL(string: urlString)!
    let urlRequest = URLRequest(url: url)
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    var arr = [[String:String]]()
    let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
        // do stuff with response, data & error here
        if let statusesArray = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [[String: Any]] {
            for item in statusesArray! {
                let photos = item["urls"] as? [String: Any]
                let photo = photos?["small"] as? String
                let myDictionary = [
                    "name": "test",
                    "imageURL": photo]
                arr.append(myDictionary as! [String : String])
            }
            print(arr)
            completion(arr)
        }
    })

    task.resume()
}

And when I want to get my array :

lazy var photos: [Photo] = {

    var photos = [Photo]()

// HERE THE ERROR APPEARS
guard let data = self.dataWithURL as? [[String: Any]] else { return photos }
    for info in data {
        let photo = Photo(info: info)
        photos.append(photo)
    }
    return photos
}()
jeanj
  • 2,106
  • 13
  • 22
Vjardel
  • 1,065
  • 1
  • 13
  • 28
  • 1
    I think you should search how to call function, how to use closure, and how to use closure as completion block first. – haider_kazal Nov 24 '16 at 09:53

2 Answers2

4

dataWithURL takes in a callback (completion handler), therefore you can only access the results in the callback.

self.dataWithURL { result in
//do stuff with the result
} 

However the problem with the code above is that you are expecting dataWithURL to return the results which it doesn't. It returns void.

Another problem is that you are trying to use the results of dataWithURL for a property. The call to access the lazy var photos would yield no result (at least on first invocation) because the call dataWithURL is async (returns immediately).

Hong Wei
  • 1,397
  • 1
  • 11
  • 16
1

You seem to be also xcode_Dev having asked this question yesterday.

I wrote a comment to that question:

You cannot return something from a function (or computed variable) which contains an asynchronous task

This is still true.

dataWithURL is an asynchronous function, it does not return anything but you have to pass a closure which is called on return.

First of all, the array is clearly [[String:String]] (array of dictionaries with string keys and string values) so it's pretty silly to use the much more unspecified type [Any]

func dataWithURL(completion: @escaping ([[String:String]]) -> Void) {

In Swift 3 specify only the type in the declaration without underscores and parameter labels.


You have to call the function this way:

dataWithURL { result in
    for item in result { // the compiler knows the type
        print(item["name"], item["imageURL"])
    }
}

Once again: There is no return value of dataWithURL. The closure is called later.

Community
  • 1
  • 1
vadian
  • 274,689
  • 30
  • 353
  • 361