Correct way to retrieve json data from remote url using swift 4 for an array - swift

I am trying to see what is the latest and greatest way to retrieve json data in swift 4 (using Codable structure).
I have the following json data in a remote url:
[
{
"products": [
{
"productid": "01",
"price": "01"
},
{
"productid": "02",
"price": "02"
}
]
}
]
I have also setup the corresponding codable structure. My questions is what would be the correct way to retrieve this data using the latest techniques for swift 4.
I am seeing various ways such as:
DataManager.getJSONFromURL ...
let jsonData = try Data(contentsOf: URL ...
let task = URLSession.shared.dataTask(with: url) ...
try JSONSerialization.data...
I would like to know which is the correct (latest) format for retrieving json data using swift 4 from a remote URL. Thank you.

I found the answer to my question.
Apple announced Swift 4 as part of Xcode 9 at WWDC 2017. It brings some really nice improvements to existing Swift 3 features as well as stability. The latest ways of working with REST API in Swift 4 is using URLSession and JSONDecoder. The later was introduced with Swift 4.
In Swift 3, most of developers used third party frameworks such as SwiftyJson and Alamofire in order to work with REST APIs. The reason for this, most of the time, was that parsing JSON using Swift was very tedious. More precisely - you had to set up initializer in your Model, had to do loops to assign values in your controller, had to typecast values and so on. You could always copy/paste your code, but still it was overwhelming. With Swift 4 all you will need to do is to write just a single line to decode and parse JSON.
URLSession and JSONDecoder are all you need in Swift 4 for retrieving json data from a remote url.
For more information and an example, you can check this site:
URLSession and JSONDecoder in Swift 4

func getRequestWithUrl(url : String ,onCompletionHandler : #escaping ([String : Any]?) -> Void){
let headers : HTTPHeaders = [
"X-Api-Key": "EPE67704498C76B16CF29B956B2A2E91",
"Accept": "application/json",
]
Alamofire.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success:
onCompletionHandler(response.result.value as? [String : Any])
break
case .failure(_):
onCompletionHandler(nil)
}
}
}

we have an API which allows us to create a new board with a title “New
York Highlights”. For this, using Alamofire the code is very easy:
AF.request("https://api.mywebserver.com/v1/board", method: .get, parameters: ["title": "New York Highlights"])
.validate(statusCode: 200..<300)
.responseDecodable { (response: DataResponse) in
switch response.result {
case .success(let board):
print("Created board title is \(board.title)") // New York Highlights
case .failure(let error):
print("Board creation failed with error: \(error.localizedDescription)")
}
}
For Alamofire you need to install framework for more detail read this document
Doing exactly the same with the URLSession API requires a bit more of
work.
enum Error: Swift.Error {
case requestFailed
}
// Build up the URL
var components = URLComponents(string: "https://api.mywebserver.com/v1/board")!
components.queryItems = ["title": "New York Highlights"].map { (key, value) in
URLQueryItem(name: key, value: value)
}
// Generate and execute the request
let request = try! URLRequest(url: components.url!, method: .get)
URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
guard let data = data,
let response = response as? HTTPURLResponse, (200 ..< 300) ~= response.statusCode,
error == nil else {
// Data was nil, validation failed or an error occurred.
throw error ?? Error.requestFailed
}
let board = try JSONDecoder().decode(Board.self, from: data)
print("Created board title is \(board.title)") // New York Highlights
} catch {
print("Board creation failed with error: \(error.localizedDescription)")
}
}
credit

Related

Api call to .net C sharp web api project fails in swift but works fine

