Looping through embedded JSON arrays in Swift? - ios

I'm trying to loop through an embedded JSON array and extract all the values to put in a local array. This is what the JSON looks like:
"welcome": {
"data": {
"tncUrl": ""
},
"items": [
{
"newUser": [
{
"stepConcept": false
},
{
"stepSafety": true
},
{
"stepFacilitator": true
},
{
"stepTransparency": true
}
],
"switcher": [
{
"stepConcept": true
},
{
"stepSafety": true
},
{
"stepFacilitator": true
},
{
"stepTransparency": true
}
]
}
]
}
I'm able to get to a point where I can see I'm retrieving values for "newUser", the problem is looping through those values and adding them to an array. I'm getting a EXC_BAD_INSTRUCTION error when doing so. This is the code I'm using to get those values:
func prepareArrayOfViews(userType: User)
{
if (welcomeJSON != nil)
{
let items : NSArray? = welcomeJSON!.value(forKey: "items") as? NSArray
if (items == nil)
{
listOfViews = ["stepConcept", "stepSafety", "stepFacilitator", "stepTransparency"]
maxPages = listOfViews.count
return
}
if (items != nil) {
if let newUser = (items?.value(forKey: "newUser") as? NSArray){
//Below is where the error "EXC_BAD_INSTRUCTION"
for key in (newUser as! NSDictionary).allKeys
{
if (((newUser as! NSDictionary).value(forKey: key as! String) as? Bool)!)
{
listOfViews.append(key as! String)
}
}
}
if (listOfViews.count == 0)
{
listOfViews = ["stepConcept", "stepSafety", "stepFacilitator", "stepTransparency"]
}
maxPages = listOfViews.count
}
}
}

I have changed your code to use native Swift structs. Since you are not handling errors or doing anything when your optional unwrapping doesn't work, I have also changed the unwrapping to guard statements.
Other than the serious problems with Swift coding practices, your issue was that you were trying to iterate through an array of dictionaries as a simple dictionary.
func prepareArrayOfViews(userType: User){
guard let welcomeJSON = welcomeJSON else {return}
guard let items = welcomeJSON["items"] as? [[String:Any]] else {
listOfViews = ["stepConcept", "stepSafety", "stepFacilitator", "stepTransparency"]
maxPages = listOfViews.count
return
}
for item in items {
if let newUser = item["newUser"] as? [[String:Any]] {
for embeddedDict in newUser {
for (key, value) in embeddedDict {
if let val = value as? Bool, val == true {
listOfViews.append(key)
}
}
}
} else if let switcher = item["switcher"] as? [[String:Any]]{
for embeddedDict in switcher {
for (key, value) in embeddedDict {
if let val = value as? Bool, val == true {
//do whatever you need to with the value
}
}
}
}
}
if (listOfViews.count == 0){
listOfViews = ["stepConcept", "stepSafety", "stepFacilitator", "stepTransparency"]
}
maxPages = listOfViews.count
}

