How to add UIActivityIndicatorView by using URLSession in Swift? - ios

I have the Master-Detail project where I am parsing data from JSON. The purpose is to add UIActivityIndicatorView (by using URLSession) to the DetailsViewController while waiting for data be fetched and loaded to the DetailsViewController. I have tried several ways by starting UIActivityIndicatorView in Master after the following:
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
Also I do not know where to stop it, I have tried it in ViewDidLoad() of the DetailViewController (before configureView()):
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
configureView()
}
But also did not work. I could not find anywhere the information about adding activity indicator by using the state of URLSession. Here I add the code from MasterViewController where I have tried to start the activity indicator:
let arrayOfUrls = [ URL(string: "http://www.omdbapi.com/?t=The+Dark+Knight&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Lord+of+the+Rings%3A+The+Return+of+the+King&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=Forrest+Gump&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=Inception&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Matrix&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=Interstellar&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Pianist&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Intouchables&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Departed&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Prestige&apikey=f85dc75e") ]
for url in arrayOfUrls {
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error {
print (error)
} else {
if let data = data {
do {
let movie = try JSONDecoder().decode(Movie.self, from: data)
print(movie.Title)
self.objects.append(movie.Title)
self.details.append(movie)
} catch {
print("Json Processing Failed")
}
}
}
}
task.resume()
}
}

Create NetworkService class and do api calls in a func, this way much better.
class NetworkService {
let arrayOfUrls = [ URL(string: "http://www.omdbapi.com/?t=The+Dark+Knight&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Lord+of+the+Rings%3A+The+Return+of+the+King&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=Forrest+Gump&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=Inception&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Matrix&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=Interstellar&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Pianist&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Intouchables&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Departed&apikey=f85dc75e"), URL(string: "http://www.omdbapi.com/?t=The+Prestige&apikey=f85dc75e") ]
func getData(completion:#escaping(Movie)->()){
for url in arrayOfUrls {
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
var movie = Movie()
if let error = error {
print (error)
} else {
if let data = data {
do {
movie = try JSONDecoder().decode(Movie.self, from: data)
print(movie.Title)
self.objects.append(movie.Title)
self.details.append(movie)
} catch {
print("Json Processing Failed")
}
}
}
completion(movie)
}
task.resume()
}
}
}
In view Controller call your func:
let networkService = NetworkService()
activityIndicator.startAnimating()
networkService.getData { result in
self.activityIndicator.stopAnimating()
//result your movie data do whatever yo want it
DispatchQueue.main.async {
//If you need to reload tableview or etc. do here
}
}

Related

Swift completion handlers - using escaped closure?

Hi i am a beginner studying swift and would like to know what to use when making an api request. What is the modern and professional way?
is it using an escaping closure like so:
func getTrendingMovies(completion: #escaping (Result< [Movie], Error >) -> Void) {
guard let url = URL(string: "\(Constants.baseUrl)/trending/all/day?api_key=\.(Constants.API_KEY)") else {return}
let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _,
error in
guard let data = data, error == nil else {
return
}
do {
let results = try JSONDecoder().decode(TrendingMoviesResponse.self, from:
data)
completion(.success(results.results))
} catch {
completion(.failure(error))
}
}
task.resume()
}
or should i make an api request without escaping closure while using a sort of delegate like so:
func performRequest(with urlString: String){
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
delegate?.didFailWithError(error: error!)
return
}
if let safeData = data{
// created parseJson func
if let weather = parseJSON(safeData){
delegate?.didUpdateWeather(self,weather: weather)
}
}
}
task.resume()
} else {
print("url is nil")
}
}
I agree with matt, the modern and professional way is async/await
func getTrendingMovies() async throws -> [Movie] {
let url = URL(string: "\(Constants.baseUrl)/trending/all/day?api_key=\(Constants.API_KEY)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(TrendingMoviesResponse.self, from: data).results
}

How to display YouTube search API response in UI text view?

I am creating a project where I have to display the YouTube search API response in a UI Text View when a button is clicked. Now I am getting the response in console.
1) How to display it as a JSON response.
2) How to display it in UI text view.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var resultTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func goButton(_ sender: Any) {
guard let url = URL(string: "https://www.youtube.com/watch?v=6Zf79Ns8_oY")else {
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) {(data, response, error) in
if let response = response {
print(response)
}
if let jsondata = data {
print(jsondata)
}
}
task.resume()
}
}
I think you are looking for that:
Parse Response using JSONSerialization and set required value to textview text.
guard let url = URL(string: "https://www.youtube.com/watch?v=6Zf79Ns8_oY")else {
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String:Any]
let text = json["KEY"] as? String
DispatchQueue.main.async{
self.YOURTEXTVIEW.text = text
}
} catch let error as NSError {
print(error)
}
}
task.resume()
maybe it is helpful.

How do I optimize chaining async requests in Swift

