Extra slash in GET request with Swift - ios

I'm trying to make GET request. Url is configured without slash at the end of URL. Server receives url with "/" at the end so I get error 404. I can see response.url with slash, but urlRequest contains URL without it.
Can't understand, what's going on.
Meant to send:
http://someUrl.com/api
Sent:
http://someUrl.com/api/
Code sample
guard let url = URL(string: self.rootUrl + "/api") else {
print ("Can't make URL")
return
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "GET"
let sessionConf = URLSessionConfiguration.default
let session = URLSession.init(configuration: sessionConf)
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /api")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
print (urlRequest)
let json = try? JSONSerialization.jsonObject(with: responseData) as? [String: Any]
if ( json == nil ) {
print ("error json")
print(response!)
} else { ....

Swift cached response for this request, set CachePolicy to ignore reload and try to do GET request to other URL and than try again.

Related

URLRequest in Swift5

I'm using the OpenWeather Current Weather Data Api, and trying to make a url request to get json data from the api in Swift5. I need to print the json data. Here is some code I found on the internet that I have been trying to use, but has not been working.
Note: I do NOT want to use any external libraries. like alamofire.
let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=35&lon=139&appid={APIKEY}")!
var request = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
if let error = error {
print(error)
} else if let data = data {
print(data)
} else {
print("nope")
}
}
task.resume()
The Openweathermap API documentation is a bit misleading, the expression {API key} indicates the API key without the braces.
Insert the key with String Interpolation
let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=35&lon=139&appid=\(APIKEY)")!
The URLRequest is not needed and dataTask returns either valid data or an error
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if let error = error { print(error); return }
print(String(data: data!, encoding: .utf8)!)
}
task.resume()
To display the data create an appropriate model and decode the data with JSONDecoder
So, at first you should be aware that you are registered and already have your own API Key. The main reason that can occur here for not opening link is that You are using a Free subscription and try requesting data available in other subscriptions . And for future if you want to do just get request you don't need to do session.dataTask(with: request), the session.dataTask(with: url) will be OK.)
Here is simpler way of your code.
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=35&lon=139&appid={APIKEY}") else {return}
let session = URLSession.shared
let task = session.dataTask(with: url) { (data, response, error) in
if let error = error {
print(error)
} else if let data = data {
print(data)
} else {
print("nope")
}
}
task.resume()
Not all APIs work with just URL
So if the API has a header in request, try this code.
Note: The header are dependent on your API.
let semaphore = DispatchSemaphore (value: 0)
let param = [
"language": "english",
"serviceRequestId": 1,
"location": ["latitude": "12.34","longitude": "12.34"]
] as [String : Any]
var request = URLRequest(url: URL(string: "UrlHere")!,timeoutInterval: Double.infinity)
request.addValue("tokenHere", forHTTPHeaderField: "Authorization")
do{
let i = try JSONSerialization.data(withJSONObject: param, options: .prettyPrinted)
// print("\(type(of: i))")
print(String(data: i,
encoding: .ascii) ?? "nil")
request.httpMethod = "POST"
request.httpBody = i
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
semaphore.signal()
do{
let postData = try JSONDecoder().decode(ModelRootClassHere.self, from: data)
print(postData)
MyData = postData
completion()
}
catch{
print(error)
print("error............")
}
}
task.resume()
semaphore.wait()
}catch{
print(error)
}

Json response "GET" request: "No session Found" - Swift

I want to make a "GET" request to apache web server to retrieve some data. Before making the above request, I log in making a "POST" request, the web server opens a new session and I get the json response:
gotten json response dictionary is
["user": {
email = "asd#asd.it";
password = "<null>";\\ for security reason it doesn't return the password
sessionID = 6C61269BB7BB40682E96AD80FF8F1CB7;
}]
So far it's all correct. But then when I try to make the "GET" request to retrive the user's data, I get this response:
gotten json response dictionary is
["message": {
errorCode = F01;
errorDetails = "No session is found in the server, either you have not set the JSESSIONID cookie or the session does not exists";
message = "No session Found";
}]
The code for the "POST" request is:
let urlComp = NSURLComponents(string: "http://localhost:8080/mnemosyne/auth")!
let postDict: [String:Any] = ["email": "asd#asd.it", "password" : "password"]
var items = [URLQueryItem]()
for (key,value) in postDict {
items.append(URLQueryItem(name: key, value: value as? String))
}
urlComp.queryItems = items
var urlRequestAuth = URLRequest(url: urlComp.url!, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0 * 1000)
urlRequestAuth.httpMethod = "POST"
let postData = try? JSONSerialization.data(withJSONObject: postDict, options: [])
urlRequestAuth.httpBody = postData
let taskAuth = URLSession.shared.dataTask(with: urlRequestAuth) { (data, response, error) in
guard error == nil else {
print(error as Any)
return
}
guard let content = data else {
print("No data")
return
}
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any] else {
print("Not containing JSON")
return
}
print("gotten json response dictionary is \n \(json)")
}
taskAuth.resume()
This is the code for "GET":
let url = URL(string: "http://localhost:8080/mnemosyne/rest/task")
var request = URLRequest(url: url!)
let taskTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else {
print ("error: \(error!)")
return
}
guard let content = data else {
print("No data")
return
}
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any] else {
print("Not containing JSON")
return
}
print("gotten json response dictionary is \n \(json)")
}
taskTask.resume()
What's the problem? How can I pass in the request the session Id that I want to use?
I think you need send sessionID in your GET request.
I would at first try some util like Postman or RESTed free apps, to test request and understand how to send correct POST request.
Depending on server implementation - you could send session ID as a part of url, in POST body or in POST header.

