json missing fields swift - arrays

So I have a function that pulls data from a json file and parses it. Its throwing me a nil error because some of the json entries don't have the "colors" field/array. How would i account for this and put in "ERROR" as the text for the ones that didn't.
func getData2(){
let dataPath = NSBundle.mainBundle().pathForResource("cardata", ofType: "json")
if let JSONData = NSData(contentsOfFile: dataPath!)
{
do
{
if let dictionariesArray = try NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions()) as?
[[String: AnyObject]]
{
for dictionary in dictionariesArray
{
let name = dictionary["name"] as! String
let type = dictionary["type"] as! String
let text = String(dictionary["text"])
if let printingsArray = dictionary["colors"] as? [String]
{
let printingsString = String(printingsArray.joinWithSeparator("-"))
nameColor[name] = printingsString
}
nameType[name] = type
nameText[name] = text
}
}
}
catch
{
print("Could not parse file at")
}
}
struct Card {
let name: String
let type: String
let colorr: String
let textt: String
init(name: String, type: String, textt: String, colorr:String) {
self.name = name
self.type = type
self.textt = textt
self.colorr = colorr
}
}
var goodCard = [Card]()
for (cardName, cardType) in nameType {
let cardText = nameText[cardName]!
let cardColor = nameColor[cardName]! //fatal error: unexpectedly found nil while unwrapping an Optional value
goodCard.append(Card(name: cardName, type: cardType, textt: cardText, colorr: cardColor))
}
if typee != "" {
let redDogs = goodCard.filter {$0.type == typee}
print(redDogs)
}

You should avoid using the forced unwrapping operator ! whenever possible. If you use it and the Optional is nil then when you use the variable it will throw an exception. Instead use optional binding to unwrap the value:
// optional binding
if let cardColor = nameColor[cardName] {
// do something with the value
}

Related

How can I merge 2 dictionaries into one array?

