I am trying to load custom data type from my API and display it on my landing page of an iOS app.
As i understand, I should call something like:
override func viewDidLoad() {
performSelector(inBackground: #selector(fetchJSON), with: nil)
let myCustomDataType = //how so I get this back
tableView.reloadData()
//...
}
Obviously the declaration of fetchJSON should be in another file so I don’t fill my Controller up with things it doesn’t need to do. However I need this function to return a list of [MyCustomDataType] and display those on my landing page.
func fetchJSON() -> [MyCustomDataType] {
//get api, fetch data, put it in my custom data type list
return myCustomDataType
}
Do I use closures? Create a global [MyCystomDataType]. Or how do I achieve this?
Be mindful of the task being async, since I display table cells kinda like facebook, or instagram news feed page.
3
Answers
performSelector(inBackground:)
is not GCD. It’s a pre-GCD method from OS X 10.5 that you should almost never use. (I don’t think I’ve had a single reason to use it since 10.6 introduced GCD.)As a rule, you don’t need to use GCD directly at all for network requests. URLSessionTask is already asynchronous. See Fetching Website data into memory for more. As shown there, you often will need to use GCD (
DispatchQueue.main.async
) to return data to the UI, but you don’t need it to launch the request.But to your underlying question, the answer is that by the time
viewDidLoad
completes, you won’t have the data yet. You need to be able to deal with not having the data, and draw your UI appropriately. When the data shows up, then you can update your UI.If
fetchJSON
loads the data asynchronously you have to add a completion handlerThen call
fetchJSON
on a background thread and reload the table view on the main threadPopular asynchronous APIs like
URLSession
andAlamofire
perform their network requests on a background thread implicitly. If you are going to use one of them you can omit the firstasync
block.performSelector
is an outdated objective-c-ish API. Don’t use it in SwiftAs Vadian said (+1), you want to use asynchronous pattern, and using closures is a good pattern. But the question is not just how to hand back data, but in the case of an error, how to report the
Error
, too. We often useResult<Success, Failure>
pattern to accomplish this. E.g. rather than a[MyCustomDataType]
parameter, it might have aResult<[MyCustomDataType], Error>
type.E.g., let’s imagine that you are performing your request via
URLSession
. Then you might have a routine like:And to perform a particular request:
Now, do not get too lost in the details, above, as your implementation might vary (e.g. you could just as easily use Alamofire or whatever). The key point is that we have a completion handler closure that includes a
Swift.Result
parameter by which data or error information will be supplied to the caller.And now, the caller can act accordingly based upon whether the result was a
success
or afailure
:But the caller doesn’t use
performSelector
. Nor does it need to dispatch this network request to a background queue, because network requests are already inherently asynchronous. Just callfetchJSON
and specify what UI update must happen in the completion handler.