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
Related
I have this code:
struct Restaurants: Identifiable {
let name: String
let imageUrl: URL
let id: String
let rating: Double
let url: String
let category: String
static var viewModels: [Restaurants] = [
Restaurants(name: "Silverio's Mexican Kitchen", imageUrl: URL(string: "https://mainsite-prod-cdn.azureedge.net/partner-images/432257/micrositeimage_p1.jpg")!, id: "hjkhjhjh", rating: 2, url: "https://google.com"),//, category: "Mexican"),
Restaurants(name: "Taqueria La Esquinita", imageUrl: URL(string: "https://s3-media0.fl.yelpcdn.com/bphoto/x-KCQ7osmvBWLA9WpPdO_Q/o.jpg")!, id: "hjdha", rating: 3, url: "https://google.com")//, category: "Mexican")
] {
didSet {
print("data set")
}
}
}
How do I store viewModels in Firestore? I want to maintain this object structure so that I can carry it across multiple devices.
The idea of this app is to help groups find a place to eat. Therefore, I have this structure:
I am now using sub-collections for each Restaurant thanks to #FaridShumbar. How do I get documents from a sub-collection?
You need the get() method in order to retrieve documents.
Following examples from the documentation, you could retrieve your documents as follows:
db.collection("parties").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
This example retrieves all documents in a collection.
I did it by saving each element in the struct separately and then putting it back together when I get the document.
Save Code:
db.collection("parties").document(Utilities.code).collection("Restaurants").document(card.name).setData([
"name" : card.name,
"img" : imgUrl,
"id" : card.id,
"rating" : card.rating,
"url" : card.url,
"yes" : FieldValue.arrayUnion([Utilities.name])
], merge: true) { error in
if error != nil {
print("error adding restaurant: \(error!)")
}
}
Retrieve code:
class restaurantsFirebase: ObservableObject {
#Published var restaurants: [RestaurantListViewModel] = []
private let db = Firestore.firestore()
private var devices = Array<String>()
func fetchData() {
db.collection("parties").document(Utilities.code).addSnapshotListener { doc, error in
if error == nil {
if doc != nil && doc!.exists {
if let getDevices = doc!.get("devices") as? Array<String> {
self.devices = getDevices
}
}
}
}
db.collection("parties").document(Utilities.code).collection("Restaurants").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("no documents")
return
}
self.restaurants = documents.compactMap({ (queryDocumentSnapshot) in
print(queryDocumentSnapshot.data())
let data = queryDocumentSnapshot.data()
let name = data["name"] as? String ?? ""
let img = data["img"] as? String ?? ""
let id = data["id"] as? String ?? ""
let rating = data["rating"] as? Double ?? 0
let url = data["url"] as? String ?? ""
let yes = data["yes"] as? Array<String> ?? []
let compiled = RestaurantListViewModel(name: name, imageUrl: URL(string: img)!, id: id, rating: rating, url: url)
print("restaurant = \(compiled), restaurants = \(self.restaurants)")
print("yes = \(yes), devices = \(self.devices)")
if yes == self.devices {
// self.restaurants.append(compiled)
return compiled
}else {
return nil
}
})
}
}
}
Use:
#ObservedObject private var viewModels = restaurantsFirebase()
db.collection("parties").document(Utilities.code).addSnapshotListener { doc, error in
if error == nil {
if doc != nil && doc!.exists {
if let dev = doc!.get("devices") as? Array<String> {
print("devices = \(dev)")
devices = dev
}
if let done = doc!.get("devicesDone") as? Array<String> {
print("devicesDone = \(done), devices = \(devices)")
if done == devices {
self.viewModels.fetchData()
showFinal = true
results = false
}
}
}
}
I have API response with nested array . But I can't understand how can I remove whole Dict by filtering the value .
This is the response screenshot
https://imgur.com/XIDyfYX
here is the json Resonse :- https://del.dog/lofavofogo.json
I have tried this but I don't know how to get filter nested value and remove whole dict at specific index
How to remove pairs from dictionary at specific index - Swift?
I want to remove the dict where section name are "NA"
Here is the code :-
Model Class For API Response :-
class filterclass: NSObject {
var classesID : String?
var classname : String?
var section = [filterSections]()
init(json: [String: Any]) {
if let classname = json["class"] as? String {
self.classname = classname
}
if let classesID = json["classesID"] as? String {
self.classesID = classesID
}
print("classname",classname)
if let evUserGoing = json["classsection"] as? [[String: Any]] {
if self.section.count > 0
{
self.section.removeAll()
}
for evUser in evUserGoing {
// print("evUser",evUser)
let userGoing = filterSections(json: evUser)
self.section.append(userGoing)
}
for sec in section {
let section = sec.secctionname
let setionID = sec.sectionID
}
}
}
}
class filterSections: NSObject {
var sectionID : String?
var secctionname : String?
var isSelctedSection : Bool = false
init(json: [String: Any]) {
if let sectionID = json["sectionID"] as? String {
self.sectionID = sectionID
}
if let secctionname = json["section"] as? String {
self.secctionname = secctionname
}
print("sectioname",secctionname)
}
}
API POST Method TO hit API :-
func getClassSectionAPI() {
if ReusableClass.sharedInstance.isNetworkAvailable() == true
{
ReusableClass.sharedInstance.showActivityIndicator()
let UUid = LoginUserInfo.sharedInstance.uuid!
let dictionary = ["uuid":UUid,"device_id":devicetoken,"school_id":LoginUserInfo.sharedInstance.schoolId!, "user_type":LoginUserInfo.sharedInstance.usertype!]
print(dictionary)
let encoder = JSONEncoder()
if let jsonData = try? encoder.encode(dictionary) {
if let jsonString = String(data: jsonData, encoding: .utf8) {
// print(jsonString)
let cipher:String = CryptoHelper.encrypt(input:jsonString)!;
let NewEncryption = "data=\(cipher)"
print(NewEncryption)
let hmac_md5 = cipher.hmac(algorithm: .sha512, key: kHMACKey)
print("hmac",hmac_md5)
UserDefaults.standard.set(hmac_md5, forKey: Headerkey)
Singleton.sharedInstance.getWebservicesverify(params: NewEncryption, Methodname: KFilterClassSection, data: Stringnil)
{ (result) in
DispatchQueue.main.async {
ReusableClass.sharedInstance.hideActivityIndicator()
}
if result != nil
{
do {
let jsonData = try JSONSerialization.data(withJSONObject: result)
if let json = String(data: jsonData, encoding: .utf8) {
let Dict = function.convertToDictionary(text: json)! as NSDictionary
guard let data = Dict[KData] as? String
else
{
return
}
self.baseDict = data
}
}
catch {
}
guard let output = CryptoHelper.decrypt(input:self.baseDict)
else
{
return
}
print(output)
let mainDict = function.convertToDictionary(text: output)! as NSDictionary
let status = mainDict[KStatus] as! NSInteger
if(status == 1)
{
DispatchQueue.main.async {
print("Main dict",mainDict)
guard let messageArray = mainDict["data"] as? [[String: Any]] else{
return
}
if self.arrayClasSection.count > 0
{
self.arrayClasSection.removeAll()
}
print("Main dict",messageArray)
for arr in messageArray {
let obj = filterclass.init(json: arr)
if let index = self.arryFilterTemperary.index(where: { $0.classname == obj.classname }) {
// let filtered = self.arryFilterTemperary.filter { $0.classname == "NA" }
obj.section = self.arryFilterTemperary[index].section
self.arrayClasSection.append(obj)
for sec in self.arryFilterTemperary[index].section {
let section = sec.sectionID
let sectionName = sec.secctionname
self.NASection = sec.secctionname!
print(self.NASection)
self.selectedNASectionID = sec.sectionID!
// let test = self.arryFilterTemperary[index].section.filter { !$0.value.contains("") }
// print(test)
}
}
else
{
self.arrayClasSection.append(obj)
}
}
ReusableClass.sharedInstance.hideActivityIndicator()
self.tableFilter.reloadData()
}
}
I want to append the data to array but before appending I want to
filter that "NA" value dict from the array
Since this is your first question I go to some greater length in answering it than usual. Playgrounds are an exceptional way to demonstrate your problem, so you should always try to compose your questions in a form of one. I will post my answer directly from the Playground I have done.
With that out of the way lets get to the question. Your main problem seems to be that you tried an ill fated JSONSerialization "shortcutt" route. This looks cheap from the outside, but working with the unavoidable optionality of a [String:Any] comes at a high cost in a language like Swift. The way to go is the brilliant Codable protocol, at least in my opinion. Once you define your data structure properly Xcode has so much more possibilities to guide you through the APIs that writing your filter code becomes a piece of cake.
Enough of the ranting, let's get to the pizza.
import UIKit
let dataStr = """
{
"status":1,
"message":"Class and sections list",
"data":[
{
"classsection":[
{
"section":"A",
"sectionID":"1",
"classesID":"1"
},
{
"section":"B",
"sectionID":"3",
"classesID":"1"
}
],
"class":"First",
"classesID":"1"
},
{
"classsection":[
{
"section":"A",
"sectionID":"2",
"classesID":"2"
},
{
"section":"B",
"sectionID":"7",
"classesID":"2"
}
],
"class":"Second",
"classesID":"2"
},
{
"classsection":[
{
"section":"A",
"sectionID":"20",
"classesID":"15"
}
],
"class":"Third",
"classesID":"15"
},
{
"classsection":[
{
"section":"NA",
"sectionID":"33",
"classesID":"22"
}
],
"class":"Pre Nursery",
"classesID":"22"
},
{
"classsection":[
{
"section":"NA",
"sectionID":"34",
"classesID":"23"
},
{
"section":"A",
"sectionID":"35",
"classesID":"23"
},
{
"section":"B",
"sectionID":"36",
"classesID":"23"
},
{
"section":"C",
"sectionID":"37",
"classesID":"23"
}
],
"class":"Fourth four",
"classesID":"23"
},
{
"classsection":[
{
"section":"NA",
"sectionID":"38",
"classesID":"24"
}
],
"class":"Fifth",
"classesID":"24"
},
{
"classsection":[
{
"section":"NA",
"sectionID":"39",
"classesID":"25"
}
],
"class":"sixth 6th",
"classesID":"25"
}
]
}
"""
struct Section: Codable {
let section, sectionId, classesId: String
enum CodingKeys: String, CodingKey {
case sectionId = "sectionID"
case classesId = "classesID"
case section
}
}
struct Class1: Codable {
let classsection: [Section]
let clazz, classesId: String
private enum CodingKeys: String, CodingKey {
case classsection
case clazz = "class"
case classesId = "classesID"
}
}
struct Response: Codable {
let status: Int
let message: String
let data: [Class1]
func filterSections(notMatching filterVal: String) -> Response {
let filteredData = data.map { (clazz) -> Class1 in
let filteredSections = clazz.classsection.filter { (sect) -> Bool in
sect.section != filterVal
}
return Class1(classsection: filteredSections, clazz: clazz.clazz, classesId: clazz.classesId)
}
return Response(status: status, message: message, data: filteredData)
}
}
let jsonData = dataStr.data(using:.utf8)!
do {
let res = try JSONDecoder().decode(Response.self, from: jsonData)
let filteredResponse = res.filterSections(notMatching: "NA")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
print(String(data:try encoder.encode(filteredResponse), encoding: .utf8)!)
} catch {
print(error)
}
As you can see your data structure is easily defined and the filtering code is really easy to understand once you wrap your head around the lambdas (which you should). The playground will output nicely formatted JSON as an answer, this way it is easy to check that your code does the right thing without all the messy parts of asynchronous communication (which are still nicely done in Swift).
Here's my last tipp of the day: Always try to isolate your problem as much as possible if you post a question on StackOverflow. I think your question carried too much legacy, you should whittle it down for the next one. This will improve your chances for a quick answer.
I need to sync two json files to add new content from File A (located in the app bundle) to File B after an app update.
Both json files are arrays of dictionaries. I need to iterate the dictionaries form File A, and based on the "id" value, if a dictionary is not present in File B I need to append those missing dictionaries and save File B back to the file system.
I have a solution below that does this, and seems to work. But it's SO ugly! Granted I put this together in about 15 minutes cringing the whole way but I'm sure there has to be a better way of handling this. Also, I don't want to further muddy the waters by converting these dictionaries to structs or models for the comparison only to convert them back to dictionaries -> json.
Any advise here would be great! I prefer clean code and this is a mess.
typealias JSON = [[String: Any]]
static private func uglySync() {
let fileName: String = "someFileName"
guard let sourceUrl = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationUrl = destinationDirectory.appendingPathComponent("Data/" + fileName + ".json")
do {
let sourceData = try Data(contentsOf: sourceUrl)
do {
if let sourceArray = try JSONSerialization.jsonObject(with: sourceData, options: .mutableContainers) as? JSON {
do {
let destinationData = try Data(contentsOf: destinationUrl)
do {
if let destinationArray = try JSONSerialization.jsonObject(with: destinationData, options: .mutableContainers) as? JSON {
var mutableArray = destinationArray
sourceArray.forEach({ (item) in
if let itemId = item["id"] as? String {
let foundItem = destinationArray.filter { $0["id"] as! String == itemId }.first
if foundItem == nil {
mutableArray.append(item)
}
}
})
do {
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationUrl)
} catch let error as NSError {
print("Couldn't write to file: \(error.localizedDescription)")
}
} else {
print("Cound not process json")
}
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
} else {
print("Cound not process json")
}
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
// oh wow the try catches :/
}
I've grouped converting the files to jsonArray to simplify the do...catch. Alternatively, if you don't need to print the error message, you could opt to have Optional try? as well to remove the do...catch block.
typealias JSONArray = [[String: Any]]
private func jsonArray(from fileURL: URL) -> JSONArray? {
do {
let fileData: Data = try Data(contentsOf: fileURL)
guard let jsonArray = (try JSONSerialization.jsonObject(with: fileData, options: .mutableContainers)) as? JSONArray else {
debugPrint("Failed to find JSON Array table")
return nil
}
return jsonArray
} catch {
print(error.localizedDescription)
return nil
}
}
func sync() {
let fileName: String = "someFileName"
guard
let fileURL: URL = Bundle.main.url(forResource: fileName, withExtension: "json"),
let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first,
let destinationURL: URL = destinationDirectory.appendingPathComponent("Data/" + fileName + ".json"),
let sourceArray = jsonArray(from: fileURL),
let destinationArray = jsonArray(from: destinationURL)
else {
return
}
var mutableArray = destinationArray
let destinationIDArray = destinationArray.compactMap { $0["id"] as? String }
mutableArray.forEach { (item) in
if let itemId = item["id"] as? String, !(destinationIDArray.contains { $0 == itemId }) {
mutableArray.append(item)
}
}
// Update File
do {
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationURL)
} catch {
print("Couldn't write to file: \(error.localizedDescription)")
}
}
I think you can put the different trys in the same do block.
do {
try function1()
try function2()
} catch {
print(error.localizedDescription)
}
So afterwards your function may look like
typealias JSON = [[String: Any]]
static private func moderatelyOkSync() {
let fileName: String = "someFileName"
guard let sourceUrl = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationUrl = destinationDirectory.appendingPathComponent("Data/" + fileName + ".json")
do {
let sourceData = try Data(contentsOf: sourceUrl)
if let sourceArray = try JSONSerialization.jsonObject(with: sourceData, options: .mutableContainers) as? JSON {
let destinationData = try Data(contentsOf: destinationUrl)
}
var mutableArray = destinationArray
sourceArray.forEach({ (item) in
if let itemId = item["id"] as? String {
let foundItem = destinationArray.filter { $0["id"] as! String == itemId }.first
if foundItem == nil {
mutableArray.append(item)
}
}
})
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationUrl)
} catch {
print(error.localizedDescription)
}
}
The way I would do it is to Decode the json files with struct and then encode(serialization) it to the other files. Because the code to do that would be a 2 liner, but you would first have to layout all the variables in the struct. Probably still not optimal
I'm getting this error when I try to proceed on parsing the JSON. Does anyone know what I gotta do to fix it?
Error 1: Cannot subscript a value of type 'AudiobookJSON' (aka 'Array<Dictionary<String, Any>>') with an index of type 'String'
Error on Print
File Model: Audiobook.swift:
import Foundation
struct Audiobook : Codable {
let id: Int
let descricao: String
let urlImagem: String
init(dictionary: AudiobookJSON) {
self.id = dictionary["id"] as! Int//////ERROR MESSAGE ////////
self.descricao = dictionary["descricao"] as! String/////ERROR MESSAGE
self.urlImagem = dictionary["urlImagem"] as! String////ERROR MESSAGE
}
}
Folder Networking: APIClient.swift:
import Foundation
typealias AudiobookJSON = [[String: Any]]
struct APIClient {
static func getAudiobooksAPI(completion: #escaping ([Audiobook]?) -> Void) {
let url = URL(string: "https://alodjinha.herokuapp.com/categoria")
let session = URLSession.shared
guard let unwrappedURL = url else { print("Error unwrapping URL"); return }
let dataTask = session.dataTask(with: unwrappedURL) { (data, response, error) in
guard let unwrappedDAta = data else { print("Error unwrapping data"); return }
do {
//let json = try JSONSerialization.jsonObject(with: unwrappedDAta, options: .allowFragments) as! [String:Any]
//let posts = json["data"] as? AudiobookJSON
let posts = try JSONDecoder().decode([Audiobook].self, from: unwrappedDAta)
print(posts)
completion(posts)
} catch {
print("Could not get API data. \(error), \(error.localizedDescription)")
}
}
dataTask.resume()
}
}
As I can see that AudiobookJSON is an array of key-value pairs that's why you are getting error: So you have to use codable like that:
First: you have to make Codable type struct like that(your codable struct variable names should be same as you are getting in response):
struct Audiobook: Codable {
let id: Int?
let descricao: String?
let urlImagem: String?
}
Second: when you get the response then parse directly using codale like that:
guard let unwrappedDAta = data else { print("Error unwrapping data"); return }
do {
let posts = try JSONDecoder().decode([Audiobook].self, from: unwrappedDAta)
print(posts)
completion(posts)
} catch let message {
print("JSON serialization error:" + "\(message)")
}
You can directly use the response like:
for audio in posts {
print("audio.id")
print("audio.descricao")
print("audio.urlImagem")
}
I unable to get the driverId from this JSON
[data: [{"id":"619","driverId":"6789","starting_time":"2016-12-12 23:24:50","end_time":null}]]
Here is my full code
//selector Method SESSION_STATUS_REQUEST_COMPLETE
func afterServiceStatus(notification : NSNotification) {
print(notification.userInfo!)
guard let data = notification.userInfo!["data"] else{
print("data have no idea ")
return
}
if let driverId = notification.userInfo![0]!["driverId"] {
print(driverId)
}
// if let driverId = data["driverId"] as? String{
//
// print(driverId)
// }
}
You can try this...
func afterServiceStatus(notification : NSNotification) {
print(notification.userInfo!)
guard let data = notification.userInfo!["data"] else{
print("data have no idea ")
return
}
let driverId = notification.userInfo![0]?.objectForKey("data")?.valueForKey("driverId")
print(driverId)
}
You should try to avoid all the force unwraps, because they make your code vulnerable to crashing if you get back JSON in an unexpected format.
This should retrieve the driverId from your dictionary:
func afterServiceStatus(notification: NSNotification) {
guard
let userInfo = notification.userInfo as? [String: Any],
let data = userInfo["data"] as? [[String: Any]],
let driverId = data[0]["driverId"] as? String
else {
return
}
print(driverId)
}
func afterServiceStatus(notification : NSNotification) {
print(notification.userInfo!)
guard let data = notification.userInfo!["data"] else{
print("data have no idea ")
return
}
if let driverId = notification.userInfo[0]["data"]["driverId"].string
{
print(driverId)
}
}
If it doesn't work then forward me link, I'll give you proper solution.