I have code that do requests chaining like A->B->C and I am using URLSession all requests are done in right order and with expected behavior. But i am wondering how I can optimize this chaining because it looks quite complex and not reusable. I am looking for the suggestion how I can do this chaining in more flexible way.
My code:
URLSession.shared.dataTask(with: URLRequest(url: URL(string: "first")!)){ data , res , err in
let second = URLRequest(url: URL(string: "second")!)
URLSession.shared.dataTask(with: second){ data , res , err in
let third = URLRequest(url: URL(string: "second")!)
URLSession.shared.dataTask(with:third){ data , res , err in
}.resume()
}.resume()
}.resume()
Actually you can use dependencies using OperationQueues like below:
func operationQueueWithBlockandCancel(){
let mainQueue = OperationQueue.main
let operationBlock1 = BlockOperation()
let operationBlock2 = BlockOperation()
let operationBlock3 = BlockOperation()
operationBlock1.addExecutionBlock {
//Any task
}
operationBlock2.addExecutionBlock {
//Any task
}
operationBlock3.addExecutionBlock {
//Any task
}
//Add dependency as required
operationBlock3.addDependency(operationBlock2)
operationBlock2.addDependency(operationBlock1)
opQueue.addOperations([operationBlock2,operationBlock1,operationBlock3,], waitUntilFinished: false)
}
As #Paulw11 suggested:
PromiseKit + PMKFoundation
import PromiseKit
import PMKFoundation
let session = URLSession.shared
firstly {
session.dataTask(with: URLRequest(url: URL(string: "first")!))
} .then { data in
session.dataTask(with: URLRequest(url: URL(string: "second")!))
} .then { data in
session.dataTask(with: URLRequest(url: URL(string: "third")!))
} .then { data -> () in
// The data here is the data fro the third URL
} .catch { error in
// Any error in any step can be handled here
}
With 1 (and only 1) retry, you can use recover. recover is like catch except it's expected that the previous then can be retried. However, this is not a loop and executes only once.
func retry(url: URL, on error: Error) -> Promise<Data> {
guard error == MyError.retryError else { throw error }
// Retry the task if a retry-able error occurred.
return session.dataTask(with: URLRequest(url: url))
}
let url1 = URL(string: "first")!
let url2 = URL(string: "second")!
let url3 = URL(string: "third")!
let session = URLSession.shared
firstly {
session.dataTask(with: URLRequest(url: url1))
} .then { data in
session.dataTask(with: URLRequest(url: url2))
} .recover { error in
retry(url: url2, on: error)
} .then { data in
session.dataTask(with: URLRequest(url: url3))
} .recover { error in
retry(url: url3, on: error)
} .then { data -> () in
// The data here is the data fro the third URL
} .catch { error in
// Any error in any step can be handled here
}
NOTE: to make this work without specifying return types and needing a return statement, I need the then and recover to be 1 line exactly. So I create methods to do the processing.

function for loading an image from internet in an imageview

I'm trying to make a function to load an image in an imageView at runtime.
Can you help making it work?
Here the complete code: (Swift 3)
import Foundation
import UIKit
extension UIImageView {
public func imageFromUrl(_ urlString: String) {
if let url = URL(string: urlString) {
let request = URLRequest(url: url)
let session = URLSession.shared
session.dataTask(with: request, completionHandler: {data, response, error -> Void in
if let imageData = data as Data? {
self.image = UIImage(data: imageData)
}
})
}
}
}
You may have missed to resume(resume()) the data task.
extension UIImageView {
public func imageFromUrl(_ urlString: String) {
if let url = URL(string: urlString) {
let request = URLRequest(url: url)
let session = URLSession.shared
session.dataTask(with: request, completionHandler: {data, response, error -> Void in
if let imageData = data as Data? {
self.image = UIImage(data: imageData)
}
}).resume()
}
}
}

How to extract a value from function in swift

So I've written a function in swift which gives me a numeric value from JSON api. My question is how can I take the value from function so I can use it in more practical means.
override func viewDidLoad() {
super.viewDidLoad()
getJSON()
}
func getJSON(){
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil{
let swiftyJSON = JSON(data: data!)
let usdPrice = swiftyJSON["bpi"]["USD"]["rate"].doubleValue
print(usdPrice)
}else{
print("There was an error!")
}
let usdPrice gets me the value so how can I take that from the function getJSON() and do something with it, for example attribute it to some label in Main.storyboard
Unfortunately the other answers are incorrect. Just returning a value will not work because you are getting the value from the completion closure of dataTaskWithRequest.
Having the statement return usdPrice should be a compiler error because the completion closure does not have a return value.
You'll need to add your own completion closure to getJSON that gets the double as a parameter.
override func viewDidLoad() {
super.viewDidLoad()
getJSON { (usdPrice) -> Void in
// do something with usdPrice
print(usdPrice)
}
}
func getJSON(completion: (Double) -> Void) {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil{
let swiftyJSON = JSON(data: data!)
let usdPrice = swiftyJSON["bpi"]["USD"]["rate"].doubleValue
completion(usdPrice)
} else {
print("There was an error!")
}
}
Your function needs to return the value as a result of its execution. For more details check out this: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID160
You have to have a return value for the function. The code below should work.
func getJSON() -> Double {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
var usdReturnValue : Double = 0.0
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil{
let swiftyJSON = JSON(data: data!)
let usdPrice = swiftyJSON["bpi"]["USD"]["rate"].doubleValue
print(usdPrice)
usdReturnValue = usdPrice
}else{
print("There was an error!")
}
}
return usdReturnValue
}

Resources