Extra argument 'error' in call in swift - swift

I am new to swift so please treat me as beginner.
I am following tutorial, this is pretty old tutorial and it has used GoogleMap framework whereas I am doing it with pod. In func geocodeAddress in MapTasks.swift file I am getting error called
Extra argument 'error' in call
func geocodeAddress(address: String!, withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
geocodeURLString = geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let geocodeURL = NSURL(string: geocodeURLString)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let geocodingResultsData = NSData(contentsOfURL: geocodeURL!)
let request = NSMutableURLRequest(URL: geocodingResultsData)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
(let data, let response, let error) in
if let _ = response as? NSHTTPURLResponse {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if error != nil {
print("error=\(error!)")
return
}
if let parseJSON = json {
}
} catch {
print(error)
}
}
}
task.resume()
else {
// Get the response status.
let status = dictionary["status"] as! String
if status == "OK" {
let allResults = dictionary["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as! String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status: status, success: true)
}
else {
completionHandler(status: status, success: false)
}
}
})
}
else {
completionHandler(status: "No valid address.", success: false)
}
}
So far I know is I am getting this error because of the diffrent version of swift. Tutorial I am following is written in old version of swift and I am doing it in new

In Swift 2.0, you cannot add 'error' argument in NSJSONSerialization method, you need to use try-catch statement as follows:
func geocodeAddress(address: String!, withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
geocodeURLString = geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let geocodeURL = NSURL(string: geocodeURLString)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let geocodingResultsData = NSData(contentsOfURL: geocodeURL!)
let request = NSMutableURLRequest(URL: geocodeURL!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
(let data, let response, let error) in
if let _ = response as? NSHTTPURLResponse {
do {
let dictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if error != nil {
print("error=\(error!)")
return
}
if let parseJSON = dictionary {
let status = dictionary["status"] as! String
if status == "OK" {
let allResults = dictionary["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as! String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status: status, success: true)
}
else {
completionHandler(status: status, success: false)
}
}
} catch {
print(error)
}
}
}
task.resume()
})
}
}

Related

Cannot convert value of type '(key: String, value: Any)' to expected argument type '[String : Any]

I'm trying to get JSON but it shows an occurred issue
Line starting with if let deliveryObject
How to get rid of the problem?
code:
struct Tracking {
let receiver: String
let placeOfMail: String
let indexOfMail: Double
init(json:[String:Any]) throws {
guard let receiver = json["name"] as? String else {
throw SerializationError.missing("Receiver data has been missed")
}
guard let placeOfMail = json ["address"] as? String else {
throw SerializationError.missing("Place of delivery has been missed")
}
guard let indexOfMail = json ["postindex"] as? Double else {
throw SerializationError.missing("Index of postmail has been missed")
}
self.receiver = receiver
self.placeOfMail = placeOfMail
self.indexOfMail = indexOfMail
}
static let basePath = "https://track.kazpost.kz/api/v2/"
static func deliveryData (withTrackid trackid:String, completion: #escaping ([Tracking]) -> ()){
let url = basePath + trackid
let request = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response : URLResponse?, error: Error?) in
var deliveryArray: [Tracking] = []
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options:[]) as? [String:Any] {
if let deliveryInformation = json ["delivery"] as? [String:Any] {
if let deliveryPlace = deliveryInformation ["address"] as? [String:Any] {
for dataPoint in deliveryPlace {
if let dataPointValue = dataPoint.value as? [String: AnyObject],
let deliveryObject = try Tracking(json: dataPointValue) {
deliveryArray.append(deliveryObject)
}
}
}
}
}
}catch {
print(error.localizedDescription)
}
completion(deliveryArray)
}
}
task.resume()
}
}

Parsing get_video_info (YouTube information data) in swift

