Cannot do Alamofire request in Swift 4 - swift

I get this error and I have no idea why I get it. It is not shown in the picture but I do import UIKit and Alamofire. It would be great if you could let me know what the problem is.
I think the main problem is in the Alamofire.request(.GET, currentWeatherURL) part.
func downloadWeatherDetails(completed: DownloadComplete) {
//Alamofire Download
let currentWeatherURL = URL(string: CURRENT_WEATHER_URL)!
Alamofire.request(.GET, currentWeatherURL).responseJSON { response in
let result = response.result
print(result)
}
}

There are several issues with your code:
The order of your parameters is wrong, the request URL should prepend the method type, which should have the name method.
Method types are now lowercased in Alamofire.
You should not force unwrap the URL initializer - moreover, Alamofire does this for you automatically, just pass the string as a parameter.
Here is your code, fixed:
func downloadWeatherDetails(completed: DownloadComplete) {
Alamofire.request(CURRENT_WEATHER_URL, method: .get).responseJSON { response in
let result = response.result
print(result)
}
}

Related

How to get original requests in Alamofire 5?

I made a wrapper for Alamofire which makes the data request first and then it prints the details of original URLRequest.
let dataRequest = session.request(url, method: .get, parameters: parameters)
let originalRequest = dataRequest.request
// Now print somehow the details of original request.
It worked fine on Alamofire 4.9, but it stopped in the newest 5.0 version. The problem is that dataRequest.request is nil. Why this behavior has changed? How can I access URLRequest underneath DataRequest?
URLRequests are now created asynchronously in Alamofire 5, so you're not going to be able to access the value immediately. Depending on what you're doing with the URLRequest there may be other solutions. For logging we recommend using the new EventMonitor protocol. You can read our documentation to see more, but adding a simple logger is straightforward:
final class Logger: EventMonitor {
let queue = DispatchQueue(label: ...)
// Event called when any type of Request is resumed.
func requestDidResume(_ request: Request) {
print("Resuming: \(request)")
}
// Event called whenever a DataRequest has parsed a response.
func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
debugPrint("Finished: \(response)")
}
}
let logger = Logger()
let session = Session(eventMonitors: [logger])
I had to obtain the URLRequest in a test case. Solved it by adding .responseData and using XCTestExpectation to wait for the async code to return:
func testThatHeadersContainContentEncoding() {
let exp = expectation(description: "\(#function)\(#line)")
let request = AF.request("http://test.org",
method: .post, parameters: ["test":"parameter"],
encoding: GZIPEncoding.default,
headers: ["Other":"Header"])
request.responseData { data in
let myHeader = request.request?.value(forHTTPHeaderField: additionalHeader.dictionary.keys.first!)
// .. do my tests
exp.fulfill()
}
waitForExpectations(timeout: 10, handler: nil)
}

Alamofire iOS13

