I am writing an iOS App in Swift 4.2.
I am geting JSON array which I convert into Swift object of Class Sector
JSON:
"Sector": [
{
"REPSECTOR": "TELECOM - SERVICES",
"PERC_HOLD": 5.6,
"CO_NAME": "REVERSE REPO"
},
{
"REPSECTOR": "TELECOM - SERVICES",
"PERC_HOLD": 1.0,
"CO_NAME": "BHARTI AIRTEL"
},
{
"REPSECTOR": "FERROUS METALS",
"PERC_HOLD": 0.3,
"CO_NAME": "COAL INDIA"
}
]
Class:
class Sector{
var REPSECTOR:String=""
var PERC_HOLD:Double=0.0
var CO_NAME:String=""
}
I am making Array array1 of type [Sector] from above.
I need to make a new array from above array which must be clubed/combined values based on REPSECTOR variable, and total of PERC_HOLD.
Expected Result:
new array with 2 elements:
1: TELECOM - SERVICES, 5.6+1.0
2: FERROUS METALS, 0.3
Suppose the array you've decoded is called array :
var array: [Sector]()
Let's create a dictionary of sectors that have the same REPSECTOR property :
let dict = Dictionary(grouping: array) {
$0.REPSECTOR
}
Now let's create an new array with sectors that have the same name, and sum their PERC_HOLD :
var newArray = [Sector]()
for (key, value) in dict {
let sector = Sector()
sector.REPSECTOR = key
sector.PERC_HOLD = value.reduce(0, { $0 + $1.PERC_HOLD })
newArray.append(sector)
}
You could check the result this way :
for s in newArray {
print(s.CO_NAME, s.PERC_HOLD)
}
First, I think you should use proper casing in your Sector type, and use Codable. For your type it would be trivial:
struct Sector: Codable {
let repsector: String
let percHold: Double
let coName: String
enum CodingKeys: String, CodingKey {
case repsector = "REPSECTOR"
case percHold = "PERC_HOLD"
case coName = "CO_NAME"
}
}
Then, assuming we have JSON like this:
let sectorsJSON = """
[
{
"REPSECTOR": "TELECOM - SERVICES",
"PERC_HOLD": 5.6,
"CO_NAME": "REVERSE REPO"
},
{
"REPSECTOR": "TELECOM - SERVICES",
"PERC_HOLD": 1.0,
"CO_NAME": "BHARTI AIRTEL"
},
{
"REPSECTOR": "FERROUS METALS",
"PERC_HOLD": 0.3,
"CO_NAME": "COAL INDIA"
}
]
""".data(using: .utf8)!
Then we can decode your JSON like this:
let decoder = JSONDecoder()
let rawSectors = try decoder.decode([Sector].self, from: sectorsJSON)
And then we can group it by .repsector and sum up percentages:
let groupedSectors = Dictionary(grouping: rawSectors, by: { $0.repsector })
.map { (arg) -> (String, Double) in
let (key, value) = arg
let percents = value.reduce(into: 0, { (acc, sector) in
acc += sector.percHold
})
return (key, percents)
}
print(groupedSectors) // [("FERROUS METALS", 0.3), ("TELECOM - SERVICES", 6.6)]
let dicSector = ["Sector": [
[
"REPSECTOR": "TELECOM - SERVICES",
"PERC_HOLD": 5.6,
"CO_NAME": "REVERSE REPO"
],
[
"REPSECTOR": "TELECOM - SERVICES",
"PERC_HOLD": 1.0,
"CO_NAME": "BHARTI AIRTEL"
],
[
"REPSECTOR": "FERROUS METALS",
"PERC_HOLD": 0.3,
"CO_NAME": "COAL INDIA"
]
]]
Codable Sector
class Sector: Codable {
var REPSECTOR:String=""
var PERC_HOLD:Double=0.0
var CO_NAME:String=""
}
Convert Dictonary to object array
var arraySector = [Sector]()
if let array = dicSector["Sector"] {
do {
let data = try JSONSerialization.data(withJSONObject: array, options: JSONSerialization.WritingOptions.prettyPrinted)
let jsonDecoder = JSONDecoder()
arraySector = try jsonDecoder.decode([Sector].self, from: data)
}
catch {
print(error.localizedDescription)
}
}
Array To Grouping
let predicate = { (element: Sector) in
return element.REPSECTOR
}
let dic = Dictionary(grouping: arraySector, by: predicate)
print(dic)
Output
["TELECOM - SERVICES": [Sector, Sector], "FERROUS
METALS": [Sector]]
This is not the best solution but working
var newArray = [Sector]()
oldArray.forEach() { element in
if newArray.contains(where: { $0.REPSECTOR == element.REPSECTOR }) {
newArray.forEach() { newElement in
if newElement.REPSECTOR == element.REPSECTOR {
newElement.CO_NAME + element.CO_NAME
} else {
newArray.append(element)
}
}
} else {
newArray.append(element)
}
}
Related
I need your help to implement a custom JSON decoding. The JSON returned by the API is:
{
"zones": [
{
"name": "zoneA",
"blocks": [
// an array of objects of type ElementA
]
},
{
"name": "zoneB",
"blocks": [
// an array of objects of type ElementB
]
},
{
"name": "zoneC",
"blocks": [
// an array of objects of type ElementC
]
},
{
"name": "zoneD",
"blocks": [
// an array of objects of type ElementD
]
}
]
}
I don't want to parse this JSON as an array of zones with no meaning. I'd like to produce a model with an array for every specific type of block, like this:
struct Root {
let elementsA: [ElementA]
let elementsB: [ElementB]
let elementsC: [ElementC]
let elementsD: [ElementD]
}
How can I implement the Decodable protocol (by using init(from decoder:)) to follow this logic? Thank you.
This is a solution with nested containers. With the given (simplified but valid) JSON string
let jsonString = """
{
"zones": [
{
"name": "zoneA",
"blocks": [
{"name": "Foo"}
]
},
{
"name": "zoneB",
"blocks": [
{"street":"Broadway", "city":"New York"}
]
},
{
"name": "zoneC",
"blocks": [
{"email": "foo#bar.com"}
]
},
{
"name": "zoneD",
"blocks": [
{"phone": "555-01234"}
]
}
]
}
"""
and the corresponding element structs
struct ElementA : Decodable { let name: String }
struct ElementB : Decodable { let street, city: String }
struct ElementC : Decodable { let email: String }
struct ElementD : Decodable { let phone: String }
first decode the zones as nestedUnkeyedContainer then iterate the array and decode first the name key and depending on name the elements.
Side note: This way requires to declare the element arrays as variables.
struct Root : Decodable {
var elementsA = [ElementA]()
var elementsB = [ElementB]()
var elementsC = [ElementC]()
var elementsD = [ElementD]()
enum Zone: String, Decodable { case zoneA, zoneB, zoneC, zoneD }
private enum CodingKeys: String, CodingKey { case zones }
private enum ZoneCodingKeys: String, CodingKey { case name, blocks }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var zonesContainer = try container.nestedUnkeyedContainer(forKey: .zones)
while !zonesContainer.isAtEnd {
let item = try zonesContainer.nestedContainer(keyedBy: ZoneCodingKeys.self)
let zone = try item.decode(Zone.self, forKey: .name)
switch zone {
case .zoneA: elementsA = try item.decode([ElementA].self, forKey: .blocks)
case .zoneB: elementsB = try item.decode([ElementB].self, forKey: .blocks)
case .zoneC: elementsC = try item.decode([ElementC].self, forKey: .blocks)
case .zoneD: elementsD = try item.decode([ElementD].self, forKey: .blocks)
}
}
}
}
Decoding the stuff is straightforward
do {
let result = try JSONDecoder().decode(Root.self, from: Data(jsonString.utf8))
print(result)
} catch {
print(error)
}
the "zone" property is an array of Zone objects. so you can decode them like:
enum Zone: Decodable {
case a([ElementA])
case b([ElementB])
case c([ElementC])
case d([ElementD])
enum Name: String, Codable {
case a = "zoneA"
case b = "zoneB"
case c = "zoneC"
case d = "zoneD"
}
enum RootKey: CodingKey {
case name
case blocks
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKey.self)
let zoneName = try container.decode(Name.self, forKey: .name)
switch zoneName {
case .a: try self = .a(container.decode([ElementA].self, forKey: .blocks))
case .b: try self = .b(container.decode([ElementB].self, forKey: .blocks))
case .c: try self = .c(container.decode([ElementC].self, forKey: .blocks))
case .d: try self = .d(container.decode([ElementD].self, forKey: .blocks))
}
}
}
Then you can filter out anything you like. For example you can pass in the array and get the result you asked in your question:
struct Root {
init(zones: [Zone]) {
elementsA = zones.reduce([]) {
guard case let .a(elements) = $1 else { return $0 }
return $0 + elements
}
elementsB = zones.reduce([]) {
guard case let .b(elements) = $1 else { return $0 }
return $0 + elements
}
elementsC = zones.reduce([]) {
guard case let .c(elements) = $1 else { return $0 }
return $0 + elements
}
elementsD = zones.reduce([]) {
guard case let .d(elements) = $1 else { return $0 }
return $0 + elements
}
}
let elementsA: [ElementA]
let elementsB: [ElementB]
let elementsC: [ElementC]
let elementsD: [ElementD]
}
✅ Benefits:
Retain the original structure (array of zones)
Handle repeating zones (if server sends more than just one for each zone)
I have defined the data model for packages data, but need to define UITableview sections on the basis of
subscriptiontype = 'Yearly', 'Monthly', 'Weekly'
Getting an error of - Cannot assign value of type '[Package]' to type '[[String : String]]?'. How can I assign it to tableview sections.
Code:
var packag = [Package]()
enum TableSection: Int {
case subscriptionType = 0, yearly, monthly, weekly, total
}
var data = [TableSection: [[String: String]]]()
func sortData() {
data[.yearly] = packag.filter({ $0.subscriptionType == "yearly" })
data[.monthly] = packag.filter({ $0.subscriptionType == "monthly" })
data[.weekly] = packag.filter({ $0.subscriptionType == "weekly" })
}
Updated Code - viewdidload():
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
//fetchData()
if let path = Bundle.main.path(forResource: "packageList", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom{ decoder -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)
return Date(timeIntervalSince1970: TimeInterval(dateStr)!)
}
let jSON = try? decoder.decode(Root.self, from: data)
if let packages = jSON?.packages {
self.sortData(packages: packages)
print(packages)
}
} catch {
// handle error
print(Error.self)
}
}
}
Root Model:
struct Root : Codable {
let packages : [Package]
}
Packages Model:
struct Package : Codable {
let availableUntil : Date
let benefits : [String]?
let desc : String
let didUseBefore : Bool
let name : String
let price : Double
let subscriptionType : String
let tariff : Tariff
}
Traiff Model:
struct Tariff : Codable {
let data : String
let sms : String
let talk : String
}
Updated PackageJson Data:
{ "packages": [
{
"name": "Platinum Maksi 6 GB",
"desc": "Zengin içerikli Platinum Maksi Paketi ile Turkcell Uygulamalarının keyfini sürün!",
"subscriptionType": "monthly",
"didUseBefore": true,
"benefits": [
"TV+",
"Fizy",
"BiP",
"lifebox",
"Platinum",
"Dergilik"
],
"price": 109.90,
"tariff": {
"data": "6144",
"talk": "2000",
"sms": "100"
},
"availableUntil": "1558131150"
},
{
"name": "Platinum Maksi 8 GB",
"desc": "Zengin içerikli Platinum Maksi Paketi ile Turkcell Uygulamalarının keyfini sürün!",
"subscriptionType": "monthly",
"didUseBefore": false,
"benefits": [
"TV+",
"Fizy",
"BiP",
"lifebox",
"Platinum",
"Dergilik"
],
"price": 129.90,
"tariff": {
"data": "8192",
"talk": "2000",
"sms": "100"
},
"availableUntil": "1555060350"
},
{
"name": "Platinum Maksi 12 GB",
"desc": "Zengin içerikli Platinum Maksi Paketi ile Turkcell Uygulamalarının keyfini sürün!",
"subscriptionType": "yearly",
"didUseBefore": false,
"benefits": [
"TV+",
"Fizy",
"BiP",
"lifebox",
"Platinum",
"Dergilik"
],
"price": 109.90,
"tariff": {
"data": "12288",
"talk": "2000",
"sms": "100"
},
"availableUntil": "1555060350"
},
The problem is that you are trying to assign a Package object to a value that is expecting an array of dictionary.
Your data variable is a dictionary that has a TableSection as a key, and an array of dictionaries as the value which you have defined by writing [[String: String]]. Then in your sortData function you're trying to assign a value to various data keys, but you're assigning a Package item to it when it's expecting an array of dictionaries.
What will work is if you change your data definition to
var data = [TableSection: Package]()
Model
import Foundation
struct JSON: Codable {
let packages: [Package]
}
struct Package: Codable {
let name, desc, subscriptionType: String
let didUseBefore: Bool
let benefits: [String]
let price: Double
let tariff: Tariff
let availableUntil: String
}
struct Tariff: Codable {
let data, talk, sms: String
}
Enum
enum TableSection: Int {
case subscriptionType = 0, yearly, monthly, weekly, total
}
Data
var data = [TableSection: [Package]]()
Parsing
if let path = Bundle.main.path(forResource: "document", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jSON = try? JSONDecoder().decode(JSON.self, from: data)
if let packages = jSON?.packages {
self.sortData(packages: packages)
}
} catch {
// handle error
}
}
Sorting
private func sortData(packages : [Package]) {
data[.yearly] = packages.filter({ $0.subscriptionType == "yearly" })
data[.monthly] = packages.filter({ $0.subscriptionType == "monthly" })
data[.weekly] = packages.filter({ $0.subscriptionType == "weekly" })
print("Data is \(data)")
}
I use LOCAL JSON, you can use server data.
I'm using the tableview to display the Two Json value but the problem is I cant add value into model struct to displaying into tableview using two Api's. i want to show percentage value in one of the cell label and
here is my json
[
{
"Percentage": 99.792098999,
}
]
my second json value
{
"Categories": [
"Developer",
"ios "
],
"Tags": [
{
"Value": "kishore",
"Key": "Name"
},
{
"Value": "2",
"Key": "office"
},
]
}
and i need show the Categories value in Categories label in tableview
value and key on tableview
here is my Struct
struct info: Decodable {
let Categories: String?
let Tags: String?
let Value: String?
let Key: String?
var Name: String?
let percentage: Double?
}
here its my code
var List = [info]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
print(json as Any)
guard let jsonArray = json as? [[String: Any]] else {
return
}
print(jsonArray)
for dic in jsonArray{
guard let per = dic["percentage"] as? Double else { return }
print(per)
}
and second json
if let array = json["Tags"] as? [[String: String]] {
for dict in array {
let key = dict["Key"]
let value = dict["Value"]
switch key {
case "office":
case "Name":
default:
break;
}
}
here is my cell for row indexpath
cell.Categories.text = list[indexpath.row].percentage
cell.Name.text = list[indexpath.row].name
cell.office.text = list[indexpath.row].office
Please use Swift 4 Codable protocol to decode the value from JSON.
//1.0 Create your structures and make it conform to Codable Protocol
struct Tags: Codable{
var Key: String
var Value: String
}
struct Sample: Codable{
var Categories: [String]
var Tags: [Tags]
}
In your method, perform below steps:
//2.0 Get your json data from your API. In example below, i am reading from a JSON file named "Sample.json"
if let path = Bundle.main.path(forResource: "Sample", ofType: "json") {
do {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
do {
//3.0 use JSONDecoder's decode method to decode JSON to your model.
let sample = try JSONDecoder().decode(Sample.self, from: jsonData)
//4.0 User the "sample" model to do your stuff. Example, printing some values
print("Sample.Category = \(sample.Categories)")
print("Sample.Name = \(sample.Tags[0].Value)")
print("Sample.Office = \(sample.Tags[1].Value)")
} catch let error {
print("Error = \(error)")
}
} catch {
// handle error
}
}
I prefer to use Codable all the time with JSON even for simpler types so for percentage I would do
struct ItemElement: Decodable {
let percentage: Double
enum CodingKeys: String, CodingKey {
case percentage = "Percentage"
}
}
and we need to keep these values in a separate array, declared as a class property
let percentageList: [Double]()
and json encoding would then be
let decoder = JSONDecoder()
do {
let result = try decoder.decode([ItemElement].self, from: data)
percentageList = result.map { item.percentage }
} catch {
print(error)
}
Similar for the second part
struct Item: Decodable {
let categories: [String]
let tags: [Tag]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
case tags = "Tags"
}
}
struct Tag: Decodable {
let value, key: String
enum CodingKeys: String, CodingKey {
case value = "Value"
case key = "Key"
}
}
use a dictionary for the result, again as a class property
var values = [String: String]()
and the decoding
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Item.self, from: data)
for item in result.tags {
values[item.key] = values.item.value
}
} catch {
print(error)
}
and then in the cell for row code
cell.Categories.text = percentageList[indexpath.row].percentage
cell.Name.text = values["name"]
cell.office.text = values["office"]
Note that this last code looks very strange since you don't have an array of name/office values judging by your json. Maybe you have simplified it some way but the code above is the best I can do with the information given even if it possibly wrong
I am currently using Openweathermap.org to get weather forecast information. And here is the JSON objects I got from their API:
{
"city":{ },
"cod":"200",
"message":0.0029,
"cnt":40,
"list":[
{
"dt":1466532000,
"main":{
"temp":296.52,
"temp_min":294.864,
"temp_max":296.52,
"pressure":1004.95,
"sea_level":1023.45,
"grnd_level":1004.95,
"humidity":58,
"temp_kf":1.65
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04d"
}
],
"clouds":{ },
"wind":{ },
"sys":{ },
"dt_txt":"2016-06-21 18:00:00"
},
{
"dt":1466542800,
"main":{ },
"weather":[ ],
"clouds":{ },
"wind":{ },
"sys":{ },
"dt_txt":"2016-06-21 21:00:00"
},
{
"dt":1466553600,
"main":{ },
"weather":[ ],
"clouds":{ },
"wind":{ },
"sys":{ },
"dt_txt":"2016-06-22 00:00:00"
}]
}
As you can see from this sample, under list there are many objects and what I want is just the temp and the weather main and description. I created a Struct to sort and hold all of the JSON objects but it keeps giving me errors. How do I sort it in terms of the "dt" and how would I extract the data from the JSON. Thanks.
Here is my Struct:
import Foundation
struct FutureWeather {
//future stuff
var futureDt: NSDate //
var futureMainWeather: String//
var futureDescription: String//
private var futureTemp: Double//
var futureTempCelsius: Double {
get {
return futureTemp - 273.15
}
}
var futureTempFahrenheit: Double {
get {
return (futureTemp - 273.15) * 1.8 + 32
}
}
init(futureWeatherData : [String:AnyObject]) {
//first set of data
let futureWeatherDictUno = futureWeatherData["list"]![0] as! [String: AnyObject]
print(futureWeatherDictUno)
let events = futureWeatherDictUno.sort({$0["dt"] < $1["dt"]})
futureDt = NSDate(timeIntervalSince1970: futureWeatherDictUno["dt"] as! NSTimeInterval)
let mainDictOne = futureWeatherDictUno["main"] as! [String: AnyObject]
futureTemp = mainDictOne["temp"] as! Double
let weatherDictOne = futureWeatherDictUno["weather"]![0] as! [String: AnyObject]
futureMainWeather = weatherDictOne["main"] as! String
futureDescription = weatherDictOne["description"] as! String
//the second set of data
let futureWeatherDictDos = futureWeatherData["list"]![1] as! [String: AnyObject]
futureDt = NSDate(timeIntervalSince1970: futureWeatherDictUno["dt"] as! NSTimeInterval)
let mainDictTwo = futureWeatherDictDos["main"] as! [String: AnyObject]
futureTemp = mainDictTwo["temp"] as! Double
let weatherDictTwo = futureWeatherDictDos["weather"]![0] as! [String: AnyObject]
futureMainWeather = weatherDictTwo["main"] as! String
futureDescription = weatherDictTwo["description"] as! String
}
}
When you go through the list array each element should already be sorted in the JSON file based on the elements dt. Also each element (futureWeatherData["list"]![0]) in the array will only have one dt key/value so you can't sort that.
What you should do is (and this is just pseudo code) is the following
let futureWeather = [Weather]()
for element : JSON in array {
let weather = Weather(json: element) //create weather element from json
futureWeather.append(weather)
}
What you don't want to do is go through each element in the list manually in your init method in the your FutureWeather class.
I would also recommend looking into using https://github.com/SwiftyJSON/SwiftyJSON and creating keys in an enum to go through the json.
As #Asdrubal pointed out in their answer SwiftyJSON is a useful library. If you want a pure-Foundation solution, you can use NSJSONSerialization to get a response from your API data. I'm posting this answer here in advance of a future, personal server-side Swift project to retrieve weather data :)
import Foundation
enum WeatherError: Int, ErrorType {
/// The API response text could not be converted to an
/// NSData object using NSUnicodeStringEncoding
case UnexpectedAPITextEncoding = 1
/// The API reponse object did not contain an array of
/// Dictionary<String, AnyObject> objects at the "list"
/// key
case UnexpectedAPIResponseFormat = 2
}
/// An abstraction of a response to the OpenWeatherMap.org forecast API.
/// Contains only date, description and temperature data, plus convenience
/// properties to display temp in F or C.
///
/// NOTE: Each of the properties on the value are Optional, reflecting the
/// fact that the API response is sparsely populated for many of its values.
struct OpenWeatherMapResponse {
/// The date of the forecast. Could be in the future
let futureDt: NSDate?
/// Textual description of the weather conditions for the forecast time
let futureDescription: String?
/// Temp provided in K
private let futureTemp: Double?
/// Temperature for the forecast time, in degrees C
var futureTempCelsius: Double? {
get {
guard let futureTemp = futureTemp else {
return nil
}
return futureTemp - 273.15
}
}
/// Temperature for the forecast time, in degrees F
var futureTempFahrenheit: Double? {
get {
guard let futureTemp = futureTemp else {
return nil
}
return (futureTemp - 273.15) * 1.8 + 32
}
}
/// Given a member of `list` from the API response,
/// creates an OpenWeatherMapResponse
init(jsonObject: AnyObject) {
if let timestamp = jsonObject["dt"] as? NSTimeInterval {
futureDt = NSDate(timeIntervalSince1970: timestamp)
} else {
futureDt = nil
}
if let mainBlock = jsonObject["main"] as? [String: AnyObject],
temp = mainBlock["temp"] as? Double {
futureTemp = temp
} else {
futureTemp = nil
}
if let weatherList = jsonObject["weather"] as? [AnyObject],
description = weatherList.first?["description"] as? String {
futureDescription = description
} else {
futureDescription = nil
}
}
/// Given a JSON Object converted from an API response, parse the object
/// into a collection of FutureWeather values.
/// - throws: WeatherError if the API response text is in an unexpected character encoding or in
/// an unexpected format
/// - returns: an array of OpenWeatherMapResponse values
static func responsesByConvertingAPIResponseString(apiResponseString: String) throws -> [OpenWeatherMapResponse] {
// NSJSONSerialization only works on NSData. Convert it before using
guard let apiData = apiResponseString.dataUsingEncoding(NSUnicodeStringEncoding) else {
throw WeatherError.UnexpectedAPITextEncoding
}
// Convert API response data to a Foundation AnyObject
let jsonObject = try NSJSONSerialization.JSONObjectWithData(apiData, options: [])
var weatherList = [OpenWeatherMapResponse]()
guard let events = jsonObject["list"] as? [[String: AnyObject]] else {
throw WeatherError.UnexpectedAPIResponseFormat
}
for event in events {
weatherList.append(OpenWeatherMapResponse(jsonObject: event))
}
return weatherList
}
}
// API response
let jsonString = "{ \"city\":{ }, \"cod\":\"200\", \"message\":0.0029, \"cnt\":40, \"list\":[ { \"dt\":1466532000, \"main\":{ \"temp\":296.52, \"temp_min\":294.864, \"temp_max\":296.52, \"pressure\":1004.95, \"sea_level\":1023.45, \"grnd_level\":1004.95, \"humidity\":58, \"temp_kf\":1.65 }, \"weather\":[ { \"id\":803, \"main\":\"Clouds\", \"description\":\"broken clouds\", \"icon\":\"04d\" } ], \"clouds\":{ }, \"wind\":{ }, \"sys\":{ }, \"dt_txt\":\"2016-06-21 18:00:00\" }, { \"dt\":1466542800, \"main\":{ }, \"weather\":[ ], \"clouds\":{ }, \"wind\":{ }, \"sys\":{ }, \"dt_txt\":\"2016-06-21 21:00:00\" }, { \"dt\":1466553600, \"main\":{ }, \"weather\":[ ], \"clouds\":{ }, \"wind\":{ }, \"sys\":{ }, \"dt_txt\":\"2016-06-22 00:00:00\" }] }"
let weatherEvents = try OpenWeatherMapResponse.responsesByConvertingAPIResponseString(jsonString)
let sortedWeatherEvents = weatherEvents.sort() { a, b in a.futureDt?.timeIntervalSince1970 < b.futureDt?.timeIntervalSince1970 }
// prints:
// event: OpenWeatherMapResponse(futureDt: Optional(2016-06-21 18:00:00 +0000), futureDescription: Optional("broken clouds"), futureTemp: Optional(296.51999999999998))
// event: OpenWeatherMapResponse(futureDt: Optional(2016-06-21 21:00:00 +0000), futureDescription: nil, futureTemp: nil)
// event: OpenWeatherMapResponse(futureDt: Optional(2016-06-22 00:00:00 +0000), futureDescription: nil, futureTemp: nil)
for event in sortedWeatherEvents {
print("event: \(event)")
}
I want to hit an api using Alamofire in which I have to pass parameters as :
"reminder":
[
{
"isAlarmOn": "true",
"time": "06:29 PM"
},
{
"isAlarmOn": "true",
"time": "06:30 AM"
}
]
For achieving this, I Created an Array as
var reminderArray : [AlarmRequestModel] = [AlarmRequestModel]()
where AlarmRequestModel is Like ,
AlarmRequestModel.swift
import Foundation
class AlarmRequestModel {
var time : String = "12:00 PM"
init(time : String){
if !time.isEmpty{
self.time = time
}
}
}
and filled reminderArray as
func setArray() {
reminderArray.insert(AlarmRequestModel(time: "5:00 PM"), atIndex: 0)
reminderArray.insert(AlarmRequestModel(time: "6:00 PM"), atIndex: 1)
reminderArray.insert(AlarmRequestModel(time: "7:00 PM"), atIndex: 2)
reminderArray.insert(AlarmRequestModel(time: "8:00 PM"), atIndex: 3)
}
And while hitting the Api , I Declared the parameters as
func hitApi()
{
let parameters : [String:AnyObject] = [
"reminder" : reminderArray.description,
]
print("parmeters : \(parameters)")
}
But I didn't get the expected output ,
the output was like ->
parmeters :["reminder": [My_Swaasth.AlarmRequestModel,My_Swaasth.AlarmRequestModel,My_Swaasth.AlarmRequestModel,My_Swaasth.AlarmRequestModel]]
Please anyone Suggest , What changes Do I Need to do in order to achieve the desired output.
There is a protocoll called CustomStringConvertible which has the
var description: String property
But that won't result the desired output. So I'd rather implement a parameter value for AlarmRequestModel
extension AlarmRequestModel {
var parameterValue: Dictionary<String, String> {
return ["isAlarmOn": "true", "time": self.time]
}
}
And use this like
reminderArray.map() { reminder in return reminder.parameterValue }
here:
let parameters : [String:AnyObject] = [
"reminder" : reminderArray.map() { reminder in return reminder.parameterValue },
]
func hitApi()
{
var JSONarray: [Dictionary<String,String>] = []
for (index,_) in reminderArray.enumerate()
{
JSONarray.append(["isAlarmOn":"true","time": reminderArray[index].time])
}
let bytes = try! NSJSONSerialization.dataWithJSONObject(JSONarray, options: NSJSONWritingOptions.PrettyPrinted)
var jsonObj = try! NSJSONSerialization.JSONObjectWithData(bytes, options: .MutableLeaves) as! [Dictionary<String, String>]
print("\"reminder\":\(jsonObj)")
}
You also want to change your Alarm Request Model to this:
class AlarmRequestModel {
var time : String!
init(time : String){
if !time.isEmpty{
self.time = time
}
}
}