I am still getting familiar with Swift, and I am having troubles with adding objects to an array at this moment.
Also the array shouldn't have duplicates.
What I have so far -
A function that is called when user does a button click on a prototype cell.
I am trying to achieve -
Select button (and indicate with a checkmark that he selected/deselected the item)
For each selected item, I have two values - bool status isActive and the selected item's subscriptionID
When user selects the item, I need to add this selection as an object and further append this to an array.
For that, I have subscriptionUpdateData: NSDictionary and my new empty array subscriptionsArray: [NSDictionary] = []
Full Function
func subscriptionCell(cell: SubscriptionCell, didToggleSubscription subscription: Subscriptions) {
var subscriptionsArray: [NSDictionary] = []
var subscriptionUpdateData: NSDictionary = ["subscriptionID": 0, "isActive": false]
if let matchingSubscription = subscriptionInformation?.filter({ $0.subscriptionID == subscription.subscriptionID }).first {
matchingSubscription.isActive = !(matchingSubscription.isActive!)
let subscriptionStatus = matchingSubscription.isActive
let subscriptionStatusForId = matchingSubscription.subscriptionID
subscriptionUpdateData = ["subscriptionID": subscriptionStatusForId!, "isActive": subscriptionStatus!]
tableView.reloadData()
}
subscriptionsArray.append(subscriptionUpdateData)
print("\(subscriptionsArray)")
}
What is going on with above -
I am able to select an item, form it as a dictionary, and add it to my array. :-)
But whenever I select a different item in my list of items, it replaces the existing element in the array with the newly selected item. :-(
I am looking for something like below (without duplicates) which is an input to a REST endpoint -
[{ "subscriptionID" : 1234,
"isActive" : true
},
{
"subscriptionID" : 5678,
"isActive" : false
},
{
"subscriptionID" : 3489,
"isActive" : true
}]
Can someone look into where I am missing something? Or whether there is a better way I can do this?
You must declare subscriptionsArray as global variable, try this code:
var subscriptionsArray: [NSDictionary] = []
func subscriptionCell(cell: SubscriptionCell, didToggleSubscription subscription: Subscriptions) {
var subscriptionUpdateData: NSDictionary = ["subscriptionID": 0, "isActive": false]
if let matchingSubscription = subscriptionInformation?.filter({ $0.subscriptionID == subscription.subscriptionID }).first {
matchingSubscription.isActive = !(matchingSubscription.isActive!)
let subscriptionStatus = matchingSubscription.isActive
let subscriptionStatusForId = matchingSubscription.subscriptionID
subscriptionUpdateData = ["subscriptionID": subscriptionStatusForId!, "isActive": subscriptionStatus!]
tableView.reloadData()
}
subscriptionsArray.append(subscriptionUpdateData)
print("\(subscriptionsArray)")
}
You can use swift dictionary to store those unique subscriptions by using their ids as keys:
id1: status1
id2: status2
...
Code sample:
var statusesDict = [Int: Bool?]()
func subscriptionToggled(subscription: Subscription) {
if let matchingSubscription = subscriptionInformation?.filter({ $0.subscriptionID == subscription.subscriptionID }).first {
let status = matchingSubscription.isActive
let id = matchingSubscription.subscriptionID
statusesDict[id] = status
}
//if you're using the same object from dataSource array, you dont need to look for it by filtering, just use the parameter object
}
Now, when you need to send the result to server just create an array from your dictionary:
func getSubscriptionsArray() -> [[String: Any]] {
var result = [Dictionary<String, Any>]()
for (id, status) in statusesDict.enumerated() {
let subscriptionDict: [String: Any] = [
"subscriptionID" : id,
"isActive" : status
]
result.append(subscriptionDict)
}
print("\(result)")
return result
}
You should not send dictionary description as it is to server. First you should serialize it to JSON:
let jsonData = try JSONSerialization.data(withJSONObject: array, options: [])
let string = String(data: jsonData, encoding: String.Encoding.utf8)
For pretty printed json:
let jsonData = try JSONSerialization.data(withJSONObject: array, options: JSONSerialization.WritingOptions.prettyPrinted)
let string = String(data: jsonData, encoding: String.Encoding.utf8)
Related
I am trying to iterate over the following dictionary:
Dictionary in Firebase
This is my code:
Global.sharedInstance.db.collection("usuarios").getDocuments { (snapshot, error) in
if error != nil {
print("error de lectura usuarios...")
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let txtIdentificador = data["identificador"] as? String ?? ""
let txtBio = data["bio"] as? String ?? ""
let txtNombre = data["nombre_usuario"] as? String ?? ""
let txtFotoPerfil = data["foto_perfil"] as? String ?? ""
var arrFotos = data["fotos"] as? [String: [String:String]]
}
}
}
}
I am able to retrieve the first few lines, like the id, the biography, name, etc.
But when I try to access the array of dictionary I have no idea.
This is the main idea:
I have a set of users, which I iterate over with the first loop 'for document in documents...", then each user has a set of photos. I want to iterate over the 3 photos, and in each iteration I want to retrieve the fields, so I can create a object called Image and associate the user with the 'hasUpload(Image)'.
I would like to know how to iterate over X photos an in each iteration retrieve the fields.
Something like this:
var arrFotos = data["fotos"] as? [String: [String:String]]
for foto in arrFotos {
for (key,value) in foto {
}
}
I get the error: For-in loop requires '[String : [String : String]]?' to conform to 'Sequence'; did you mean to unwrap optional?
A similar StackOverflow case can be found here and this is how they resolved it:
You can either do this, where x is the index and token is the element:
for (x, token) in transcriptCandidate.enumerated() {
}
Or this if you don't need the index:
for token in transcriptCandidate {
}
JSON Response:
How to map that model classes to Array and how to display them in Table. I am new to swift please help me in this.
This data of departments will receive from my JSON response
{
"content" :
[
{
"Agriculture" :
[
{
"displayName" : "Agri e-Permit",
"downloads" : 3
},
{
"displayName" : "Shri test",
"downloads" : 1
}
]
},
{
"Education" :
[
{
"displayName" : "apple cat",
"downloads" : 1
}
]
}
]
}
My issue is how to create a data model and how to assign values and how to use it in tableView with sections in ViewController is my issue.
*** Here The departments "Agriculture", "Education" and "Municipality" ........ more. It's not constant. we have to take the first index of that array for the department.
Let's start with some basic Structs for our parsed objects. These can later be used to populate our UITableView.
struct Section {
let name: String
let rows: [Row]
}
struct Row {
let displayName: String
let downloads: Int
}
Next lets create some sections from your JSON. The code below is unsafe (lots of ! and is also quite verbose), but should hopefully help explain what you need to do:
// This will hold our final sections for populating the table view
var sections = [Section]()
// This is test code for loading a json file from disk - assume you will get yours from a network call
let url = Bundle.main.url(forResource: "Data", withExtension: ".json")!
let data = try! Data(contentsOf: url)
// This converts the Data object to a Swift Dictionary that we can now use to create our sections
let dict = try! JSONSerialization.jsonObject(with: data, options:.allowFragments) as! [String: Any]
// Each section dictionary is in an array within the json "content"
let contentArray = dict["content"] as! [[String: Any]]
// Loop all section dictionaries in the array.
for sectionDictionary in contentArray {
// Then enumerate inside that dictionary to get the section contents.
for (sectionKey, sectionValue) in sectionDictionary {
// Check we have another array of dictionaries as our value, otherwise bail out.
guard let rowsArray = sectionValue as? [[String: Any]] else { return }
// Use compactMap to build our rows.
let rows: [Row] = rowsArray.compactMap { rowDict in
// Check we have a valid row otherwise bail out.
guard let displayName = rowDict["displayName"] as? String, let downloads = rowDict["downloads"] as? Int else { return nil }
return Row(displayName: displayName, downloads: downloads)
}
let section = Section(name: sectionKey, rows: rows)
sections.append(section)
}
}
You should then have a populated array (sections) ready to use in your table.
I'm using Json Object to display in tableview.I parsed Json successfully print data also. and I'm using foreach statement to get the data into variable But the problem is I'm getting in variable the last item in json object. I want to display user to name variable to present name on it
here is my Json data
{
"Categories": [
"school, class"
],
"Tags": [
{
"Value": "ashok",
"Key": "Name"
}, {
"Value": "III",
"Key": "class"
}, {
"Value": "A",
"Key": "section"
}
]
}
here is my model array
struct classInfo: Decodable {
let Categories: String
let Tags: String
let Value: String
let Key: String
var Name: String
let class: String
}
here is my code
var ClassList = [classInfo]()
var name: String = ""
var class: String = ""
In JSONSerialization FUNCTION
do{
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject]
print(json as AnyObject)
let cat = json["Categories"] as? [String: Any]
print(cat!)
if let array = json["Tags"] as? [[String: Any]] {
for obj in array {
if let dict = obj as? NSDictionary {
// Now reference the data you need using:
let Value = dict.value(forKey: "Value")
let Key = dict.value(forKey: "Key")
print(Value!)
print(Key!)
self.name = tag["Value"] as! String
print(self.name)
self.class = tag["Value"] as! String
print(self.class)
}
}
}
IN CELL FOR ROW FUNCTION
cell.Name.text = "Name:\(name)"
cell.class.text = "class: \(class)"
use this
class ModelClass{ var name:String ; var classs:String }
class ModelDataClass { static var modelData = [ModelClass]() }
in your viewController. - add values in models and
in you cellForRowAt -- >
cell.nameTextLabel.text = ModelDataClass.modelData[indexPath.row].name
cell.classsTextLabel.text = ModelDataClass.modelData[indexPath.row].classs
heyy , try this then ,
class ModelClass {var name:String}
now create a custom tableView cell
class CustomTableViewCell:UITableViewCell {
var dataModel : ModelClass?{
didSet{
guard let model = dataModel else {return}
print(model.name)
// set label text here : ex := myLabel.text = model.name
}
}
// declare all IBLayouts or create them programmatically... its your choice
}
in Your tableView cell (cellForRowAt ---> )
use this snippet
var dataModel = [ModelClass]()
dataModel = [ModelClass(name:"Rishabh"),ModelClass(name:"Adi"),ModelClass(name:"FFFf")]
now pass this dataModel to cellDAtaModel by using
let cell = tableView.deq() as! CustomTableViewCell //deq() - > to lazy to type whole lol ;P type by yourself
cell.dataModel = dataModel[indexPath.row]
I am creating a model for saving user data to a Firestore database and am initializing it with a dictionary. Depending on what fields I want to update, I put those values in the dictionary so that the user model only contains certain fields and will therefore only update those fields in my database. However, I want to somehow require that certain fields are provided in certain use cases.
* Below example is a very simplified version of what I am trying to do *
For example: if I am saving a new user, I want to make sure that I include a name, a profile image, and a description. But if I simply want to update a field, then I don't want to require that all those fields are included
I am 99% certain I am attacking this the wrong way, so any help is appreciated.
My Current User Model:
struct FirestoreUser {
var id: String
var name: String?
var profileImage: String?
var dictionary: [String: Any]
init(dictionary: [String: Any]) {
self.id = dictionary["id"] as! String
self.name = dictionary["name"] as? String
self.profileImage = dictionary["profileImage"] as? String
self.dictionary = dictionary
}
}
// MARK: Firestore functions
extension FirestoreUser {
typealias Handler = ((Error?) -> Void)?
func saveNewFirestoreUser(then handler: Handler = nil) {
// make sure all necessary variables are set
// if they aren't all set, something went wrong
guard let _ = name, let _ = profileImage else { return }
let firestore = Firestore.firestore()
let ref = firestore.collection("users").document(id)
ref.setData(dictionary) { (error) in
if let error = error {
handler?(error)
}
handler?(nil)
}
}
}
Construct struct with optional value and pass nil as the parameter which you don't want to update.
You can use the below extension to convert struct to dictionary later.
struct FirestoreUser: Codable {
var id: String
var name: String?
var profileImage: String?
}
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
}
Use -
let test = FirestoreUser(id: "01", name: "Abhinav", profileImage: nil)
print(test.dictionary)
Output -
["name": Abhinav, "id": 01]
In realtime database use updateChildValues instead of setValue if you just want to update the child. I believe there's something equivalent in firestore.
Answer for realtime database:
Update
self.ref.child("users/\(user.uid)/username").setValue(username)
Set new data
let key = ref.child("posts").childByAutoId().key
let post = ["uid": userID,
"author": username,
"title": title,
"body": body]
let childUpdates = ["/posts/\(key)": post,
"/user-posts/\(userID)/\(key)/": post]
ref.updateChildValues(childUpdates)
Answer for firestore:
Just read the documentation for firestore, use updateData instead of addDocument:
let washingtonRef = db.collection("cities").document("DC")
// Set the "capital" field of the city 'DC'
washingtonRef.updateData([
"capital": true
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
Set/Add new data
// Add a new document with a generated id.
var ref: DocumentReference? = nil
ref = db.collection("cities").addDocument(data: [
"name": "Tokyo",
"country": "Japan"
]) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
So rule of thumb is to only pass in the field that you need to update instead of the whole collection/dictionary.
I have 3 arrays when I insert data inside table than that data also add in the array (key, value pair).
var person = ["ABC","XYZ","PQR"]
var email = ["abc#yahoo.com","xyz#yahoo.com","pqr#yahoo.com"]
var mobile = ["1234567890","1234567890","1234567890"]
My problem is how to create JSON object and data store key value pair.
I want this
{
"blogs": [
{
"person": "ABC",
"email": "abc#yahoo.com",
"contact": "1234567890"
},
{
"person": "XYZ",
"email": "xyz#yahoo.com",
"contact": "1234567890"
},
{
"person": "PQR",
"email": "pqr#yahoo.com",
"contact": "1234567890"
}
]
}
so that data passes to url()
In the action button that adds data in array and table
#IBAction func meeting_info(_ sender: Any) {
var PersonName = person_name.text
var Email = email_id.text
var MobileNo = mobile_no.text
if (person_name.text?.isEmpty)! || (email_id.text?.isEmpty)! || (mobile_no.text?.isEmpty)! {
displayMyAlertMessage(userMessage: "please check field empty or not");
}
else{
person.append(person_name.text!)
email.append(email_id.text!)
mobile.append(mobile_no.text!)
meetingTableView.reloadData()
}
}
I want to generate JSON array from person, email and contact in key value pairs
to answer your question.
var person = ["ABC","XYZ","PQR"]
var email = ["abc#yahoo.com","xyz#yahoo.com","pqr#yahoo.com"]
var mobile = ["1234567890","1234567890","1234567890"]
var paramCollection = [Any]()
var index = 0
for personData in person {
var dataCollection = [String:Any]()
dataCollection["person"] = personData
dataCollection["email"] = email[index]
dataCollection["contact"] = mobile[index]
paramCollection.append(dataCollection)
index += 1
}
let finalParameter = ["blogs":paramCollection]
}
//This will do the trick but to make it more robust you should rethink your design
// maybe use struct to store a persons data
struct Blog {
var person: String
var email: String
var mobile: String
init(name:String, email:String, phone:String) {
self.person = name
self.email = email
self.mobile = phone
}
}
//and instead of having three arrays holding three different property, you can have one array of
var blogArray = [Blog]()
//You understand where I'm going with this
This is not a great design by choice to have multiple arrays relating to the data of same Entity.
Ideally create an Entity Model called Blog with fields like personName, email, mobileNo like below -
struct Blog {
var personName: String?
var email: String?
var mobileNo: String?
}
And then in your code have an array of this to save the data then you can directly convert it into Json using the link
Convert Custom Structs to Json
Try this:
let jsonObject: [String: Any]?
let array: [[String: Any]] = [[:]]
for i in 0..person.count {
let dict = ["person": person[i],
"email": email[i],
"contact": mobile[i]]
array.append(dict)
}
jsonObject = ["blogs": array]
let validateJson = JSONSerialization.isValidJSONObject(jsonObject)
if validateJson {
//Go Ahead
}
let dictionary = ["key1": "value1", "key2": "value2"]
let jsonData = try? JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted)
// Verifying it worked:
let parsedObject = try! JSONSerialization.jsonObject(with: jsonData!, options: .allowFragments)