To get a direct link to YouTube video I made request to get the get_video_info file, in this file that link, but I have to parse it, I find the solution to parse it by PHP but I want to parse it directly from my App
I get the data from my code like this:
let youtubeContentID = "sZz7tiToK1U"
if let infoURL = URL(string:"https://www.youtube.com/get_video_info?video_id=\(youtubeContentID)") {
let request = URLRequest(url: infoURL)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
if let error = error {
print(error)
} else if let data = data, let result = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
print(result)
}
})
task.resume()
}
}
I got data with many Ascii symbols like this:
c=WEB&vss_host=s.youtube.com&innertube_api_version=v1&xhr_apiary_host=youtubei.youtube.com&apiary_host_firstparty=&status=ok&t=1&enabled_engage_types=3%2C6%2C4%2C5%2C17%2C1&ssl=1&adaptive_fmts=type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26eotf%3Dbt709%26projection_type%3D1%26lmt%3D1550114115045036%26bitrate%3D16962786%26size%3D3840x2160%26index%3D221-1771%26quality_label%3D2160p%26xtags%3D%26url%3Dhttps%253A%252F%252Fr2---sn-nuj-wxqek.googlevideo.com%252Fvideoplayback%253Fexpire%253D1551500605%2526usequic%253Dno%2526gir%253Dyes%2526mime%253Dvideo%25252Fwebm%2526requiressl%253Dyes%2526keepalive%253Dyes%2526fvip%253D2%2526clen%253D547103152%2526source%253Dyoutube%2526aitags%253D133%25252C134%25252C135%25252C136%25252C137%25252C160%25252C242%25252C243%25252C244%25252C247%25252C248%25252C271%25252C278%25252C313%25252C394%25252C395%25252C396%25252C397%2526signature%253D9C28A5C103FA95701CD3655795DDB7F2C0954828.55534AC9C7BEE5D644C3A34C2CD4A4EEC9E2FD38%2526lmt%253D1550114115045036%2526ip%253D129.208.30.232%2526key%253Dyt6%2526c%253DWEB%2526ei%253D3bB5XKeFFNWd1wbBpqjgAg%2526txp%253D5531432%2526id%253Do-AIOVogCB8KFe32o_VgxSx-LqaEjNBZxiZ1jl81VTXZhF%2526sparams%253Daitags%25252Cclen%25252Cdur%25252Cei%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Ckeepalive%25252Clmt%25252Cmime%25252Cmm%25252Cmn%25252Cms%25252Cmv%25252Cpl%25252Crequiressl%25252Csource%25252Cusequic%25252Cexpire%2526initcwndbps%253D296250%2526itag%253D313%2526ms%253Dau%25252Crdu%2526mt%253D1551478917%2526mv%253Dm%2526dur%253D428.933%2526pl%253D19%2526ipbits%253D0%2526mm%253D31%25252C29%2526mn%253Dsn-nuj-wxqek%25252Csn-hgn7yn7l%26clen%3D547103152%26init%3D0-220%26itag%3D313%26primaries%3Dbt709%26fps%3D30%2Ctype%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26eotf%3Dbt709%26projection_type%3D1%26lmt%3D1550113771564979%26bitrate%3D6318874%26size%3D2560x1440%26index%3D220-1763%26quality_label%3D1440p%26xtags%3D%26url%3Dhttps%253A%252F%252Fr2---sn-nuj-wxqek.googlevideo.com%252Fvideoplayback%253Fexpire%253D1551500605%2526usequic%253Dno%2526gir%253Dyes%2526mime%253Dvideo%25252Fwebm%2526requiressl%253Dyes%2526keepalive%253Dyes%2526fvip%253D2%2526clen%253D155020170%2526source%253Dyoutube%2526aitags%253D133%25252C134%25252C135%25252C136%25252C137%25252C160%25252C242%25252C243%25252C244%25252C247%25252C248%25252C271%25252C278%25252C313%25252C394%25252C395%25252C396%25252C397%2526signature%253D4D7BF761EE4A6DFD048DE3D48550FCE80E61B7D0.D625BCC9645471ABA478F79C197B2208F354E15F%2526lmt%253D1550113771564979%2526ip%253D129.208.30.232%2526key%253Dyt6%2526c%253DWEB%2526ei%253D3bB5XKeFFNWd1wbBpqjgAg%2526txp%253D5531432%2526id%253Do-AIOVogCB8KFe32o_VgxSx- .... ext
I find code can deal with this YouTube request, it created on 2015 so it can't run on the new Swift, so I edited it to work on Swift 4, just copy this code in side a new Swift file then call function h264videosWithYoutubeID(youtubeID: your TouTube ID) and you will get the correct url:
import UIKit
public extension NSURL {
func dictionaryForQueryString() -> [String: AnyObject]? {
if let query = self.query {
return query.dictionaryFromQueryStringComponents()
}
let result = absoluteString?.components(separatedBy: "?")
if result!.count > 1 {
return result!.last?.dictionaryFromQueryStringComponents()
}
return nil
}
}
public extension NSString {
func stringByDecodingURLFormat() -> String {
let result = self.replacingOccurrences(of: "+", with: " ")
return result.removingPercentEncoding!
}
func dictionaryFromQueryStringComponents() -> [String: AnyObject] {
var parameters = [String: AnyObject]()
for keyValue in components(separatedBy: "&") {
let keyValueArray = keyValue.components(separatedBy: "=")
if keyValueArray.count < 2 {
continue
}
let key = keyValueArray[0].stringByDecodingURLFormat()
let value = keyValueArray[1].stringByDecodingURLFormat()
parameters[key] = value as AnyObject
}
return parameters
}
}
public class YoutubeUrlReciver: NSObject {
static let infoURL = "http://www.youtube.com/get_video_info?video_id="
static var userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4"
public static func youtubeIDFromYoutubeURL(youtubeURL: NSURL) -> String? {
if let
youtubeHost = youtubeURL.host,
let youtubePathComponents = youtubeURL.pathComponents {
let youtubeAbsoluteString = youtubeURL.absoluteString
if youtubeHost == "youtu.be" as String? {
return youtubePathComponents[1]
} else if youtubeAbsoluteString?.range(of: "www.youtube.com/embed") != nil {
return youtubePathComponents[2]
} else if youtubeHost == "youtube.googleapis.com" ||
youtubeURL.pathComponents!.first == "www.youtube.com" as String? {
return youtubePathComponents[2]
} else if let
queryString = youtubeURL.dictionaryForQueryString(),
let searchParam = queryString["v"] as? String {
return searchParam
}
}
return nil
}
public static func h264videosWithYoutubeID(youtubeID: String) -> [String: AnyObject]? {
let urlString = String(format: "%#%#", infoURL, youtubeID) as String
let url = NSURL(string: urlString)!
let request = NSMutableURLRequest(url: url as URL)
request.timeoutInterval = 5.0
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
request.httpMethod = "GET"
var responseString = NSString()
let session = URLSession(configuration: URLSessionConfiguration.default)
let group = DispatchGroup()
group.enter()
session.dataTask(with: request as URLRequest, completionHandler: { (data, response, _) -> Void in
if let data = data as NSData? {
responseString = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue)!
}
group.leave()
}).resume()
group.wait()
let parts = responseString.dictionaryFromQueryStringComponents()
if parts.count > 0 {
var videoTitle: String = ""
var streamImage: String = ""
if let title = parts["title"] as? String {
videoTitle = title
}
if let image = parts["iurl"] as? String {
streamImage = image
}
if let fmtStreamMap = parts["url_encoded_fmt_stream_map"] as? String {
// Live Stream
if let _: AnyObject = parts["live_playback"]{
if let hlsvp = parts["hlsvp"] as? String {
return [
"url": "\(hlsvp)" as AnyObject,
"title": "\(videoTitle)" as AnyObject,
"image": "\(streamImage)" as AnyObject,
"isStream": true as AnyObject
]
}
} else {
let fmtStreamMapArray = fmtStreamMap.components(separatedBy: ",")
for videoEncodedString in fmtStreamMapArray {
var videoComponents = videoEncodedString.dictionaryFromQueryStringComponents()
videoComponents["title"] = videoTitle as AnyObject
videoComponents["isStream"] = false as AnyObject
return videoComponents as [String: AnyObject]
}
}
}
}
return nil
}
public static func h264videosWithYoutubeURL(youtubeURL: NSURL,completion: ((
_ videoInfo: [String: AnyObject]?, _ error: NSError?) -> Void)?) {
DispatchQueue.global().async {
if let youtubeID = self.youtubeIDFromYoutubeURL(youtubeURL: youtubeURL), let videoInformation = self.h264videosWithYoutubeID(youtubeID: youtubeID) {
DispatchQueue.main.async {
completion?(videoInformation, nil)
}
}else{
DispatchQueue.main.async {
completion?(nil, NSError(domain: "com.player.youtube.backgroundqueue", code: 1001, userInfo: ["error": "Invalid YouTube URL"]))
}
}
}
}
}