I am using a asp.net back end with a login end point but no matter what I DO in the swift version of this code I get a 415 when I use it in .net and sharp the api works am not sure what am doing wrong here.
And yes I have enabled transport protocol but its not decoding the jwt token correctly for me in swift
Basically the end point returns the jet token used for accessing the api in an object
let jwtAccessToken: String = ""
let urlString = "http://url.com/login" *** hidden for security
purposes but is correct ****
func CallWebApi()
{
// create the url with URL
let url = URL(string: urlString)! // change server url accordingly
let parameters: [String: Any] = [ "username":
"user1#domain.com", "password": "pass1"]
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded",
forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "Post"
do {
request.httpBody = try
JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
return
}
let task = URLSession.shared.dataTask(with: request) {
data, response, error in
guard
let data = data,
let response = response as? HTTPURLResponse,
error == nil
else {
// check for fundamental networking error
print("error", error ?? URLError(.badServerResponse))
return
}
guard (200 ... 299) ~= response.statusCode else {
// check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
// do whatever you want with the `data`, e.g.:
do {
let responseObject = data
print(responseObject)
} catch {
print(error)
// parsing error
if let responseString = String(data: data, encoding: .utf8) {
print("responseString = \(responseString)")
} else {
print("unable to parse response as string")
}
}
}
task.resume()
}
MyModel is basically a string
import Foundation
class AuthenticationResponse: ObservableObject {
#Published var jwtToken: String
init(jwtToken: String) {
self.jwtToken = jwtToken
}
}
I think 20 years of c sharp in not helping and am doing things it way and not the swift way if someone could advice be great.
Also in csharp we were told its not great in keeping alive the http client as can degrade performance is this the same for swift and if any library's you can recommend makes the code a bit neater the api has swagger docs enabled.
Edit 3
Example response expected back
{
"id": "b181104e-ba3e-4dba-b124-4bb4a3873b17",
"firstName": "user1",
"lastName": "lastname",
"username": "user1lastname#domainname.com",
"playerId": 0,
"jwtToken": "token in is here",//hidden for security
"error": {
"eventName": null,
"errorMessage": null,
"errorDate": null,
"statusCode": null,
"json": null
},
"refreshToken": null
}
I typically send this to the end point from C sharp
{
"username": "user1#domain.com",
"password": "pass1"
}
What I found I had to do was this
let decoder = JSONDecoder()
let responseObject = try
decoder.decode(AuthenticationResponse.self, from: data)
print(responseObject)
And change my class to be off this
import Foundation
struct AuthenticationResponse: Codable {
var jwtToken: String
}
After I done that I got the expected string back but my question is how does one get this to run correctly its completing before I think I need await but also where is it best to stored the jwttoken?

Equivalent of Request.serializeResponseJSON - Migration Alamofire 5

I have a problem with the following code:
func request(url: URL, completionHandler: #escaping (AFDataResponse<Any>) -> Void) {
let httpResponse = fakeResponse.response
let data = fakeResponse.data
let error = fakeResponse.error
let result = Request.serializeResponseJSON(options: .allowFragments, response: httpResponse, data: data, error: error)
guard let url = urlBuild(queryType: "q", from: "0", to: "10", uri: nil) else { return }
let urlRequest = URLRequest(url: url)
completionHandler(AFDataResponse(request: urlRequest, response: httpResponse, data: data, result: result))
}
I get the following error with Alamofire 5: Type 'Request' has no member 'serializeResponseJSON'.
I'm new to development with Swift and Alamofire, and I can't find a method equivalent to Request.serializeResponseJSON in Alamofire 5.
Thanks in advance for your help :)
All of the serialize methods from previous versions of Alamofire have been refactored into concrete types. For example, serializeResponseJSON has been replaced by the JSONResponseSerializer, which you can use with the response(responseSerializer:) method. However, for your usage, you shouldn't need to create an instance directly, as you can pass the same parameters to responseJSON. I suggest you use responseDecodable with Decodable types instead of responseJSON, as it will ensure your types are properly parsed.
I also suggest you read through our Usage, Advanced Usage, and API documentation and find some newer tutorials as you start using Alamofire.

Partially downloading data in Swift

