Getting non--void error on function return [duplicate] - swift

This question already has answers here:
Unexpected Non-Void Return Value In Void Function (Swift 2.0)
(2 answers)
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I'm getting this error and I know this problem has been addressed on here before by people not adding the return -> to the function. I do not understand why this is still giving me error.
Unexpected non-void return value in void function
I'm trying to return a String called message.
func ParseIt(proURL: String, startStr: String, stopStr: String) -> String {
let url = URL(string: "https://www.siteimfetchingfrom.com/827444000973")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
} else {
let htmlContent = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
//print(htmlContent)
// Get all Product Info
//var proName = "id=\"productName\" value=\""
if let contentArray = htmlContent?.components(separatedBy: startStr) {
//print(contentArray)
if contentArray.count > 0 {
//proName = "\" required"
let newContentArray = contentArray[1].components(separatedBy: stopStr)
if newContentArray.count > 0 {
let message = newContentArray[0]
//print(newContentArray)
print(newContentArray[0])
return message // Error happens Here
}
}
}
}
}
task.resume()
}

The line return message is written inside of a closure. A return statement written inside a closure will return from the closure, not the surrounding function.
Seeing how you are performing a web request and getting the response, you should have a completion handler instead of a return. You can't immediately return a string from ParseIt because the request will take time.
// notice the extra completion parameter and the removal of the return type
func ParseIt(proURL: String, startStr: String, stopStr: String, completion: #escaping (String) -> Void) {
let url = URL(string: "https://www.siteimfetchingfrom.com/827444000973")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
...
// replace the return statement with this:
completion(message)
}
task.resume()
}
You can call it like this:
ParseIt(proURL: ..., startStr: ..., stopStr: ...) {
result in
// do something with "result"
}

Look carefully where your return statement belongs. It's not returning from ParseInt, it's actually returning from the completion closure passed to URLSession.shared.dataTask. The return type of that completion handler is void.
func dataTask(with request: URLRequest, completionHandler: #escaping
(Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask

Related

Unable to execute below closure due to parameter type in swift

I have a situation to upload content to S3 Bucket AWS.
I am using the below code and the code is not compiled.
Please advise.
let data = // The data to upload
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = {(task, progress) in DispatchQueue.main.async(execute: {
// Do something e.g. Update a progress bar.
})
}
let completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
// Do something e.g. Alert a user for transfer completion.
// On failed uploads, `error` contains the error object.
})
}
let transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadData(data,
bucket: S3BucketName,
key: S3UploadKeyName,
contentType: "image/png",
expression: expression,
completionHandler: completionHandler).continueWith { (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let _ = task.result {
// Do something with uploadTask.
}
return nil;
}
I am getting 2 below errors.
Unable to infer type of a closure parameter 'error' in the current context.
Unable to infer type of a closure parameter 'task' in the current context

How to refactor Swift API call to use Swift 5.5 async/await?

I want to refactor some API calls to use Swift 5.5's new async/await in my SwiftUI project. However, it's unclear to me how to replace or accomodate the completions.
Here's an example function which I want to refactor:
static func getBooks(completion: #escaping ([Book]?) -> Void) {
let request = getRequest(suffix: "books")
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
fatalError("Error: \(error)")
}
if let data = data {
if let books = try? JSONDecoder().decode([Book].self, from: data) {
DispatchQueue.main.async {
print("books.count: \(books.count)")
completion(books)
}
return
} else {
fatalError("Unable to decode JSON")
}
} else {
fatalError("Data is nil")
}
}.resume()
}
I beleve the new function signature would look something like this:
static func getBooks() async throws -> ([Book]?) {
// ...
}
However, I have no idea what to do with the URLSession.shared.dataTask, DispatchQueue.main.async and completion, etc.
Anyone know what the new function body should look like?
Thanks
func getBooks() async throws -> [Book] {
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode([Book].self, from: data)
}
This will throw if the request fails, and if the response cannot be decoded. Since the function is marked as throwing, then the calling function has to handle the raised errors.
You don't need to declare the returned [Book] to be optional, because it will either return an honest array, or throw an error.
In your additional code, you had to call your completion handler on the main queue, because you were calling it from within the completion block of the request. You don't need to do that here.

Throw error from dataTask completionHandler

In swift how do I throw an error within a completion handler like this:
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
do {
//something
completion(result)
} catch let jsonError {
throw CustomError.myerror //THIS DOESN'T WORK
}
})
task.resume()
as the error is
Invalid conversion from throwing function of type '(_, _, _) throws ->
()' to non-throwing function type '(Data?, URLResponse?, Error?) ->
Void'
Short story: You can't throw in a dataTask completion closure
You could return two values in the completion handler
...completion: #escaping (ResultType?, Error?)->Void
and return
completion(result, nil)
completion(nil, CustomError.myerror)
or more convenient use an enum with associated type
enum Result {
case success(ResultType), failure(Error)
}
...completion: #escaping (Result)->Void
and return
completion(.success(result))
completion(.failure(CustomError.myerror))
You can process the result
foo() { result in
switch result {
case .success(let resultType): // do something with the result
case .failure(let error): // Handle the error
}
}
Update:
In Swift 5 using the new built-in Result type it's even more comfortable because Result can capture the result of the throwing expression
...completion: #escaping (Result<MyType,Error>)->Void
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
completion(Result { try something()})
})
task.resume()
Update 2:
With async/await completion handlers are gone
do {
let (data, response) = try await URLSession.shared.data(for: request)
} catch {
throw CustomError.myerror
}

