I'm working on an food ordering app currently when I add items into any array, then I need to pass that items to cartVC via CartDataModal where the cartArrayDict is of NSMutableArray type. But when I insert the SelectedDict the app crashes can anyone help me with this?
Heres my code:
var restMenu = [[String:Any]]()
func addTapped(cell: RestaurantItemViewCell)
{
//get the indexPath for add button click
let indexPath = self.restTableView.indexPath(for: cell)
print("the indexPath is", indexPath?.row)
print("all obj is",restMenu)
var selectedDict = restMenu[(indexPath?.row)!]
print("selected dict is",selectedDict)
selectedDict["ItemQuant"] = cell.itemQuantityLabel.text
print("selected dict is",selectedDict)
// Append In Cart Modal
CartDataModal.shared_Inst.cartArrayDict.insert(selectedDict, at: (indexPath?.row)!)
print("rest menu is",restMenu)
restMenu.remove(at: (indexPath?.row)!)
print("rest menu is",restMenu)
restMenu.insert(selectedDict, at: (indexPath?.row)!)
print("restmenu is",restMenu)
}
My CartDataModal:
class CartDataModal: NSObject {
static let shared_Inst = CartDataModal()
var cartArrayDict: [String:Any]!
}
from my ViewController i'm getting data From my Restaurant.plist
func moveToDetailController(img:UIImage,name:String)
{
print("the rest name is", name)
let pathUrl = Bundle.main.path(forResource: "ResturantFile", ofType: ".plist")
print("path url is",pathUrl as Any)
let finalArray = NSMutableArray(contentsOfFile: pathUrl!)
print("final Array is",finalArray)
let restaurantNames = finalArray?.firstObject as? NSDictionary
print("resturant name is",restaurantNames as Any)
if let menuDataArray:[[String:Any]] = restaurantNames?.value(forKey: name) as? [[String:Any]]
{
print("menu data is",menuDataArray)
let restVC = self.storyboard?.instantiateViewController(withIdentifier: "RestaurantViewController") as! RestaurantViewController
restVC.tempImg = img
restVC.tempTitle = name
restVC.restMenu = menuDataArray
self.navigationController?.pushViewController(restVC, animated: true)
}
}
You need to modify your struct:
class CartDataModal: NSObject {
static let shared_Inst = CartDataModal()
var cartArrayDict = [[String: Any]]()
}
You need to init array of the dictionary before insert. So You can init in struct or you can init before use according to your use.
Related
I have an app where when a user taps a cell from a TableView representing a group of images, he is taken to another tableView where all the images within that group should be shown. However I am unsure of how to do this.
I have currently have extracted at the beginning the info necessary to make a reference and get all the data, which is in an array of objects. However how can I access these values from another class?
DataModel:
struct UserImage {
var userID: String
var image: UIImage
var postNum: String
}
I am creating an array of this as shown bellow, in a P1TableVC:
let arrayOfUserImageData = [UserImage]()
The function which retrieves and store the data looks as follows:
func fetchAllUserFristImage() {
print("Description: calling of fetchAllUserFristImage()")
Database.database().reference().child("Posts").observe(.childAdded, with: {(snapshot) in
if snapshot.value as? [String: AnyObject] != nil {
let user = snapshot.key
print("Description: calling of snapshot.value is not nil ")
self.databaseRef = Database.database().reference()
let usersPostRef2 = self.databaseRef.child("Posts").child(user)
usersPostRef2.observe(.value, with: {(postXSnapshots) in
if let postDictionary2 = postXSnapshots.value as? [String:AnyObject] {
for (p) in postDictionary2 {
if let posts = p.value as? [String:AnyObject] {
print("Description: posts has value of: \(posts)")
//to get back to where i was delete the below for i
for (i) in posts {
if let imageUrlString = i.value as? [String:AnyObject], let postUrl = imageUrlString["image1"] as? String {
print("Description: inside the if let imageUrlString = i.value ")
self.feedArray.append(Post(fetchedImageURL: postUrl))
if let imageUrl = URL(string: postUrl), let imageDataL = try? Data(contentsOf: imageUrl), let image = UIImage(data: imageDataL) {
print("Description: inside the if let imageUrl = URL(string: postUrl)")
print("Description: img url's of posts: \(imageUrl)")
self.tableData.append(UserImage(userID: user, image: image, postNum: p.key))
self.tableView.reloadData()
} else {print("this user had no posts, was nil")}
}
}
}
}
}
})
//below shud stay same
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
In didSelectItem method fetch the selected data from tableData based on indexPath.row and pass this data to next controller (make a variable in next controller and before pushing/presenting assign the fetched data to that variable).
didSelectItem : Fetch the selected data - let data = tableData[indexPath.row]
Assign it to next controller's variable -
let vc = nextVC()
vc.fetchedData = data
push/present vc
Or you may probably use Singleton
For instance: just create class Model
class Model {
static let sharedInstance = Model()
var tableVC: P1TableVC!
}
Then inside of your VC in viewDidLoad
class P1TableVC: UIViewController {
override func viewDidLoad() {
Model.sharedInstance.tableVC = self
}
}
Then you can use your class everywhere you want
Model.sharedInstance.tableVC.arrayOfUserImageData
Or you can create variable for all your data
class Model {
static let sharedInstance = Model()
var data: [UserImage]()
}
And then use data not VC
Model.sharedInstance.data
In order to populate my tableView, I append items (created from a struct) to a local array:
func loadList() {
var newAnnotations: [AnnotationListItem] = []
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").queryOrderedByKey().observeSingleEvent(of: .value, with: {snapshot in
for item in snapshot.children {
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
annotationList = newAnnotations
self.tableView.reloadSections([0], with: .fade)
})
}
}
When I click a specific row, I am taken to a DetailViewController where it is only a large UITextView (named notes). The UITextView.text displayed is based on the selected indexPath.row and the "notes" value is retrieved from the array. Now the user is able to type some text and when they are done, the textViewDidEndEditing function is called:
func textViewDidEndEditing(_ textView: UITextView) {
notes.resignFirstResponder()
navigationItem.rightBarButtonItem = nil
let newNotes = self.notes.text
print(newNotes!)
}
Now I'd like to updateChildValues to newNotes to the child node "notes" in my JSON:
"users" : {
"gI5dKGOX7NZ5UBqeTdtu30Ze9wG3" : {
"annotations" : {
"-KuWIRBARv7osWr3XDZz" : {
"annotationSubtitle" : "1 Cupertino CA",
"annotationTitle" : "Apple Infinite Loop",
"notes" : "Does it work?!",
}
How can I access the selected autoID so I can update the specific notes node. So far the best I have is:
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(somehow access the specific childID).updateChildValues(["notes": newNotes])
Any help will be greatly appreciated. Thanks in advance
UPDATE
The annotationListItem struct is created:
struct AnnotationListItem {
let key: String?
var annotationTitle: String?
let annotationSubtitle: String?
let notes: String?
let ref: DatabaseReference?
init(key: String = "", annotationTitle: String, annotationSubtitle: String, notes: String) {
self.key = key
self.annotationTitle = annotationTitle
self.annotationSubtitle = annotationSubtitle
self.notes = notes
self.ref = nil
}
init(snapshot: DataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
annotationTitle = snapshotValue["annotationTitle"] as? String
annotationSubtitle = snapshotValue["annotationSubtitle"] as? String
notes = snapshotValue["notes"] as? String
ref = snapshot.ref
}
init(Dictionary: [String: AnyObject]) {
self.key = Dictionary["key"] as? String
self.annotationTitle = Dictionary["annotationTitle"] as? String
self.annotationSubtitle = Dictionary["annotationSubtitle"] as? String
self.notes = Dictionary["notes"] as? String
self.ref = nil
}
func toAnyObject() -> Any {
return [
"annotationTitle": annotationTitle as Any,
"annotationSubtitle": annotationSubtitle as Any,
"notes": notes as Any
]
}
}
UPDATE
This is how the annotationListItem is created to be stored in Firebase:
// Using the current user’s data, create a new AnnotationListItem that is not completed by default
let uid = Auth.auth().currentUser?.uid
guard let email = Auth.auth().currentUser?.email else { return }
let title = placemark.name
let subtitle = annotation.subtitle
let notes = ""
// declare variables
let annotationListItem = AnnotationListItem(
annotationTitle: title!,
annotationSubtitle: subtitle!,
notes: notes)
// Add the annotation under their UID
let userAnnotationItemRef = uidRef.child(uid!).child("annotations").childByAutoId()
userAnnotationItemRef.setValue(annotationListItem.toAnyObject())
I think you only need to do this:(since you have declared the note as global)
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(note.key).updateChildValues(["notes": newNotes])
inside the method where you change the notes
If I am not mistaken you are creating an array of a custom object?
var newAnnotations: [AnnotationListItem] = []
You could do something like: var newAnnotations: [(key: String, value: [String : Any])] = [] (Any only if you are going to have Strings, Integers, ect. If it'll only be String then specify it as a String.
Accessing the key would be: newAnnotations[indexPath.row].key in your cellForRowAtIndex of your tableView. Accessing values would be: newAnnotations[indexPath.row].value["NAME"].
You can have a separate array that holds the key and just append it at the same time as your population:
for item in snapshot.children {
guard let itemSnapshot = task as? FDataSnapshot else {
continue
}
let id = task.key //This is the ID
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
Another thing you could do is go up one more level in your firebase call:
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").observeSingleEvent(of: .value, with: {snapshot in
if snapshot is NSNull{
//Handles error
} else{
if let value = snapshot.value as? NSDictionary{ //(or [String: String]
//set localDictionary equal to value
}
}
self.tableView.reloadSections([0], with: .fade)
})
}
And then when you select a row: let selectedItem = localDictionary.allKeys[indexPath.row] as! String //This is the ID you pass to your viewController.
I am on a Timer project that takes a value from a variable through unwind segue to pass it to another view controller, then append its value to an Array that should be used to insert a row with the task name whenever the user pressed save button I need to save the result permanently, but I am confused which data should I save the value of the variable itself, the row, or the array?
var taskList = [String]()
#IBAction func saveToTaskList (segue: UIStoryboardSegue) {
let newItemViewController = segue.source as! AddTaskTableViewController
let name = newItemViewController.itemName
let date = newItemViewController.date
print("itemName passed is: \(name)")
if name == "" {
}
else {
print(date)
taskList.append(name!)
let indexToInsert = taskList.count == 0 ? 0 : taskList.count - 1
let indexPath = IndexPath(row: indexToInsert, section: 0)
tableView.insertRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}
add task view controller
Timer / Task List
Ok, thanks to the hint of Matt Le Fleur
I solved the issue by using objects as below:
#IBAction func saveToTaskList (segue: UIStoryboardSegue) {
let newItemViewController = segue.source as! AddTaskTableViewController
var name = newItemViewController.itemName
let date = newItemViewController.date
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items:[String]
if let tempItems = itemsObject as? [String] {
items = tempItems
items.append(name!)
print(items)
} else {
items = [name!]
}
UserDefaults.standard.set(items, forKey: "items")
name = ""
}
Then added a ViewDidAppear as below:
override func viewDidAppear(_ animated: Bool) {
let itemsObject = UserDefaults.standard.object(forKey: "items")
if let tempItems = itemsObject as? [String] {
items = tempItems
}
tableView.reloadData()
}
I'm new to Swift. I have been having trouble downloading Firebase dictionaries and turning them into an array of objects.
What am I doing wrong with the syntax below? I've spent the last two days unsuccessfully trying to figure this out. The following gives me an index out of range error. Is this because the Firebase Dictionary hasn't finished downloading yet or is my for in loop sytax flawed? Perhaps both? Thanks.
// Array of Location Objects
var locationsArray:[Location] = [Location]()
var ref = Firebase(url: "<MYFIREBASEURL>")
var dictionaryOfRecommendations:[NSDictionary] = [NSDictionary]()
var currentlyConstructingLocation:Location = Location()
func getLocationData() {
let titleRef = self.ref.childByAppendingPath("events")
titleRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
var tempDict = [NSDictionary]()
for item in snapshot.children {
let child = item as! FDataSnapshot
let dict = child.value as! NSDictionary
tempDict.append(dict)
}
self.dictionaryOfRecommendations = tempDict
})
// Parse data from Firebase
// Loop through each dictionary and assign values to location object
var index:Int
for index in 0...dictionaryOfRecommendations.count {
// Current Json dictionary
let jsonDictionary:NSDictionary = self.dictionaryOfRecommendations[index]
self.currentlyConstructingLocation.title = jsonDictionary["title"] as! String!
self.currentlyConstructingLocation.locationsLatitudeArray = jsonDictionary["latitude"] as! Double
self.currentlyConstructingLocation.locationsLongitudeArray = jsonDictionary["longitude"] as! Double
// Append to Locations Array and start new Location
self.locationsArray.append(currentlyConstructingLocation)
self.currentlyConstructingLocation = Location()
}
// Notify the MainViewController that the Locations are ready.
...
}
Here's the updated correct code for the question above based on Jay's helpful guidance:
// Model to download location data for events.
//Firebase reference
var ref = Firebase(url: "<MYFIREBASEURL")
var locationsArray:[Location] = [Location]()
var dictionaryOfRecommendations:[NSDictionary] = [NSDictionary]()
var currentlyConstructingLocation:Location = Location()
func getLocationData() {
let titleRef = self.ref.childByAppendingPath("events")
titleRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
var tempDict = [NSDictionary]()
for item in snapshot.children {
let child = item as! FDataSnapshot
let dict = child.value as! NSDictionary
tempDict.append(dict)
}
self.dictionaryOfRecommendations = tempDict
self.ParseFirebaseData()
})
}
func ParseFirebaseData() {
// Parse data from Firebase
// Loop through each dictionary and assign values to location object
var index:Int
for index in 0...dictionaryOfRecommendations.count - 1 {
// Current Json dictionary
let jsonDictionary:NSDictionary = self.dictionaryOfRecommendations[index]
self.currentlyConstructingLocation.title = jsonDictionary["title"] as! String!
self.currentlyConstructingLocation.locationsLatitudeArray = jsonDictionary["latitude"] as! Double
self.currentlyConstructingLocation.locationsLongitudeArray = jsonDictionary["longitude"] as! Double
// Append to Locations Array and start new Location
self.locationsArray.append(currentlyConstructingLocation)
self.currentlyConstructingLocation = Location()
}
}
I am working on iOS project using Swift and I am trying to add a search bar to my TableViewController
This is my code:
the properties part:
var UserNamesArray: NSMutableArray = []
and
var filteredUsers = [UserNamesArray]()
When I add the previous line it give me an error: instance member 'UserNamesArray' cannot used on type 'MyTableVC' .
So this is the problem, what can I put instead of [UserNamesArray]?
viewDidLoad function:
ref.queryOrderedByChild("Job").queryEqualToValue("Programers")
.observeEventType(.Value, withBlock: { snapshot in
if let dict = snapshot.value as? NSMutableDictionary{
print("dict====== \(dict)")
for (key,value) in dict {
let mainDict = NSMutableDictionary()
mainDict.setObject(key, forKey: "userid")
if let dictnew = value as? NSMutableDictionary{
if let metname = dictnew["UserName"] as? String
{
mainDict.setObject(metname, forKey: "UserName")
}
if let metname = dictnew["Details"] as? String
{
mainDict.setObject(metname, forKey: "Details")
}
if let metname = dictnew["Email"] as? String
{
mainDict.setObject(metname, forKey: "Email")
}
}
print("mainDict========= \(mainDict)")
self.UserNamesArray.addObject(mainDict)
}
print("UserNamesArray ==== \(self.UserNamesArray)")
}
self.tableView.reloadData()
})
}
To make it clear, I took a screen shot of values of the variables:
1) dict 2) mainDict 3) UserNamesArray
Please, click here
That what my table view controller look like:
Here
I attempted to make it clear as I can I hope that someone can help.
Thanks in advance!
var filteredUsers = [UserNamesArray]() doesn't work because UserNamesArray is not a valid type, it's just an object.
For example in the syntax var foo = [Bar](), Bar is the type of the objects in the array foo.
So if filteredUsers is meant to hold an array of String objects then what you want is:
var filteredUsers = [String]()