Because
//here newUser is an NSArray
if let newUser = (items?.value(forKey: "newUser") as? NSArray){
//here newUser forced to NSDictionary
for key in (newUser as! NSDictionary).allKeys
try to change this part to
if let newUsers = (items?.value(forKey: "newUser") as? NSArray){
for newUser in newUsers
{
for key in (newUser as! NSDictionary).allKeys
{
if (((newUser as! NSDictionary).value(forKey: key as! String) as? Bool)!)
{
listOfViews.append(key as! String)
}
}
}
}

Related

translate string with YandexAPI in for loop

I want to translate googleMap Api result for location address to Persian. i'm using YandexApi for translate address. separate address components with "،" and pass this array to for api. my problem is after every service call my userInterface updated. how can solve this problem.
for example when I pass this array to method:["road","avenue"]
address label text update after every translate response
first text for label = "جاده"
last text for label = "جاده ، خیابان"
there is my code:
func translateText(text:[String],closure:#escaping ((_ success:String?,_ error:Error?) -> Void)) {
var translateString: String = ""
var responseError: Error?
for item in text {
myGroup.enter()
let urlString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20171105T134956Z.795c7a0141d3061b.dc25bae76fa5740b2cdecb02396644dea58edd24&text=\(item)&lang=fa&format=plain&options=1"
if let allowString = Utilities.shareInstance.getQueryAllowedString(url: urlString) {
if let url = URL(string:allowString){
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let res = json as? [String:Any] {
if let code = res["code"] as? Int {
if code == 200 {
if let textArr = res["text"] as? [AnyObject] {
let flattArr = Utilities.shareInstance.flatStringMapArray(textArr)
if flattArr.count > 0 {
translateString += " ، " + flattArr[0]
}
}
}
}
}
}catch {
responseError = error
}
self.myGroup.leave()
closure(translateString, responseError)
}
}
}
}
}
private func translatingAddressArrayForChecking(address:[String]) {
var addressString = ""
let addressComponent = address
if addressComponent.count > 0 {
YandexTranslateAPI.shateInstance.translateText(text: addressComponent, closure:{ success,_ in
if let res = success {
addressString = res
OperationQueue.main.addOperation {
self.layoutActivity(start: false)
if self.searchState == .Origin && self.destinationButton.isHidden == false{
if let selectOrigin = self.originSelectedCity {
if let city = selectOrigin.city_title {
if addressString.contains(city) {
self.originAddressLabel.text = addressString
if let location = self.startCoordinate {
self.setOrigin(location: location)
}
}else {
self.CreateCustomTopField(text: "wrong city", color: Constants.ERROR_COLOR)
}
}
}
}else if self.searchState == .Destination || self.destinationButton.isHidden == true {
if let selectOrigin = self.destinationSelectedCity {
if let city = selectOrigin.city_title {
if addressString.contains(city) {
self.destinationAddressLabel.text = addressString
if let location = self.endCoordinate {
self.setDestination(location: location)
}
}else {
self.CreateCustomTopField(text: "wrong city", color: Constants.ERROR_COLOR)
}
}
}
}
}
}
})
}
addresses label text update multiple with array component.

Managing encapsulated data in closure

Newbie here.Using Google API Nearby Search. I have problem sending encapsulated data into closure, already populated table with vicinity info, but when i try to send placeID info into closure to get Details, it gives me nil.
Here i get placeID and vicinity, and afterwards populate tableView with places array. Class Place is in separate swift file, function downloadPlaceID is inside ViewController.
class Place {
var placeId: String!
var vicinity: String!
var _placeId: String {
if placeId == nil {
placeId = ""
}
return placeId
}
var _vicinity: String {
if vicinity == nil {
vicinity = ""
}
return vicinity
}
init( place: [String:Any]) {
if let ids = place["id"] as? String {
self.placeId = ids
}
if let vicinities = place["vicinity"] as? String {
self.vicinity = vicinities
}
}
}
func downloadPlaceID (completed: #escaping DownloadComplete) {
let placeURL = URL(string: nearbyURL)
Alamofire.request(placeURL!).responseJSON { (response) in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let results = dictionary["results"] as? [[String:Any]] {
if let status = dictionary["status"] as? String {
if status == "OK" {
for obj in results {
place = Place(place: obj)
// here i get all the placeID's
places.append(place)
}
}
}
}
}
completed()
}
}
Then i try to get details, into which I put placeID:
func downloadDetails( input: String, completed: DownloadComplete) {
let details = "\(detailsBaseURL)\(detailsPlaceId)\(input)\(detailsKey)\(detailsSearchAPIKey)"
print(placeID)
Alamofire.request(details).responseJSON { response in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let result = dictionary["result"] as? [String:Any] {
if let phoneNumber = result["formatted_phone_number"] as? String {
self.phone = phoneNumber
print(self.phone!)
}
if let geometry = result["geometry"] as? [String:Any] {
if let location = geometry["location"] as? [String:Any] {
if let latitude = location["lat"] as? Double {
self.lat = latitude
print(self.lat!)
}
if let longitude = location["lng"] as? Double {
self.lng = longitude
print(self.lng!)
}
}
}
if let openingHours = result["opening_hours"] as? [String:Any] {
if let openNow = openingHours["open_now"] as? Bool {
self.workHours = openNow
print(self.workHours!)
}
}
}
}
}
}
Here is code inside viewDidLoad that i'm trying to use to get details.
override func viewDidLoad() {
super.viewDidLoad()
downloadPlaceID {
detail.downloadDetails(input: place.placeId, completed: {
})
}
}
It should be "place_id" instead of "id"
class Place {
var placeId: String!
var vicinity: String!
var _placeId: String {
if placeId == nil {
placeId = ""
}
return placeId
}
var _vicinity: String {
if vicinity == nil {
vicinity = ""
}
return vicinity
}
init( place: [String:Any]) {
if let ids = place["place_id"] as? String {
self.placeId = ids
}
if let vicinities = place["vicinity"] as? String {
self.vicinity = vicinities
}
}
}