Custom Serialization finds incorrect "response" in Alamofire 4.0

I'm attempting to define a custom model serialization for Alamofire 4.0. So far I'm following the model presented used by responseJson and friends. Specifically, what I have so far is:
extension Alamofire.Request {
public static func serializeResponseModel<T:ModelObject>(response:HTTPURLResponse?, data:Data?, error:Error?) -> Alamofire.Result<T> {
switch serializeResponseJSON(options: [], response: response, data: data, error: error) {
case .success(let jsonObject):
do {
return .success(try T(json:jsonObject as! JSONObject))
}
catch {
return .failure(error)
}
case .failure(let error):
return .failure(error)
}
}
}
extension Alamofire.DataRequest {
public static func serializeResponseModel<T:ModelObject>() -> DataResponseSerializer<T> {
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponseConcierge(response: response, data: data, error: error)
}
}
#discardableResult
public func responseModel<T:ModelObject>(queue: DispatchQueue? = nil, completionHandler: #escaping (DataResponse<T>) -> Void) -> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.serializeResponseModel(),
completionHandler: completionHandler
)
}
}
Unfortunately, the framework is somewhat poorly implemented and the line return response( is finding the response property (defined in Request) and not the appropriate response method (defined in DataRequest), which leads to the compile error:
Cannot call value of non-function type 'HTTPURLResponse?'
What am I missing here that allows this to work in the responseJson case, but not in my case?
Apparently the problem arose from over-generalization, and the compiler not being able to generate an appropriate type for DataRequest.serializeResponseModel() When I changed responseModel to the following and specified the appropriate type, things work as expected:
#discardableResult
public func responseModel<T:ModelObject>(queue: DispatchQueue? = nil, completionHandler: #escaping (DataResponse<T>) -> Void) -> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.modelResponseSerializer() as DataResponseSerializer<T>,
completionHandler: completionHandler
)
}

Trailing Closures on generics?

Hi I am trying to understand the following code from Alamofire. How can you initialise a struct with "{}" I know that you can call a closure with Trailing Closures. I know I am totally missing something, but what?
extension Request {
public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<T, NSError> { // What is this?
request, response, data, error in
guard error == nil else { return .Failure(error!) }
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
if let
response = response,
responseObject = T(response: response, representation: value)
{
return .Success(responseObject)
} else {
let failureReason = "JSON could not be serialized into response object: \(value)"
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
The struct ResponseSerializer from Alamofire
public struct ResponseSerializer<Value, Error: ErrorType>: ResponseSerializerType {
/// The type of serialized object to be created by this `ResponseSerializer`.
public typealias SerializedObject = Value
/// The type of error to be created by this `ResponseSerializer` if serialization fails.
public typealias ErrorObject = Error
/**
A closure used by response handlers that takes a request, response, data and error and returns a result.
*/
public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>
/**
Initializes the `ResponseSerializer` instance with the given serialize response closure.
- parameter serializeResponse: The closure used to serialize the response.
- returns: The new generic response serializer instance.
*/
public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>) {
self.serializeResponse = serializeResponse
}
}
Your question can be greatly pared down (and should have been). Here is the relevant declaration of ResponseSerializer:
public struct ResponseSerializer<Value, Error: ErrorType>: ResponseSerializerType {
public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>) {
self.serializeResponse = serializeResponse
}
}
So this initializer, init(serializeResponse:), takes one parameter — a function taking four parameters and returning one parameter (of the specified types).
Thus, we can initialize like this:
func f (request:NSURLRequest?, response:NSHTTPURLResponse?, data:NSData?, error:NSError?) -> Result<Value, Error>) {
guard error == nil else { return .Failure(error!)
}
let responseSerializer = ResponseSerializer<T, NSError>(serializeResponse:f)
However, this can be condensed. We don't really need the function f for anything else, so we can supply it as an anonymous function; it doesn't need a name or a full declaration. Moreover, there is a "shortcut" rule for anonymous functions, that if an anonymous function is the last parameter to a function, it can be provided literally after the function's closing parentheses, with the parameter name omitted. And if the function takes no other parameters, its parentheses can be omitted altogether.
Well, this init is exactly such a function — it takes a function as its last (and only) parameter — so that is exactly what the code in question does:
let responseSerializer = ResponseSerializer<T, NSError> {
request, response, data, error in
guard error == nil else { return .Failure(error!)
}
If I read it all correctly the code above has a pattern similar to this:
// just a something
struct Blah {
var stuffs : (message:String) -> Void
init(closure:(message:String) -> Void) {
self.stuffs = closure
}
}
// an extension because the code above is also in an extension, but not needed at all
extension Blah {
// a function with a closure that also returns an instance of Self
func spawnChild(closure:(message:String) -> Void) -> Blah {
return Blah(closure: closure) // closure is passed to spawn
}
}
Tests :
let alpha = Blah { (message) -> Void in
print("alpha",message)
}
let beta = alpha.spawnChild { (message) -> Void in
print("beta", message)
}
alpha.stuffs(message: "parrent") // alpha parent
beta.stuffs(message: "spawn") // beta spawn
Remember that the trailing closure is just syntactic sugar for an input parameter that is a closure. So anything that takes input parameters can have a trailing closure.