How to pass bearer token to make Yelp API call with URLSessoin

U P D A T E D... The function with what works!
I would like to incorporate the yelp api into an app but can't successfully pass my authorization token on the URL string. Do I need to do something to connect the URLRequest to the URLSessoin call and its not using the header? Maybe the key value pairs is wrong? The below function returns:
error = {
code = "TOKEN_MISSING";
description = "An access token must be supplied in order to use this endpoint.";
};
I was able to use postman to get the yelp API call working, but only by clicking the "Header" section on postman and putting in Bearer and then my yelp key. I googled around a bit and found some links that indicate that you can add a header to the URLSession that I assume would work the way postman does but I haven't been able to get it to work.
I know there are some githubs with yelp API repos but I am trying to not install a large set of code that I don't understand into my app, when all I want is the JSON that I can see is coming through on postman. Can anyone help me understand how I would edit code similar to the Here example below so that I can get the Authorization/Bearer that yelp requires?
func getYelp() {
let appSecret = "Bearer <YELP APIKEY>"
let link = "https://api.yelp.com/v3/businesses/search?latitude=37.786882&longitude=-122.399972"
if let url = URL(string: link) {
// Set headers
var request = URLRequest(url: url)
request.setValue("Accept-Language", forHTTPHeaderField: "en-us")
request.setValue(appSecret, forHTTPHeaderField: "Authorization")
print("Attempting to get places around location from Yelp")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject // Added "as anyObject" to fix syntax error in Xcode 8 Beta 6
print("Printing all JSON/n/n//n--------------------------")
print(jsonResult)
print("Printing from results/n/n//n--------------------------")
if let description = ((jsonResult["search"] as? NSDictionary)?["context"] as? NSDictionary)?["href"] as? String {
} else {
print("JSON pull failed/n/n//n--------------------------")
}
} catch {
print("JSON Processing Failed/n/n//n--------------------------")
}
}
}
}
task.resume()
} else {
resultLabel.text = "Couldn't get results from Here"
}
}
You're mixing up between the headers and the url, you need to set your headers correctly
if let url = URL(string: "https://places.cit.api.here.com/places/v1/discover/around?at=37.776169%2C-122.421267&app_id=\(app_id)&app_code=\(app_code)") {
var request = URLRequest(url: url)
// Set headers
request.setValue("Accept-Language", forHTTPHeaderField: "en-us")
request.setValue("Authorization", forHTTPHeaderField: "Bearer " + token // Token here)
print("Attempting to get places around location")
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
// ...
Lets say you have an api with "https://google.com" (this is just an example with fake keys)
and an api key that is "ApiKey: 92927839238293d92d98d98d92".
You would then take this information and do this.
let uri = URL(string:"https://google.com")
if let unwrappedURL = uri {
var request = URLRequest(url: unwrappedURL)request.addValue("92927839238293d92d98d98d92", forHTTPHeaderField: "ApiKey")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
// you should put in error handling code, too
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
// HERE'S WHERE YOUR DATA IS
print(json)
} catch {
print(error.localizedDescription)
}
}
}
dataTask.resume()
}
Please remember that you would replace the google.com with your GET address and the APIKey header with your own api key values.
Also, this will print out all the JSON like in PostMan.
If this works for you, then I also have a link on accessing the JSON Objects.

swift JSON login REST with post and get response example