How to write model class for json data with dictionary?

In this class already i had written model class but here some of the data has been added newly but i am trying to create for the model class for remaining dictionaries but unable to implement it can anyone help me how to implement it ?
In this already i had implemented model class for items array and here i need to create a model class for search criteria dictionary and inside arrays
func listCategoryDownloadJsonWithURL() {
let url = URL(string: listPageUrl)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil { print(error!); return }
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
let objArr = jsonObj["items"] as? [[String:Any]]
self.list = objArr!.map{List(dict: $0)}
DispatchQueue.main.async {
let itemsCount = self.list.count
for i in 0..<itemsCount {
let customAttribute = self.list[i].customAttribute
for j in 0..<customAttribute.count {
if customAttribute[j].attributeCode == "image" {
let baseUrl = "http://192.168.1.11/magento2/pub/media/catalog/product"
self.listCategoryImageArray.append(baseUrl + customAttribute[j].value)
}
}
}
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
self.collectionView.reloadData()
self.collectionView.isHidden = false
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
struct List {
let name : String
let sku : Any
let id : Int
let attributeSetId : Int
let price : Int
let status : Int
let visibility : Int
let typeId: String
let createdAt : Any
let updatedAt : Any
var customAttribute = [ListAttribute]()
init(dict : [String:Any]) {
if let customAttribute = dict["custom_attributes"] as? [[String: AnyObject]] {
var result = [ListAttribute]()
for obj in customAttribute {
result.append(ListAttribute(json: obj as! [String : String])!)
}
self.customAttribute = result
} else {
self.customAttribute = [ListAttribute]()
}
self.name = (dict["name"] as? String)!
self.sku = dict["sku"]!
self.id = (dict["id"] as? Int)!
self.attributeSetId = (dict["attribute_set_id"] as? Int)!
self.price = (dict["price"] as? Int)!
self.status = (dict["status"] as? Int)!
self.visibility = (dict["visibility"] as? Int)!
self.typeId = (dict["type_id"] as? String)!
self.createdAt = dict["created_at"]!
self.updatedAt = dict["updated_at"]!
}
}
struct ListAttribute {
let attributeCode : String
let value : String
init?(json : [String:String]) {
self.attributeCode = (json["attribute_code"])!
self.value = (json["value"])!
}
}

I am unable to get the child names from given url in swift 3?

In the api am getting global array but in this I am unable to get the children names as separate array only the last loaded array name has been getting in the array how to get the all children names in the array please help me how to get all the names in it and here is my code already tried
var detailsArray = NSArray()
var globalArray = NSMutableArray()
let url = "http://www.json-generator.com/api/json/get/cwqUAMjKGa?indent=2"
func downloadJsonWithURL() {
let url = NSURL(string: self.url)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
{
self.detailsArray = (jsonObj?.value(forKey: "data") as? [[String: AnyObject]])! as NSArray
print(self.detailsArray)
for item in self.detailsArray
{
let majorDic = NSMutableDictionary()
let detailDict = item as! NSDictionary
print(detailDict["name"]!)
majorDic .setValue(detailDict["name"]!, forKey: "name")
print(detailDict["children"]!)
if !(detailDict["children"]! is NSNull)
{
let children = detailDict["children"]! as! NSArray
let childrenstring = NSMutableString()
if children.count > 0 {
for item in children{
let chilDic = item as! NSDictionary
print(chilDic["name"]!)
print(chilDic["products"]!)
majorDic.setValue(chilDic["name"]!, forKey: "Childernname")
let products = chilDic["products"]! as! NSArray
if products.count > 0
{
for item in products
{
var sr = String()
sr = (item as AnyObject) .value(forKey: "name") as! String
childrenstring.append(sr)
childrenstring.append("*")
}
majorDic.setValue(childrenstring, forKey: "Childernproducts")
}
else
{
print("products.count\(products.count)")
majorDic.setValue("NO DATA", forKey: "Childernproducts")
}
}
}
else
{
print("childernw.count\(children.count)")
majorDic.setValue("NO DATA", forKey: "Childernname")
}
}
else
{
majorDic.setValue("NO DATA", forKey: "Childernname")
majorDic.setValue("NO DATA", forKey: "Childernproducts")
}
self.globalArray.add(majorDic)
}
print("TOTAL ASSECTS\(self.globalArray)")
OperationQueue.main.addOperation({
self.tableView.reloadData()
print(self.globalArray)
print(self.detailsArray)
})
}
}).resume()
}
Try this:
var detailsArray = [DataList]()
func downloadJsonWithURL() {
let url = NSURL(string: "http://www.json-generator.com/api/json/get/cwqUAMjKGa?indent=2")
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
{
let objArr = (jsonObj?["data"] as? [[String: AnyObject]])! as NSArray
for obj in objArr {
self.detailsArray.append(DataList(json: obj as! [String : AnyObject]))
}
print(self.detailsArray)
}
}).resume()
}
Model Class
class DataList: NSObject {
var count: Int
var category_id: Int
var childern:[Children]
var name:String
init (json: [String: AnyObject]){
if let childrenList = json["children"] as? [[String: AnyObject]] {
var result = [Children]()
for obj in childrenList {
result.append(Children(json: obj))
}
self.childern = result
} else {
self.childern = [Children]()
}
if let count = json["count"] as? Int { self.count = count }
else { self.count = 0 }
if let category_id = json["category_id"] as? Int { self.category_id = category_id }
else { self.category_id = 0 }
if let name = json["name"] as? String { self.name = name }
else { self.name = "" }
}
}
class Children:NSObject{
var count:Int
var category_id:Int
var products:[Products]
var name:String
init (json: [String: AnyObject]){
if let productList = json["products"] as? [[String: AnyObject]] {
var result = [Products]()
for obj in productList {
result.append(Products(json: obj))
}
self.products = result
} else {
self.products = [Products]()
}
if let count = json["count"] as? Int { self.count = count }
else { self.count = 0 }
if let category_id = json["category_id"] as? Int { self.category_id = category_id }
else { self.category_id = 0 }
if let name = json["name"] as? String { self.name = name }
else { self.name = "" }
}
}
class Products:NSObject{
var product_id:Int
var name:String
init (json: [String: AnyObject]){
if let product_id = json["product_id"] as? Int { self.product_id = product_id }
else { self.product_id = 0 }
if let name = json["name"] as? String { self.name = name }
else { self.name = "" }
}
}
NOTE: Please check your data type while parsing
I too had the same problem. Instead of using for loop try using flatMap() and then extract the value using value(forKey: " ") function
let productNames = productValues.flatMap() {$0.value}
let names = (productNames as AnyObject).value(forKey: "name")
It had worked for me for my json. My Json was similar to yours.
I got separate values in array.