How to work with async functions swift? Completion handlers [duplicate]

This question already has answers here:
Run code only after asynchronous function finishes executing
(2 answers)
Closed 5 years ago.
Im trying to wait for the function to process in order to show my image. I have try many things but none of this worked. I know this is an async function and basically i have to wait in order to get the right values but I dont know how to fix this function right here. I hope you can help me out. Thank you!
func createListProductsGood(Finished() -> void) {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
self.productPhoto.image = UIImage(data: data as Data)
}}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
self.productPhoto.image = UIImage(data: data as Data)
}}
}
}
})
finished()
}
Edited:
This is how my viewDidLoad looks like:
override func viewDidLoad() {
super.viewDidLoad()
setAcceptedOrRejected()
createListProductsGood{_ in
}
}
func createListProductsGood(finished: #escaping (_ imageData: Data) -> Void) {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
DispatchQueue.main.async {
self.productPhoto.image = UIImage(data: data as Data)
}
}}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
DispatchQueue.main.async {
self.productPhoto.image = UIImage(data: data as Data)
}
}}
}
}
})
}
This is my second method:
func setAcceptedOrRejected() {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
if self.ProductId == snapshot.key{
self.texto = prod["NotInterested"] as! String
self.refProducts.child("Products").child(self.ProductId).updateChildValues(["NotInterested": self.texto + ", " + self.userUID])
} })
}
You should change:
func createListProductsGood(Finished() -> void) {
to:
func createListProductsGood(finished: #escaping (_ something: SomeType) -> Void) {
or to be more specific:
func createListProductsGood(finished: #escaping (_ imageData: Data) -> Void) {
then wherever in your function you get the image, you call
finished(imageData)
so you can pass the imageData through a closure to where its needed.
then you call this function like this:
createListProductsGood{ imageData in
...
let image = UIImage(data: imageData)
// update UI from main Thread:
DispatchQueue.main.async {
self.productPhoto.image = image
}
}
Also:
it's not convention to use Finished(), you should use finished()
using void is wrong. You must use Void or ()
If you're having problems with closures and completionHandlers, I recommend you first try getting your hands dirty with a simple UIAlertController. See here. Try creating an action with a closure, e.g. see here
EDIT :
Thanks to Leo's comments:
func createListProductsGood(finished: #escaping(_ imageData: Data?, MyError?) -> Void) {
let value: Data?
let error = MyError.someError("The error message")
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active // REMOVE
self.productPhoto.image = UIImage(data: data as Data) // REMOVE
finished(data, nil) //ADD
}else{
finished(nil,error) //ADD
}
}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active // REMOVE
self.productPhoto.image = UIImage(data: data as Data) // REMOVE
finished(data,nil) //ADD
}else{
finished(nil,error) //ADD
}
}
}
}
})
}
And then you call it like:
createListProductsGood { imageData, error in guard let value = imageData, error == nil else { // present an alert and pass the error message return }
...
let image = UIImage(data: imageData)
// update UI from main Thread:
DispatchQueue.main.async {
self.ProductId = active
self.productPhoto.image = image } }
Basically this way the createListProductsGood takes in 2 closures, one for if the image is present, another for if an error was returned.

Callback syntax in swift 3

I am trying to create a callback on swift 3 but haven't had any luck so far. I was taking a look at this question: link which is similar, but the answer gives me an error.
Basically I have an API struct with a static function that I need to have a callback.
import UIKit
struct API {
public static func functionWithCallback(params: Dictionary<String, String>, success: #escaping ((_ response: String) -> Ticket), failure: #escaping((_ error:String) -> String) ) {
let app_server_url = "http://api.com" + params["key"]!
let url: URL = URL(string: app_server_url)!
var request: URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json charset=utf-8", forHTTPHeaderField: "Accept")
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
DispatchQueue.main.async {
do {
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
print(json)
var message = ""
if let result = json["result"] as? String {
if(result == "success") {
//attempt to call callback gives me an error: extra argument in call
success("") {
let ticket = json["ticket"] as! NSDictionary
var date = ticket["date"] as! String
var ticket: Ticket = nil
ticket.setDate(date: date)
return ticket
}
}
else {
message = json["message"] as! String
print(message)
}
} catch let error {
print(error.localizedDescription)
let description = error.localizedDescription
if let data = description.data(using: .utf8) {
do {
let jsonError = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
let message = jsonError?["message"] as! String
} catch {
}
}
}
}
})
task.resume()
}
}
So I basically can't call the callback success because it gives me an error: Extra argument in call. Any idea on how to fix it?
My goal is to call:
API.functionWithCallback(params: params, success() -> Ticket {
//do something with the returned ticket here
},
error() -> () {
//do something with the error message here
}
)
I believe you have it wrong on how to use call back closures, from what I can understand of your question you want to do something with the ticket in the call back closure and to do that it should be a parameter of the closure not the return type of the closure.
Replace your function declaration with this:
public static func functionWithCallback(params: Dictionary<String, String>, success: #escaping ((_ response: String, _ ticket: Ticket) -> Void), failure: #escaping((_ error:String) -> Void) ) {
And inside the function replace this:
success("") {
let ticket = json["ticket"] as! NSDictionary
var date = ticket["date"] as! String
var ticket: Ticket = nil // Im not sure what you are trying to do with this line but this will definitely give an error
ticket.setDate(date: date)
return ticket
}
With:
let ticket = json["ticket"] as! NSDictionary
var date = ticket["date"] as! String
var ticket: Ticket = nil // fix this line
ticket.setDate(date: date)
success("",ticket)
And then you can call the function like this:
API.functionWithCallback(params: params, success: { response, ticket in
// you can use ticket here
// and also the response text
}) { errorMessage in
// use the error message here
}
Try this :
func uploadImage(api: String,token : String, methodType : String, requestDictionary: [String:AnyObject],picData:[Data], successHandler: #escaping (AnyObject) -> Void,failureHandler: #escaping (NSError) -> Void)
{
if Common_Methods.Reachability1.isConnectedToNetwork() == false
{
let del :AppDelegate = (UIApplication.shared.delegate as? AppDelegate)!
let nav : UINavigationController = (del.window?.rootViewController as? UINavigationController)!
let alert = UIAlertController(title: "", message: "The Internet connection appears to be offline" , preferredStyle: UIAlertControllerStyle.alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default)
{
UIAlertAction in
}
alert.addAction(okAction)
nav.present( alert, animated: true, completion: nil)
return
}
let apiUrl = "\(KbaseUrl)\(api)"
let session = URLSession.shared
let url: NSURL = NSURL(string: apiUrl as String)!
print(url)
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = methodType
let boundary = NSString(format: "---------------------------14737809831466499882746641449") as String
//-------- add token as perameter and set a check if token not nill then set token in header -------
if(token.characters.count > 0)
{
request.setValue(token, forHTTPHeaderField: "x-logintoken")
}
request.setValue("Keep-Alive", forHTTPHeaderField: "Connection")
request.setValue("multipart/form-data; boundary="+boundary, forHTTPHeaderField: "Content-Type")
let data = createBodyWithParameters(parameters: requestDictionary, filePathKey:nil, imageDataKey: picData.count > 0 ? picData : [], boundary: boundary)
print(data)
request.httpBody = data
let task = session.dataTask(with: request as URLRequest) { data, response, error in
// handle fundamental network errors (e.g. no connectivity)
guard error == nil && data != nil else {
successHandler(data as AnyObject )//completion(data as AnyObject?, error as NSError?)
print(error)
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
return
}
// check that http status code was 200
if let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode != 200 {
do {
let responseObject = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
if let responseDictionary = responseObject as? [String:AnyObject]
{
if responseDictionary["statusCode"] as! Int == 401
{
// self.objDelegate.sessionExpire(msgStr: "Session Expired. Please login again to continue.")
}
else
{
//completion(String(data: data!, encoding: String.Encoding.utf8) as AnyObject?, nil)
}
}
} catch let error as NSError {
print(error)
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
// completion(String(data: data!, encoding: String.Encoding.utf8) as AnyObject?, nil)
}
}
// parse the JSON response
do {
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
let responseObject = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
successHandler(responseObject! )
} catch let error as NSError {
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
// completion(String(data: data!, encoding: String.Encoding.utf8) as AnyObject?, error)
failureHandler(error)
}
}
task.resume()
// return task
}
and function Call is :
WebService.sharedInstance.uploadImage(api: KEditEmployerProfile,token: token,methodType: "PUT", requestDictionary: parameters1 as! [String : AnyObject], picData: [imageData as Data], successHandler: { (responseObject) in
print(responseObject)
}) { (error) in
print(error)
}
}

Nested dataTaskWithRequest in Swift tvOS

I'm a C# developer convert to Swift tvOs and just starting to learn. I've made some progress, but not sure how to handle nested calls to json. The sources are from different providers so I can't just combine the query.
How do I wait for the inner request to complete so the TVSeries has the poster_path? Is there a better way to add the show to the collection and then process the poster path loading in another thread so it doesn't delay the UI Experience?
func downloadTVData() {
let url_BTV = NSURL(string: BTV_URL_BASE)!
let request_BTV = NSURLRequest(URL: url_BTV)
let session_BTV = NSURLSession.sharedSession()
//get series data
let task_BTR = session_BTV.dataTaskWithRequest(request_BTV) { (data_BTV, response_BTV, error_BTV) -> Void in
if error_BTV != nil {
print (error_BTV?.description)
} else {
do {
let dict_BTV = try NSJSONSerialization.JSONObjectWithData(data_BTV!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results_BTV = dict_BTV!["results"] as? [Dictionary<String, AnyObject>]{
for obj_BTV in results_BTV {
let tvshow = TVSeries(tvDict: obj_BTV)
//for each tv series try to load a poster_path from secondary provider
if let str = obj_BTV["title"] as? String!{
let escapedString = str?.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())!
if let url = NSURL(string: self.SEARCH_URL_BASE + escapedString!) {
let request = NSURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print (error?.description)
} else {
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results = dict!["results"] as? [Dictionary<String, AnyObject>] {
//iterate through the poster array
for obj in results {
if let path = obj["poster_path"] as? String {
tvshow.posterPath = path
break
}
}
}
} catch let error as NSError {
print(error.description)
}
}
}
task.resume()
}
}
self.tvSeries.append(tvshow)
}
dispatch_async(dispatch_get_main_queue()){
self.collectionView.reloadData()
}
}
} catch let error as NSError {
print(error.description)
}
}
}
task_BTR.resume()
}
Thanks for your help!
I would recommend breaking things apart into multiple methods, with callbacks to sequence the operations, and utilizing Swift's built-in throws error handling mechanism. Here's an example, not perfect, but might help as a starting point:
class TVSeries
{
let title: String
var posterPath: String?
enum Error: ErrorType {
case MalformedJSON
}
init(tvDict: [String: AnyObject]) throws
{
guard let title = tvDict["title"] as? String else {
throw Error.MalformedJSON
}
self.title = title
}
static func loadAllSeries(completionHandler: [TVSeries]? -> Void)
{
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: BTV_URL_BASE)!) { data, response, error in
guard let data = data else {
print(error)
completionHandler(nil)
return
}
do {
completionHandler(try fromJSONData(data))
}
catch let error {
print(error)
}
}.resume()
}
static func fromJSONData(jsonData: NSData) throws -> [TVSeries]
{
guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
throw Error.MalformedJSON
}
guard let results = dict["results"] as? [[String: AnyObject]] else {
throw Error.MalformedJSON
}
return try results.map {
return try TVSeries(tvDict: $0)
}
}
func loadPosterPath(completionHandler: () -> Void)
{
guard let searchPath = title.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet()) else {
completionHandler()
return
}
let url = NSURL(string: SEARCH_URL_BASE)!.URLByAppendingPathComponent(searchPath)
NSURLSession.sharedSession().dataTaskWithURL(url) { [weak self] data, response, error in
defer { completionHandler() }
guard let strongSelf = self else { return }
guard let data = data else {
print(error)
return
}
do {
strongSelf.posterPath = try TVSeries.posterPathFromJSONData(data)
}
catch let error {
print(error)
}
}.resume()
}
static func posterPathFromJSONData(jsonData: NSData) throws -> String?
{
guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
throw Error.MalformedJSON
}
guard let results = dict["results"] as? [[String: AnyObject]] else {
throw Error.MalformedJSON
}
for result in results {
if let path = result["poster_path"] as? String {
return path
}
}
return nil
}
}
It might also be worth your time to look into something like RxSwift or Alamofire, which help you with these kinds of data-conversion / sequencing operations.