I want to update my code in the past when I used swift 2 or 3. I am stuck where I want to use Alamofire but the way to use it changed and I don't know how to use it anymore. Can anybody update this part of the code and explain a little bit? Thank you.
This is the original code.
Alamofire.request(.POST, url)
.response{ (request, response, data, error) in
let xml = SWXMLHash.parse(data!)
let sunsetTime = xml["result"]["rise_and_set"]["sunset_hm"].element?.text
self.sunsetTimeLabel.text = sunsetTime
self.getDateFromString(sunsetTime,year: comp.year,month: comp.month,day: comp.day)
if (error != nil) {
print(error)
}
}
this is the code I was writing.
AF.request(url, method: .post).responseJSON { (responseData) in
let xml = SWXMLHash.parse(responseData as Data)
let sunsetTime = xml["result"]["rise_and_set"]["sunset_hm"].element?.text
self.sunsetTimeLabel.text = sunsetTime
There is an error saying "Cannot convert value of type 'AFDataResponse' (aka 'DataResponse') to type 'Data' in coercion"
Your first code snippet is Alamofire 3 syntax. I infer from the second code snippet that you are now using Alamofire 5.
There are a few issues:
You are calling responseJSON (which you’d only use if your response was JSON, not XML). Use response or, better, responseData.
The response object passed to this closure is not a Data, itself. In the case of responseData method, it is a AFDataResponse object, which has a data property (which is a Data?). You have to extract the Data object from this AFDataResponse, either by unwrapping the contents of the data property, or from the result (see next point).
You should probably check for success or failure and extract the Data from the response.result object.
So, pulling this together, you end up with something like:
AF.request(url, method: .post).responseData { response in
switch response.result {
case .failure(let error):
print(error)
case .success(let data):
let xml = SWXMLHash.parse(data)
...
}
}

Swift program never enters CompletionHandler for a dataTask

I am in the process of implementing a REST API with Swift. Of course, part of this API is using HTTP requests to retrieve and send data.
Full disclosure, I am inexperienced with Swift and am using this as a learning project to get my feet wet, so to speak. But it's turned into much more of a difficult project than I anticipated.
In implementing the first get method, I have (finally) gotten rid of all the compilation errors. However, when I call the function which utilizes the URLRequest, URLSession, dataTask, etc, it is never entered.
Upon debugging the program, I can watch the program execution reach the CompletionHandler, and skip over it right to "task.resume()."
A similar construction works in a Swift Playground, but does not work in the actual project proper.
So far I have tried a few things, namely making the function access a class instance variable, in hopes that that would force it to execute. But it does not.
I think the issue may be dealing with synchronicity, and perhaps I need to use a Semaphore, but I want to make sure I'm not missing anything obvious first.
import Foundation
/**
A class to wrap all GET and POST requests, to avoid the necessity of repeatedly writing request code in each API method.
*/
class BasicRequest {
private var url: URL
private var header: [String: String]
private var responseType: String
private var jsonResponse: Any?
init(url: URL, header: [String: String], responseType: String) {
self.url = url
self.header = header
self.responseType = responseType
} //END INIT
public func requestJSON() -> Any {
// Create the URLRequest object, and fill the header with the header fields as provided.
var urlRequest = URLRequest(url: self.url)
for (value, key) in self.header {
urlRequest.addValue(value, forHTTPHeaderField: key)
}
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
print("Entered the completion handler")
if error != nil {
return
}
guard let httpResponse = response as? HTTPURLResponse, 200 == httpResponse.statusCode else {
print("HTTP Request unsuccessful")
return
}
guard let mime = response?.mimeType, mime == "application/json" else {
print("Not a JSON response")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print(json)
self.jsonResponse = json
} catch {
print("Could not transform to JSON")
return
}
}
task.resume()
return "Function has returned"
} //END REQUESTJSON
}
The expected result would be returning a JSON object, however that does not seem to be the case.
With respect to error messages, I get none. The only log I get in the debugger is the boilerplate "process exited with code 0."
To be truthful, I'm at a loss with what is causing this not to work.
It appears you're writing this in a command-line app. In that case the program is terminating before the URLRequest completes.
I think the issue may be dealing with synchronicity, and perhaps I need to use a Semaphore, but I want to make sure I'm not missing anything obvious first.
Exactly.
The typical tool in Swift is DispatchGroup, which is just a higher-level kind of semaphore. Call dispatchGroup.enter() before starting the request, and all dispatchGroup.leave() at the end of the completion handler. In your calling code, include dispatchGroup.wait() to wait for it. (If that's not clear, I can add code for it, but there are also a lot of SO answers you can find that will demonstrate it.)

Alamofire multiple requests iteration

I have four different requests in my application, three of them requires one call only and the last requires from 1 - 10.
All works fine until the last request when I´m iterating through my data and making the calls. This is my code in Class1:
var data = ...
var points = ...
// I create a new group
let getPointGroup = dispatch_group_create()
// I iterate through my data
for d in data{
// I enter the group
dispatch_group_enter(getPointGroup)
dataService.getPoints(d.point), success: { points -> Void in
points.append(points)
// I leave the group so that I go to the next iteration
dispatch_group_leave(getPointGroup)
}
}
Alamofire request looks like this in Class2:
let headers = [
"Authorization": "Bearer \(token)",
"Content-Type": "application/x-www-form-urlencoded"
]
Alamofire.request(.GET, url, headers:headers)
.responseJSON { response in
switch response.result {
case .Success:
let json = JSON(data: response.data!)
print(json)
success(json)
case .Failure(let error):
print(error)
}
}
But I never hit the GET request, if I remove the iteration completely and just call the Alamofire request once it works perfectly.
Any ideas of how to solve the Alamofire iteration request?
Edit
Not really a duplicate, I have the snippets below in the different classes and the example does not really solve my issue
If this is not running, you could be deadlocking if you use dispatch_group_wait on the main thread, thereby blocking that thread, and preventing Alamofire from running any of its completion handlers (which also require the main thread). This is solved (assuming you are, indeed, using dispatch_group_wait), by replacing that with dispatch_group_notify.
Thus:
let group = dispatch_group_create()
for d in data {
// I enter the group
dispatch_group_enter(group)
dataService.getPoints(d.point)) { additionalPoints, error in
defer { dispatch_group_leave(group) }
guard let let additionalPoints = additionalPoints else {
print(error)
return
}
points.append(additionalPoints)
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
// go to the next iteration here
}
Where:
func getPoints(point: WhateverThisIs, completionHandler: (JSON?, NSError?) -> ()) {
let headers = [
"Authorization": "Bearer \(token)"
]
Alamofire.request(.GET, url, headers: headers)
.responseJSON { response in
switch response.result {
case .Success:
let json = JSON(data: response.data!)
completionHandler(json, nil)
case .Failure(let error):
completionHandler(nil, error)
}
}
}
Now, I don't know what your various parameter types were, so I was left to guess, so don't get lost in that. But the key is that (a) you should make sure that all paths within your Alamofire method will call the completion handler; and (b) the caller should use dispatch_group_notify rather than dispatch_group_wait, avoiding the blocking of any threads.
Note, in order to make the completion handler callable even if the network request failed, I had to make the parameter to that closure optional. And while I was there, I added an optional error parameter, too.
A few unrelated changes included in the above sample:
I'd suggest using a different variable name for the parameter of your closure. The points.append(points) in your original code snippet suggests some confusion between your points array and the points that is passed back in the closure.
You don't have to set the Content-Type header, as Alamofire does that for you.
I didn't change it above, but it is inefficient to use responseJSON (which uses NSJSONSerialization to parse the JSON) and then use SwiftyJSON to parse the raw data with NSJSONSerialization again. Personally, I don't bother with SwiftyJSON, but if you want to use it, I'd suggest use Alamofire's response method rather responseJSON. There's no point in parsing the JSON twice.

HTTP Request error using Alamofire

I am trying to access my MAMP database webservice using Alamofire. Following is my code:
Following is my router to construct my URL:
enum Router: URLRequestConvertible {
static let baseURLString = "http://pushchat.local:44447/"
case PostJoinRequest(String,String,String,String,String)
var URLRequest: NSURLRequest {
let (path: String, parameters: [String: AnyObject]) = {
switch self {
case .PostJoinRequest (let addPath, let userID, let token, let nickName, let secretCode):
let params = ["cmd": "join", "user_id": "\(userID)", "token": "\(token)", "name": "\(nickName)", "code": "\(secretCode)"]
return (addPath, params)
}
}()
let URL = NSURL(string: Router.baseURLString)
let URLRequest = NSURLRequest(URL: URL!.URLByAppendingPathComponent(path))
let encoding = Alamofire.ParameterEncoding.URL
return encoding.encode(URLRequest, parameters: parameters).0
}
}
Following is my viewdidload code:
Alamofire.request(.POST,Router.PostJoinRequest("api.php","12345678901234","12345678901234","ABCDEF","TopSecret")).responseJSON()
{(request, response, JSON, error) in
println(JSON)
}
Following is the compile error:
Cannot invoke 'responseJSON' with an argument list of type '((,,,)->_)'
Following is the declaration from Almofire for your reference.
:param: method The HTTP method.
:param: URLString The URL string.
:param: parameters The parameters. `nil` by default.
:param: encoding The parameter encoding. `.URL` by default.
:returns: The created request.
*/
// public func request(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
// return request(encoding.encode(URLRequest(method, URLString), parameters: parameters).0)
// }
Please let me know why am I facing this issue while chaining and what is it that I am not doing right?
Thanks for your help.
Dev
The compiler error message is really misleading – there is no problem with responseJSON but with request method itself.
In fact compiler does not like your second parameter. You are passing URLRequestConvertible but URLStringConvertible is expected (see the signature you posted).
Maybe you wanted to call another version of request method:
//:param: URLRequest The URL request
//:returns: The created request.
public func request(URLRequest: URLRequestConvertible) -> Request
In that case you have to adjust your Router class and set HTTP method into NSURLRequest created inside. For example:
let URLRequest = NSMutableURLRequest(URL: URL!.URLByAppendingPathComponent(path))
URLRequest.HTTPMethod = "POST"
Note you will also probably need to use another parameter/data encoding.