Firebase Realtime Array count mismatch

I have an iOS swift app using Firebase realtime database. If I use the app normally so far I cannot find any issue. However, I want to anticipate edge cases.
I am trying to stress test my app before I push the update, and one way I am doing it is quickly going back and forth from a VC with a tableView to the next VC which is a detail VC. If I do it several times eventually the tableview will show lots of duplicate data.
I have tested my app by having a tableview open on my simulator and going into my Firebase Console and manually changing a value and instantly on the device the string changes.
So I am confused as to why my tableview would show an incorrect amount of children if it is constantly checking what the value should be.
// MARK: Firebase Methods
func checkIfDataExits() {
DispatchQueue.main.async {
self.cardArray.removeAll()
self.ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.hasChild("cards") {
self.pullAllUsersCards()
} else {
self.tableView.reloadData()
}
})
}
}
func pullAllUsersCards() {
cardArray.removeAll()
let userRef = ref.child("users").child((user?.uid)!).child("cards")
userRef.observe(DataEventType.value, with: { (snapshot) in
for userscard in snapshot.children {
let cardID = (userscard as AnyObject).key as String
let cardRef = self.ref.child("cards").child(cardID)
cardRef.observe(DataEventType.value, with: { (cardSnapShot) in
let cardSnap = cardSnapShot as DataSnapshot
let cardDict = cardSnap.value as! [String: AnyObject]
let cardNickname = cardDict["nickname"]
let cardType = cardDict["type"]
let cardStatus = cardDict["cardStatus"]
self.cardNicknameToTransfer = cardNickname as! String
self.cardtypeToTransfer = cardType as! String
let aCard = CardClass()
aCard.cardID = cardID
aCard.nickname = cardNickname as! String
aCard.type = cardType as! String
aCard.cStatus = cardStatus as! Bool
self.cardArray.append(aCard)
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
})
}
I got help and changed my code drastically, so now it works
func checkIfDataExits() {
self.ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
})
}
func pullCardData() {
let cardRef = self.ref.child("cards")
cardRef.observe(DataEventType.value, with: { (snapshot) in
for cards in snapshot.children {
let allCardIDs = (cards as AnyObject).key as String
if allCardIDs == self.cardID {
if let childId = self.cardID {
let thisCardLocation = cardRef.child(childId)
thisCardLocation.observe(DataEventType.value, with: { (snapshot) in
let thisCardDetails = snapshot as DataSnapshot
if let cardDict = thisCardDetails.value as? [String: AnyObject] {
self.selectedCard?.cardID = thisCardDetails.key
self.selectedCard?.nickname = cardDict["nickname"] as? String ?? ""
self.selectedCard?.type = cardDict["type"] as? String ?? ""
self.pullServicesForCard()
}
})
}
}
}
})
}
func pullServicesForCard() {
if let theId = self.cardID {
let thisCardServices = self.ref.child("cards").child(theId).child("services")
thisCardServices.observe(DataEventType.value, with: { (serviceSnap) in
if self.serviceArray.count != Int(serviceSnap.childrenCount) {
self.serviceArray.removeAll()
self.fetchAndAddAllServices(serviceSnap: serviceSnap, index: 0, completion: { (success) in
if success {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
})
}
})
}
}
func fetchAndAddAllServices(serviceSnap: DataSnapshot, index: Int, completion: #escaping (_ success: Bool) -> Void) {
if serviceSnap.hasChildren() {
if index < serviceSnap.children.allObjects.count {
let serviceChild = serviceSnap.children.allObjects[index]
let serviceID = (serviceChild as AnyObject).key as String
let thisServiceLocationInServiceNode = self.ref.child("services").child(serviceID)
thisServiceLocationInServiceNode.observeSingleEvent(of: DataEventType.value, with: { (thisSnap) in
let serv = thisSnap as DataSnapshot
if let serviceDict = serv.value as? [String: AnyObject] {
let aService = ServiceClass(serviceDict: serviceDict)
self.serviceCurrent = serviceDict["serviceStatus"] as? Bool
self.serviceName = serviceDict["serviceName"] as? String ?? ""
self.serviceURL = serviceDict["serviceURL"] as? String ?? ""
self.serviceFixedBool = serviceDict["serviceFixed"] as? Bool
self.serviceFixedAmount = serviceDict["serviceAmount"] as? String ?? ""
self.attentionInt = serviceDict["attentionInt"] as? Int
self.totalArr.append((serviceDict["serviceAmount"] as? String)!)
// self.doubleArray = self.totalArr.flatMap{ Double($0) }
// let arraySum = self.doubleArray.reduce(0, +)
// self.title = self.selectedCard?.nickname ?? ""
// if let titleName = self.selectedCard?.nickname {
// self.title = "\(titleName): \(arraySum)"
// }
aService.serviceID = serviceID
if serviceDict["serviceStatus"] as? Bool == true {
self.selectedCard?.cStatus = true
} else {
self.selectedCard?.cStatus = false
}
if !self.serviceArray.contains(where: { (service) -> Bool in
return service.serviceID == aService.serviceID
}) {
self.serviceArray.append(aService)
self.serviceArray.sort {$1.serviceAttention < $0.serviceAttention}
}
}
self.fetchAndAddAllServices(serviceSnap: serviceSnap, index: index + 1, completion: completion)
})
}
else {
completion(true)
}
}
else {
completion(false)
}
}

Resources