I'm trying to develop a download accelerator in Swift. It should get the file's size and divide it to n parts. Then it should download them at once by running multiple threads, and then merge the parts.
I read C# - Creating a Download Accelerator, unfortunately it doesn't help me.
I can do the multiple thread part easily by
DispatchQueue.main.async {
// The new thread
}
but the other part is harder. I usually download a file like this:
try Data(contentsOf: URL(string: assetsUrl!)!)
or I can do the thing that is explained in this answer
class Downloader {
class func load(url: URL, to localUrl: URL, completion: #escaping () -> ()) {
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = try! URLRequest(url: url, method: .get)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Success: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: localUrl)
completion()
} catch (let writeError) {
print("error writing file \(localUrl) : \(writeError)")
}
} else {
print("Failure: %#", error?.localizedDescription);
}
}
task.resume()
}
}
But this is not C - it's very simplistic and doesn't accept many arguments. How can I make it get "first 200_000 bytes" from the server?
First of all, the server needs to implement HTTP range requests. If it doesn't, and you don't control the server, then you will not be able to do this.
If the server supports HTTP range requests, then you need to specify the range with request headers, as explained here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
The essentials are that you first send a HEAD request to figure out whether the server supports HTTP range requests. This is determined by whether the response includes the Accept-Ranges header, with a non-zero value.
If the server supports HTTP range requests, then you can make a request for the resource, with the Range header set for example to a value of bytes=0-1023 (depends which format the Accept-Ranges header specified, in this case bytes)

Thread 1 : signal SIGABRT alamofire

I'm very new to Swift 3, and i have to do a GET request on my API. I'm using Alamofire, which uses Asynchronous functions.
I do exactly the same on my Android App, and the GET returns JSON data
This is my code in swift :
func getValueJSON() -> JSON {
var res = JSON({})
let myGroup = DispatchGroup()
myGroup.enter()
Alamofire.request(url_).responseJSON { response in
res = response.result.value as! JSON
print("first result", res)
myGroup.leave()
}
myGroup.notify(queue: .main) {
print("Finished all requests.", res)
}
print("second result", res)
return res
}
But i have a problem with the line "res = response.result.value" wich gives me the error : Thread 1 : signal SIGABRT
I really don't understand where the problem comes from, it was pretty hard to do a "synchronous" function, maybe i'm doing it wrong.
My objective is to store the result of the request in a variable that i return. Anyone can help ?
I'd recommend you to use Alamofire together with SwiftyJSON because that way you'll be able to parse JSON easier a lot.
Here's a classical example:
Alamofire.request("http://example.net", method: .get).responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
If you need to pass parameters, or headers, just add it in the request method.
let headers: HTTPHeaders = [
"Content-Type:": "application/json"
]
let parameters: [String: Any] = [
"key": "value"
]
So your request will be something like this (this is POST request):
Alamofire.request("http://example.net", method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
switch response.result {
case .success(let value):
print(value)
case .failure(let error):
print(error)
}
}
I haven't tested it, but it should work. Also, you need to set allow arbitary load to yes (App Transport Security Settings in info.plist) if you want to allow requests over HTTP protocol.
This is NOT recommended, but it's fine for development.

How to install properly Alamofire SwiftyJSon and Alamofire-SwiftyJson

Has you can see from my previous questions, I got a lot of trouble parsing JSON Data. After few days of headache with that, I think the best way still to use alamofire/swiftyjson. I also found the alamofire-swiftyjson to let everything working well together.
But I am not sure how to install this three "libraries" together.
I download the whole Alamofire pack inside my project, I add the SwiftyJson.swift in my project and finally download the Alamofire-SwiftyJson in my project.
But when I change my alamofire request with "responseSwiftyJSON" I get an error saying " "Request" does not have a member name "responseSwiftyJSON
Alamofire.request(.GET, "http://mysiteweb.com/app/data/jsonpersodata.php", parameters: ["username": username]).responseSwiftyJSON { (request, response, data, error)
Add Alamofire and SwiftyJSON to the project. Then you can use Alamofire to request the data from the server and SwiftyJSON for serialization.
Alamofire 4
Alamofire.request(url).responseJSON { response in
guard let data = response.data else {
// No data returned
return
}
let json = JSON(data: data)
print(json)
}
Alamofire 3
Alamofire.request(.GET, url).validate().responseJSON { response in
switch response.result {
case .Success:
if let jsonData = response.result.value {
let json = JSON(jsonData)
print(json)
}
case .Failure(let error):
print(error)
}
}
Alamofire 2
Alamofire.request(.GET, "http://api.example.com", parameters: ["username" : username])
.responseJSON { request, response, data, error in
let swiftyJSONObject = JSON(data: data!)
}
Note that you have to unwrap data because server may return nothing.