ambiguous reference to member datatask swift Xcode 8 - swift

I just updated my Xcode and have been trying to learn more about it. This is my code for Collection View Cell. I'm trying to get data from URL but I've been annoyed with this error. I tried all the solutions in here but they have different function structure. Also other solutions didn't work.
import UIKit
class PersonCell: UICollectionViewCell {
#IBOutlet weak var personImage : UIImageView!
func ConfigureCell (imgURL : String)
{
}
func DownloadImage ( url : NSURL)
{
}
func GetDatafromURL (URL : NSURL , completion : #escaping (( _ data : NSData? , _ response : URLResponse? , _ error : NSError?) -> Void))
{
URLSession.shared.dataTask(with: URL) { (data , response , error) in
completion (data , response, error)
}.resume()
}
}
the code that worked in the tutorial video is something like this
func GetDatafromURL (URL : NSURL , completion : (( data : NSData? , response : NSURLResponse? , error : NSError?) -> Void))
{
NSURLSession.sharedSession.dataTaskWithURL( URL) { (data , response , error) in
completion(data : data , responce : response, error : error)
}.resume()

You can convert your NSURL to a URL using .absoluteURL
guard let url = URL.absoluteURL else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
//
}.resume()
UPDATE
Your completion block has the wrong types. You're using NSData instead of Data and NSError instead of Error. Here's an example of what it should look like below.
func getData(from url: URL, completion: #escaping (_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Void)
{
URLSession.shared.dataTask(with: url) { data, response, error
completion(data, response, error)
}.resume()
}

Please check the latest reference.
func dataTask(with: URL, completionHandler: (Data?, URLResponse?, Error?) -> Void)
Declaration
func dataTask(with url: URL,
completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
In Swift 3, the completionHandler of dataTask(with:completionHandler:) takes 3 arguments with types Data?, URLResponse? and Error?. So, your method's completion, which is of type (NSData?,URLResponse?,NSError?) -> Void cannot be applied.
And it takes URL as its first parameter.
(Thanks to vadian, he suggested all needed things here.)
So, your GetDatafromURL needs to be something like this:
func GetDatafromURL(url: URL, completion: #escaping ((_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Void))
{
URLSession.shared.dataTask(with: url) { (data, response, error) in
completion (data , response, error)
}.resume()
}

Related

Mocking URLSession to return a Mocked URLSessionDataTask

I want to mock URLSession, and return a mocked URLSessionDataTask.
To Mock URLSession I create a protocol
protocol URLSessionProtocol {
func dataTask(with url: URL, completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
}
that URLSession can then conform to in an extension
extension URLSession: URLSessionProtocol {}
Now I want to do the same for URLSessionDataTask, and so implement a similar protocol and extension for it. I need to do this, since the way I call URLSession requires use of func dataTask(with url: URL, completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
protocol URLSessionDataTaskProtocol {
func resume()
}
extension URLSessionDataTask: URLSessionDataTaskProtocol {}
So then my URLSessionDataTask mock is set up as follows:
class URLSessionMock: URLSessionProtocol {
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
// data and error can be set to provide data or an error
var data: Data?
var error: Error?
func dataTask(
with url: URL,
completionHandler: #escaping CompletionHandler
) -> URLSessionDataTask {
let data = self.data
let error = self.error
return URLSessionDataTaskMock {
completionHandler(data, nil, error)
}
}
}
With my URLSessionDataTaskMock presented with:
class URLSessionDataTaskMock: URLSessionDataTaskProtocol {
private let closure: () -> Void
init(closure: #escaping () -> Void) {
self.closure = closure
}
// override resume and call the closure
func resume() {
closure()
}
}
Doesn't work since URLSessionDataTaskMock within the URLSessionProtocol isn't the correct return type - I need to return a URLSessionDataTask.
I can't cast my URLSessionDataTaskMock to URLSessionDataTask as the types are not related.
How can I return my URLSessionDataTaskMock from my URLSessionProtocol?
You can probably get away with something like this. The key would be your associated type in your URLSessionProtocol
protocol URLSessionProtocol {
associatedtype DataTaskType
func dataTask(with url: URL, completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void) -> DataTaskType
}
extension URLSession: URLSessionProtocol {}
protocol URLSessionDataTaskProtocol {
func resume()
}
extension URLSessionDataTask: URLSessionDataTaskProtocol {}
class URLSessionDataTaskMock: URLSessionDataTaskProtocol {
typealias CompletionHandler = URLSessionMock.CompletionHandler
private let completion: CompletionHandler
init(completion: #escaping CompletionHandler) {
self.completion = completion
}
func resume() {
// create some data
completion(nil, nil, nil)
}
}
class URLSessionMock: URLSessionProtocol {
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
// data and error can be set to provide data or an error
var data: Data?
var error: Error?
func dataTask(
with url: URL,
completionHandler: #escaping CompletionHandler
) -> URLSessionDataTaskMock {
return URLSessionDataTaskMock(completion: completionHandler)
}
}

Type of expression is ambiguous without more context in dataTask

I am converting my code to swift 3 but getting "Type of expression is ambiguous without more context" error in the following code .
open class func performGetRequest(_ targetURL: URL!, completion: #escaping (_ data: Data?, _ HTTPStatusCode: Int, _ error: NSError?) -> Void) {
var request = URLRequest(url: targetURL)
request.httpMethod = "GET"
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
let task = session.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async(execute: {
completion(data, response.statusCode,error)
})
}
task.resume()
}
The compiler wants Error not NSError.
Further in Swift 3 omit the parameter labels in the completion handler and don't pass IUO* parameters if you actually mean non-optional:
open class func performGetRequest(_ targetURL: URL, completion: #escaping (Data?, Int, Error?) -> Void) {
Finally you don't need an URLRequest in this case, GET is the default, so just pass the URL to dataTask and delete the two lines related to the request.
(*) Implicit Unwrapped Optional
There is two problem in your code first response.statusCode, response is type of URLResponse and it doesn't have property statusCode so you need to type cast it to HTTPURLResponse to get the statusCode. Second is from Swift 3 use native Error instead of NSError, So your whole code would be like this.
open class func performGetRequest(_ targetURL: URL!, completion: #escaping (_ data: Data?, _ HTTPStatusCode: Int, _ error: Error?) -> Void) {
var request = URLRequest(url: targetURL)
request.httpMethod = "GET"
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
let task = session.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async(execute: {
completion(data, (response as! HTTPURLResponse).statusCode,error)
})
}
task.resume()
}

Many problems with Swift3: cannot understand new syntax for completion handlers

Yesterday I updated to new Mac OS X Sierra and XCode 8 which forced me to update to Swift 3.0 syntax. In my app I have many functions like the following:
fileprivate func requestFisheFieldWithHandler(_ url:String, completionHandler: #escaping (_ success: NSDictionary?, _ error: NSError?) -> Void) {
let configuration = URLSessionConfiguration.default
let url: URL = URL(string: url)!
let urlRequest: URLRequest = URLRequest(url: url)
let session = URLSession(configuration: configuration)
let task = session.dataTask(with: urlRequest, completionHandler: { (data: Foundation.Data?, response: URLResponse?, error: NSError?) -> Void in
if (error != nil) {
//print(error?.code)
//print(error)
completionHandler(success: nil, error: error)
}
else {
do {
let responseJSON = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions()) as! [String: String]
completionHandler(success: responseJSON, error:nil)
}
catch let error as NSError {
completionHandler(success: nil, error:error)
}
}
} as! (Data?, URLResponse?, Error?) -> Void)
task.resume()
}
and I get this error:
"Cannot convert value of type '(Data?, URLResponse?, Error?) -> Void' to expected argument type '(Data?, URLResponse?, Error?) -> Void'"
Moreover, I also used many associative arrays to collect data from downloaded JSON file, like the following:
for comune in response! {
self.comuni.append(comune["nome"] as! String)
self.comuniWithID[comune["nome"] as! String] = Int(comune["idcomune"] as! String)
}
DispatchQueue.main.async {
self.myPicker.reloadComponent(1)
}
and another error I get is:
"Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members"
Please, would someone help me to correct them? Because I cannot understand what they mean and my app will be published next 30th September...
The most significant change is that all parameter labels in closures have been removed in Swift 3.
This is your code Swift 3 compatible.
As always, do not cast a Swift collection type to a Foundation counterpart. You will throw away all type information.
And don't use the annotations in the completion block return values, the compiler can infer the types. If you need to look up the actual types ⌥-click on the symbol.
fileprivate func requestFisheFieldWithHandler(_ url:String, completionHandler: #escaping ([String: String]?, NSError?) -> Void) {
let configuration = URLSessionConfiguration.default
let url: URL = URL(string: url)!
let urlRequest = URLRequest(url: url)
let session = URLSession(configuration: configuration)
let task = session.dataTask(with: urlRequest) { (data, response, error) -> Void in
if (error != nil) {
completionHandler(nil, error as NSError?)
}
else {
do {
let responseJSON = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions()) as! [String: String]
completionHandler(responseJSON, nil)
}
catch let error as NSError {
completionHandler(nil, error)
}
}
}
task.resume()
}
Regarding the second error you have to cast response! to something more meaningful than Any, I guess ... in response as! [[String:Any]]
The completion handler of dataTask(with:completionHandler:) in Swift 3 is changed to completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void so use Error instead of NSError.
let task = session.dataTask(with: urlRequest, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
For more about completion Handler check Apple Documentation.
For your error
Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members"
You need to specify the type of your response to [[String:Any]] then all goes to normal.
if let array = response as? [[String: Any]] {
for comune in array {
self.comuni.append(comune["nome"] as! String)
self.comuniWithID[comune["nome"] as! String] = Int(comune["idcomune"] as! String)
}
}

Swift 2.0 : Cannot invoke with an argument list of type... (HTTP Request)

Since I upgraded to Xcode 7 beta I have an error that I can't fix.
Here's the full code from my DataManager.swift
import Foundation
var TopAppURL:String = String()
var numberAsked:String = String()
class DataManager {
class func getInfo(ID : String){
TopAppURL = "http://sweetapi.com/?=\(ID)"
numberAsked = ID
}
class func loadDataFromURL(url: NSURL, completion:(data: NSData?, error: NSError?) -> Void) {
var session = NSURLSession.sharedSession()
// Use NSURLSession to get data from an NSURL
let loadDataTask = session.dataTaskWithURL(url, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if let responseError = error {
completion(data: nil, error: responseError)
} else if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode != 200 {
var statusError = NSError(domain:"com.raywenderlich", code:httpResponse.statusCode, userInfo:[NSLocalizedDescriptionKey : "HTTP status code has unexpected value."])
completion(data: nil, error: statusError)
} else {
completion(data: data, error: nil)
}
}
})
loadDataTask.resume()
}
class func getDataFromSweetApiOk(success: ((IDdata: NSData!) -> Void)) {
//1
print("DataManager loads \(TopAppURL)")
loadDataFromURL(NSURL(string: TopAppURL)!, completion:{(data, error) -> Void in
//2
if let urlData = data {
//3
success(IDdata: urlData)
}
})
}
}
So I got this error : "Cannot invoke 'dataTaskWithURL' with an argument list of type '(NSURL, completionHandler: (NSData!, NSURLResponse!, NSError!) -> Void)'"
I searched everywhere how to fix this but like Swift 2.0 is very new, I didn't found any solution.
func dataTaskWithURL(_ url: NSURL,
completionHandler completionHandler: ((NSData!,
NSURLResponse!,
NSError!) -> Void)?) -> NSURLSessionDataTask
has changed to
func dataTaskWithURL(_ url: NSURL,
completionHandler completionHandler: (NSData?,
NSURLResponse?,
NSError?) -> Void) -> NSURLSessionDataTask?
in iOS9. The completionHandler no longer is optional, and all parameters in the completionHandler are now optionals instead of implicitly unwrapped optionals.
Now, to help with this in future changes to the optional system, try to avoid (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in, you can simply use data, response, error in and then option-click for more details.
This will remove bloat from your code, and thus improve readability.
To solve your problem in the comments, check out this question.

Return object for a method inside completion block

I want to make a method with URL parameter that returns the response of calling that URL.
How can I return the data obtained inside a completion block for a method?
class func MakeGetRequest(urlString: String) -> (data: NSData, error: NSError)
{
let url = NSURL(string: urlString)
var dataResponse: NSData
var err: NSError
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
//How can I return the data obtained here....
})
task.resume()
}
If you want the MakeGetRequest method to return data obtained via dataTaskWithURL, you can't. That method performs an asynchronous call, which is most likely completed after the MakeGetRequest has already returned - but more generally it cannot be know in a deterministic way.
Usually asynchronous operations are handled via closures - rather than your method returning the data, you pass a closure to it, accepting the parameters which are returned in your version of the code - from the closure invoked at completion of dataTaskWithURL, you call that completion handler closure, providing the proper parameters:
class func MakeGetRequest(urlString: String, completionHandler: (data: NSData, error: NSError) -> Void) -> Void
{
let url = NSURL(string: urlString)
var dataResponse: NSData
var err: NSError
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
completionHandler(data: data, error: error)
})
task.resume()
}
Swift 5 update:
class func makeGetRequest(urlString: String, completionHandler: #escaping (Data?, Error?) -> Void) -> Void {
let url = URL(string: urlString)!
var dataResponse: Data
var err: NSError
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, respone, error) -> Void in
completionHandler(data, error)
})
task.resume()
}