It's my first experience with REST in iOS development with swift. I couldn't find any working or straight (simple) example for doing what i need here.
I have a login backend (https://myaddress.com/rest/login), where I need to pass 2 params: login and password. When I pass good values (user exists in database) I get 2 variables as a result: token (string) and firstLogin (bool). So when I get those values I know that login is successful and I can log in into my app.
So I am begging you for an example (just a simple function) of how to achieve that. If I get working code example I will know how to use it for other rest services in my app. I tried many solutions from tutorials I found, but any of them was working for me.. So to not waste my time searching I would like someone experienced to show me the way to achieve that.
I am not sure if Alamofire is so good to use, I know that swift 4 has it's own build neetwork services and to work with json. Any solution that works would be great.
Also, side question - if I would prefer to use Alamofire, do I need to use swiftyJSON also? Or it's just for parsing?
You can use URLSession if you don't like to import Alamofire in your Project to perform a simple task.
here are some method : GET, POST, DELETE METHODS and tutorial
GET METHOD
func makeGetCall() {
// Set up the URL request
let todoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// now we have the todo
// let's just print it to prove we can access it
print("The todo is: " + todo.description)
// the todo object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
guard let todoTitle = todo["title"] as? String else {
print("Could not get todo title from JSON")
return
}
print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
POST METHOD
func makePostCall() {
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
let newTodo: [String: Any] = ["title": "My First todo", "completed": false, "userId": 1]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /todos/1")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,
options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let todoID = receivedTodo["id"] as? Int else {
print("Could not get todoID as int from JSON")
return
}
print("The ID is: \(todoID)")
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
DELETE METHOD
func makeDeleteCall() {
let firstTodoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
var firstTodoUrlRequest = URLRequest(url: URL(string: firstTodoEndpoint)!)
firstTodoUrlRequest.httpMethod = "DELETE"
let session = URLSession.shared
let task = session.dataTask(with: firstTodoUrlRequest) {
(data, response, error) in
guard let _ = data else {
print("error calling DELETE on /todos/1")
return
}
print("DELETE ok")
}
task.resume()
}
Thanks #MAhipal Singh for you answer. I'll post here example with Alamafire that I used so it's all in one stack question. It's easier than I though, solutions I tried to use before were not working cause I had problems with pinning certificate about I forgot..
func loginRest(login:String, password:String, deviceId:String){
let urlStr = restServices.REST_MAIN_URL + restServices.REST_LOGIN
let params = ["login":login, "password":password, "deviceId":deviceId]
let paramsJson = try! JSONSerialization.data(withJSONObject: params)
var headers: HTTPHeaders = ["Content-Type": "application/json"]
Alamofire.request(urlStr, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success:
print("SUKCES with \(response)")
case .failure(let error):
print("ERROR with '\(error)")
}
}
If the post is proper the response is (console print):
SUKCES with SUCCESS: {
firstLogin = 1;
token = "dfkafjkfdsakfadsjfksjkfaadjfkjdfkjfskjfdkafjakfjakfjsafksjdafjy878328hjh";
}

What to do when link requires cookies to function in app

So when I send request without any cookies I get an error.
when trying to get request manually (in a browser) I first need to go to homepage, which loads 4 cookies, then the request can proceed without error. Some experimenting showed that I need only one of the cookies.
This is completely new to me and any snippets/guides on how this can be implemented are welcome. I understand that this question Is pretty vague but I am lost in where to start looking and would appreciate any help
let url = URL(string: "homepage")
var request = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print("response1")
print("error = \(error)")
print("response = \(response)")
return
}
if let json = try? JSONSerialization.jsonObject(with: data) {
print("responseObject = \(json)")
} else {
print("response2")
print("responseString = \(String(data: data, encoding: .utf8))")
}
let url2 = URL(string: "requestpage")
var request = URLRequest(url: url2!)
request.setValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print("response2")
print("error = \(error)")
print("response = \(response)")
return
}
if let json = try? JSONSerialization.jsonObject(with: data) {
print("responseObject = \(json)")
} else {
print("response2")
print("responseString = \(String(data: data, encoding: .utf8))")
}
}
task.resume()
}
task.resume()
In your code, you should:
initiate URLSessionTask for the landing page that sets the cookies;
that will, with no intervention on your part, will set the cookies in your URLSession;
after that response is successfully received, you can then send the next request and the cookies should be there automatically with no extra code.
Unless you did something to explicitly interfere with this process, URLSession seamlessly receives the the cookies and includes them in subsequent requests for you.
If you're scraping a web site (and if so, please check the ToS to make sure it's permitted), you are well advised to request the pages in the same order that a web browser would. It might feel like you could bypass that landing page and set the cookies yourself, but often the cookie values are generated dynamically and you can have problems if you try to bypass that landing page.
You asked for an example, and there's really not much to show. Just initiate second request from the completion of the first request. There's not a single reference to cookies in the Swift code. URLSession takes care of everything:
let url = baseURL.appendingPathComponent("setcookie.php")
var request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print("error = \(error)")
print("response = \(response)")
return
}
let url = baseURL.appendingPathComponent("results.php")
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print("error = \(error)")
print("response = \(response)")
return
}
if let json = try? JSONSerialization.jsonObject(with: data) {
print("responseObject = \(json)")
} else {
print("responseString = \(String(data: data, encoding: .utf8))")
}
}
task.resume()
}
task.resume()
My first page, setcookie, just set a cookie:
<?php
setcookie("foo", "bar", time()+3600);
$result = array("success" => true);
echo json_encode($result);
?>
And my second page retrieved the cookies:
<?php
$result = array("success" => true);
if (isset($_COOKIE))
$result["_COOKIE"] = $_COOKIE;
header("Content-Type: application/json");
echo json_encode($result);
?>
And the above Swift code output:
responseObject = {
"_COOKIE" = {
foo = bar;
};
success = 1;
}

Resources