var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)
Preparing for the response
Declare an array as below
var data: NSMutableData = NSMutableData()
Receiving the response
1.
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
// Received a new request, clear out the data object
self.data = NSMutableData()
}
2.
func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
// Append the received chunk of data to our data object
self.data.appendData(data)
}
3.
func connectionDidFinishLoading(connection: NSURLConnection!) {
// Request complete, self.data should now hold the resulting info
// Convert the retrieved data in to an object through JSON deserialization
var err: NSError
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
if jsonResult.count>0 && jsonResult["results"].count>0 {
var results: NSArray = jsonResult["results"] as NSArray
self.tableData = results
self.appsTableView.reloadData()
}
}
When NSURLConnection receives a response, we can expect the didReceiveResponse method to be called on our behalf. At this point we simply reset our data by saying self.data = NSMutableData(), creating a new empty data object.
After a connection is made, we will start receiving data in the method didReceiveData. The data argument being passed in here is where all our juicy information comes from. We need to hold on to each chunk that comes in, so we append it to the self.data object we cleared out earlier.
Finally, when the connection is done and all data has been received, connectionDidFinishLoading is called and we’re ready to use the data in our app. Hooray!
The connectionDidFinishLoading method here uses the NSJSONSerialization class to convert our raw data in to useful Dictionary objects by deserializing the results from your Url.
This answer was last revised for Swift 5.3 and iOS 14.4 SDK.
Given some already obtained JSON data, you can use JSONDecoder to decode it into your Decodable model (or a collection of models).
let data: Data = /* obtain your JSON data */
let model = try JSONDecoder().decode(Model.self, from: data)
Such model must conform to the Decodable protocol and contain correct mapping between properties and JSON dictionary keys. As an example, consider the following JSON array containing search results of cities beginning with "Wa".
For that, you need to create a model that contains the correct properties of correct types. If you're using a web API, its documentation will be of great help here.
struct SearchResult: Decodable {
let id: Int
let city: String
let region: String
let country: String
}
Then decode the data with JSONDecoder:
let results = try JSONDecoder().decode([SearchResult].self, from: data)
Given a new array of decoded search results, call one of UITableView's functions to reload its data. Note that the decode function can throw an error which you must somehow handle.
To learn more about decoding custom types in Swift and more advanced usage of the Codable APIs, I recommend checking out this documentation article.
I also wrote a small library which is specialized for the mapping of the json response into an object structure. I am internally using the library json-swift from David Owens. Maybe it is useful for someone else.
class EmployeeContainer : ROJSONObject {
required init() {
super.init();
}
required init(jsonData:AnyObject) {
super.init(jsonData: jsonData)
}
lazy var employees:[Employee] = {
return Value<[Employee]>.getArray(self, key: "employees") as [Employee]
}()
}
Then to actually map the objects from the JSON response you only have to pass the data into the EmployeeContainer class as param in the constructor. It does automatically create your data model.
var baseWebservice:BaseWebservice = BaseWebservice();
var urlToJSON = "http://prine.ch/employees.json"
var callbackJSON = {(status:Int, employeeContainer:EmployeeContainer) -> () in
for employee in employeeContainer.employees {
println("Firstname: \(employee.firstname) Lastname: \(employee.lastname) age: \(employee.age)")
}
}
baseWebservice.get(urlToJSON, callback:callbackJSON)
The console output looks then like the following:
Firstname: John Lastname: Doe age: 26
Firstname: Anna Lastname: Smith age: 30
Firstname: Peter Lastname: Jones age: 45
You supply a sample JSON object with a class name and the tool will generate a corresponding Swift class, as well as any needed subsidiary Swift classes, to represent the structure implied by the sample JSON. Also included are class methods used to populate Swift objects, including one that utilizes the NSJSONSerialization.JSONObjectWithData method. The necessary mappings from the NSArray and NSDictionary objects are provided.
From the generated code, you only need to supply an NSData object containing JSON that matches the sample provided to the tool.
import UIKit
let jsonString = "{\"name\": \"John Doe\", \"phone\":123456}"
let data = jsonString.data(using: .utf8)
var jsonObject: Any
do {
jsonObject = try JSONSerialization.jsonObject(with: data!) as Any
if let obj = jsonObject as? NSDictionary {
print(obj["name"])
}
} catch {
print("error")
}
In Swift 4+ is strongly recommended to use Codable instead of JSONSerialization.
This Codable includes two protocols: Decodable and Encodable. This Decodable protocol allows you to decode Data in JSON format to custom struct/class conforming to this protocol.
For example imagine situation that we have this simple Data (array of two objects)
let data = Data("""
[
{"name":"Steve","age":56},
{"name":"iPhone","age":11}
]
""".utf8)
then have following struct and implement protocol Decodable
struct Person: Decodable {
let name: String
let age: Int
}
now you can decode your Data to your array of Person using JSONDecoder where first parameter is type conforming to Decodable and to this type should Data be decoded
do {
let people = try JSONDecoder().decode([Person].self, from: data)
} catch { print(error) }
... note that decoding has to be marked with try keyword since you could for example make some mistake with naming and then your model can't be decoded correctly ... so you should put it inside do-try-catch block
Cases that key in json is different from name of property:
If key is in named using snake_case, you can set decoder's keyDecodingStrategy to convertFromSnakeCase which changes key from property_name to camelCase propertyName
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let people = try decoder.decode([Person].self, from: data)
If you need unique name you can use coding keys inside struct/class where you declare name of key
let data = Data("""
{ "userName":"Codable", "age": 1 }
""".utf8)
struct Person: Decodable {
let name: String
let age: Int
enum CodingKeys: String, CodingKey {
case name = "userName"
case age
}
}
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
]
Model class
struct PostsModel : Decodable {
let userId : Int?
let id : Int?
let title : String?
let body : String?
}
Fetch Response
let url = URL(string: K.GET_POSTS)!
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let safeData = data,
let response = response as? HTTPURLResponse,
error == nil else { // check for fundamental networking error
print("error", error ?? "Unknown error")
delegate?.onError(error!)
return
}
guard (200 ... 299) ~= response.statusCode else { // check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
let responseString = String(data: safeData, encoding: .utf8)
do {
let decoder = JSONDecoder()
let loginResponseModel = try decoder.decode([PostsModel].self, from: data!)
}
catch {
print(error)
}
print("responseString = \(responseString)")
}
task.resume()