My JSON data look like this image below. Now I wanna merge the value of Shop Type and Promotion into one to use as collection view data. How can I do that?
I just filter the response data from the server like this:
var dataBanDau: [SDFilterModel] = []
var quickData: [SDFilterModel] = []
let filters: [SDFilterModel] = data
self.filterEntries = filters
//let nsarray = NSArray(array: self.filterEntries! , copyItems: true)
// self.filterEntriesStoreConstant = nsarray as! Array
self.dataBanDau = filters
for i in 0..<self.dataBanDau.count {
if self.dataBanDau[i].search_key.count == 0 {
self.quickData.append(self.dataBanDau[i])
}
}
self.quickData = self.quickData.filter {
$0.type != "range"
}
DispatchQueue.main.async {
//Note: Reload TableView
self.quickFilterCollection.reloadData()
completed(true)
}
}
the class SDFilterModel:
class SDFilterModel: DSBaseModel {
var name = String()
var type = String()
var is_expanded = Int()
var search_key = String()
var filterEntries : [SDFilterModel]?
override func copy(with zone: NSZone? = nil) -> Any {
// This is the reason why `init(_ model: GameModel)`
// must be required, because `GameModel` is not `final`.
let copy = SDFilterModel(dict: self.dictionary)
if let arrAttribute = NSArray(array: self.value , copyItems: true) as? [AttributeValueModel] {
copy.value = arrAttribute
}
return copy
}
override init(dict: Dictionary<String, Any>) {
super.init(dict: dict);
value = self.valueParse()
name = dict.getString(forKey: "name")
type = dict.getString(forKey: "type")
search_key = dict.getString(forKey: "search_key")
is_expanded = dict.getInt(forKey: "is_expanded")!
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
var value: [AttributeValueModel] = [];
func valueParse()-> [AttributeValueModel] {
guard let childs = (self.dictionary["value"]) as? [Dictionary<String, AnyObject>]
else { return [] }
var output: [AttributeValueModel] = [];
for aDict in childs {
let item = AttributeValueModel(dict:aDict);
// if type == .Range && item.option_id == "0" {
// item.setRangeOptionID(aValue: item.option_name!)
// }
//
output.append(item);
}
return output;
}
Let be Assume you have let myArray = [1,2,3,4,5,6,7,8]
Now you wanted to square of each and every element in the array,
With for loop you do like this
for item in myArray {
print(item * item)
}
Now assume item = $0
With for map you jus do
myArray.map({ $0 * $0 })
Both will gave same output.
map : Use to do same operation on every element of array.
flatmap : It is used to flattern the array of array.
let myArr = [[1,2,3],[4,5,6,7]]
and you want o/p as [1,2,3,4,5,6,7]
So can get above output with myArr.flatMap({$0})
Now back to your question.
let reqArray = myModel.data.map({ $0.value }).flatMap({ $0 })
First, map gaves you array-of-array of key value but you need a single array, so for that you need to use flatmap.
You can take ref : https://medium.com/#Dougly/higher-order-functions-in-swift-sorted-map-filter-reduce-dff60b5b6adf
Create the models like this
struct Option {
let name: String
let searchKey: String
let id: String
}
struct Model {
let type: String
let name: String
let isExpanded: Bool
let value: [Option]
}
You should get the options array values and join all the arrays
let models:[Model] = //...
let collectionViewArray = models.map { $0.value }.reduce([Option](), +)
Using for loop
var collectionViewArray = [Option]()
for model in models {
collectionViewArray.append(contentsOf: model.value)
}

Swift Collection <__NSCFSet: 0x161174110> was mutated while being enumerated

I understand that I can't add or remove an element from an array while it's being enumerated, but...I'm not! I think I have narrowed down where the crash happens but it doesn't make any sense to me.
func sendToCloud() {
for quoteNumber in newQuotes {
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Quotes")
let predicate = NSPredicate(format: "quoteNumber == %#", quoteNumber)
fetchRequest.predicate = predicate
fetchRequest.fetchLimit = 1
do {
//The next two lines is where the crash happens...
quotes = try context.fetch(fetchRequest)
let currentQuote = quotes.first as! Quotes
//-------------------------------------------
prepRecordForCoreData(obj: currentQuote, recordName: currentQuote.ckrecordname!, recordType: "Quotes")
repRecordForCoreData(obj: currentQuote.customer!, recordName: currentQuote.ckrefcustomer!, recordType: "Customers")
} catch let error as NSError {
print("ERROR SAVING TO CLOUD", error)
}
}
//clear array
newQuotes = []
coreData.saveContext { (success) -> Void in
if success {
AppData.uploadRecords()
}
}
}
Just in case...
func prepRecordForCoreData(obj: SignatureManagedObject, recordName: String, recordType: String) {
let zone = CKRecordZone(zoneName: Contstants.Strings.zoneName)
let id = CKRecord.ID(recordName: recordName, zoneID: zone.zoneID)
let record = CKRecord(recordType: recordType, recordID: id)
let coder = NSKeyedArchiver(requiringSecureCoding: true)
record.encodeSystemFields(with: coder)
let metadata = coder.encodedData
obj.ckmetadata = metadata
obj.ckrecordname = recordName
obj.ckupload = true
}

complex json data array in array in static tableview (for detail)

import UIKit
struct Base : Codable {
let genres : [Genres]?
let name : String?
let overview : String?
}
struct Genres : Codable {
let id : Int?
let name : String?
}
func fillSelectedShowDetails() {
let selectedURL = URL(string: " ")
guard let downloadedURL = selectedURL else {return}
URLSession.shared.dataTask(with: downloadedURL) { (data, urlResponse, error) in
guard let data = data, error == nil, urlResponse != nil else {
print("something went wrong in selectedURL")
return
}
print("Downloaded selectedURL")
do {
let decoder = JSONDecoder()
let decodedResults = try decoder.decode(Base.self, from: data)
self.detailedTV = decodedResults
DispatchQueue.main.async {
self.showNameLabel.text = decodedResults.name
self.overwievLabel.text = decodedResults.overview
if self.detailedTV?.genres?.count == 2 {
self.genreLabel.text = decodedResults.genres?[1].name
} else {
self.genreLabel.text = decodedResults.genres?[0].name
}
print(decodedResults.genres)
}
} catch {
print("something wrong after downloaded in selectedURL\(error)")
}
}.resume()
}
I need every genre name and write it to text. How can I do that?
If your goal is to create a single string from all of the values for the name property then use map and joined:
let genreNames = decodedResults.genres?.compactMap { $0.name }.joined(separator: ",") ?? """
self.genreLabel.text = genreNames

how to get array from dictionary in swift 3

I don't understand how to get array from dictionary, i have tried in this code but i want get array only content and type
This is my array
{
wsResponse = {
aarti =(
{
content = "";
identifier = "slok_one";
title = 1;
type = "\U092d\U0915\U094d\U0924\U093e\U092e\U0930";
},
{
content = "";
identifier = "slok_two";
title = 2;
type = "\U092d\U0915\U094d\U0924\U093e\U092e\U0930";
},
{
content = "";
identifier = "slok_three";
title = 3;
type = "\U092d\U0915\U094d\U0924\U093e\U092e\U0930";
}
}
);
};
Here is my code
Alamofire.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
if let status = response.response?.statusCode {
switch(status){
case 201:
print("example success")
default:
print("error with response status: \(status)")
}
}
//to get JSON return value
if let array = response.result.value
{
let JSON = array as! NSDictionary
print("\(JSON)")
let response = JSON["wsResponse"] as! NSDictionary
let data = response["aarti"]
}
}
i want array from this ,like content,title,type
thank you in advance
According to the JSON this prints all values in the aarti array:
if let JSON = response.result.value as? [String:Any] {
if let response = JSON["wsResponse"] as? [String:Any],
let aarti = response["aarti"] as? [[String:Any]] {
for item in aarti {
let content = item["content"] as! String
let identifier = item["identifier"] as! String
let title = item["title"] as! Int
let type = item["type"] as! String
print(content, identifier, title, type)
}
}
}
Basically do not use NSDictionary / NSArray in Swift.
If you want to put all content values in an array use flatMap. The line can replace the whole repeat loop.
let contentArray = aarti.flatMap { $0["content"] as? String }
PS: If you are going to create multiple arrays from the values don't do that: Use a custom struct or class
if someone want to get dictionary of array and use it in tableview
declaration:
var list:[Any] = []
and initialisation :
self.list = (self.ListDic?["data"] as! NSArray!) as! [Any]
and use:
let dictObj = self.list[indexPath.row] as! NSDictionary
print("object value: ",dictObj["text"] as! String)

struggling with JSON parsing in swift

I am trying to load data in JSON format from my server into IOS application.
Here is my JSON:
[
{
"BankName": "bank1",
"CurrencyName": "cur1",
"SellRate": "0.65",
"BuyRate": "0.55",
"OfficialRate": "0.6"
},
{
"BankName": "bank1",
"CurrencyName": "cur2",
"SellRate": "1.65",
"BuyRate": "1.55",
"OfficialRate": "1.6"
}
]
There are 2 files in my project:
1:
import Foundation
class Shot {
var bankName: String!
var currencyName: String!
var sellRate: String!
var buyRate: String!
var offRate: String!
init (data: NSDictionary) {
self.bankName = getStringFromJSON(data, key:"BankName")
self.currencyName = getStringFromJSON(data, key:"CurrencyName")
self.sellRate = getStringFromJSON(data, key:"SellRate")
self.buyRate = getStringFromJSON(data, key:"BuyRate")
self.offRate = getStringFromJSON(data, key: "OfficialRate")
}
func getStringFromJSON(data: NSDictionary, key: String) -> String {
if let info = data[key] as? String{
return info
}
return ""
}
}
2:
import Foundation
class JsonTest {
func loadJson(completion: ((AnyObject) -> Void)!) {
var urlString = "http://a.com/g.php"
let session = NSURLSession.sharedSession()
let sourceUrl = NSURL(string: urlString)
var task = session.dataTaskWithURL(sourceUrl!){
(data, response, error) -> Void in
if error != nil {
println(error.localizedDescription)
} else {
var error: NSError?
var jsonData = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &error) as NSArray
var shots = [Shot]()
println(jsonData)
for shot in jsonData{
let shot = Shot(data: shot as NSDictionary)
shots.append(shot)
}
println(shots) //[jsontest.Shot, jsontest.Shot]
}
}
task.resume()
}
}
I am trying to populate array automatically when my app starts. To do it I have a code in my mainViewController class.
override func viewDidLoad() {
super.viewDidLoad()
let api = JsonTest()
api.loadJson(nil)
}
The problem occurs when I try to print shots variable in the second file.
it returns [jsontest.Shot, jsontest.Shot] when I was expecting the array of dictionaries.
println(jsonData) works fine and shows JSON data from URL.
Can anybody advise what is wrong in my program?
"shots" is an array of instances of Shot, not a dictionary:
class Shot {
var bankName: String!
var currencyName: String!
var sellRate: String!
var buyRate: String!
var offRate: String!
init (data: NSDictionary) {
self.bankName = getStringFromJSON(data, key:"BankName")
self.currencyName = getStringFromJSON(data, key:"CurrencyName")
self.sellRate = getStringFromJSON(data, key:"SellRate")
self.buyRate = getStringFromJSON(data, key:"BuyRate")
self.offRate = getStringFromJSON(data, key: "OfficialRate")
}
func getStringFromJSON(data: NSDictionary, key: String) -> String {
if let info = data[key] as? String{
return info
}
return ""
}
}
var shots = [Shot]()
let urlString = "http://almazini.lt/getrates.php"
let sourceUrl = NSURL(string: urlString)
// Using NSData instead of NSURLSession for experimenting in Playground
let data = NSData(contentsOfURL: sourceUrl!)
var error: NSError?
// As I'm using Swift 1.2 I had to change "as" with "as!"
let jsonData = NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers, error: &error) as! NSArray
for shot in jsonData{
let shot = Shot(data: shot as! NSDictionary)
shots.append(shot)
}
println(shots[0].bankName)
Update for Swift 2
var shots = [Shot]()
let urlString = "http://almazini.lt/getrates.php"
// Using NSData instead of NSURLSession for experimenting in Playground
if let sourceUrl = NSURL(string: urlString) {
NSURLSession.sharedSession().dataTaskWithURL(sourceUrl, completionHandler: { (data, response, error) in
if error == nil {
if let data = data, jsonData = try? NSJSONSerialization.JSONObjectWithData(data, options: []), jsonArray = jsonData as? [NSDictionary] {
for item in jsonArray {
let shot = Shot(data: item)
shots.append(shot)
}
print(shots[0].bankName)
} else {
print("no JSON data")
}
} else {
print(error!.localizedDescription)
}
}).resume()
}
Seems like there are two problems:
You're trying to use println to debug instead of setting a breakpoint and checking your objects values.
You have not created a description or debugDescription property for your object, so println on your object is just using some default implementation.
shots is an array of your custom object, so when you call println, it's using the description for Array, which prints out the objects in the array, comma separated, and within square brackets.
The default description property for classes in Swift just prints the class name.
Ideally, you should just use a break point to check the values of your object to be certain it initialized correctly, but if it's actually important to get them to print right, it's only a matter of implementing the description property:
override var description: String {
get {
// build and return some string that represents your Shot object
}
}

Resources