I am trying to edit my code that I created that is posting text to firebase. I was originally only allowing 8 pictures and descriptions no more, no less. I am now refactoring so the user can pick any number up to 8. I have the checks for the photos working and it uploads the amount picked. I have put checks in place to see if there is a picture or not and if there is it will show the text box to add a description otherwise it is hidden. My problem is I have a dictionary that posts to firebase that is posting 8 descriptions so if i was to create 2 it would fill the other 6 with "" is there a way to check that if an image is nil like I do above then it adds to the dictionary to upload. My function is below..
func postToFirebase() {
let post: Dictionary<String, AnyObject> = [
"photoInfo1": photoInfo1.text as AnyObject,
"photoInfo2": photoInfo2.text as AnyObject,
"photoInfo3": photoInfo3.text as AnyObject,
"photoInfo4": photoInfo4.text as AnyObject,
"photoInfo5": photoInfo5.text as AnyObject,
"photoInfo6": photoInfo6.text as AnyObject,
"photoInfo7": photoInfo7.text as AnyObject,
"photoInfo8": photoInfo8.text as AnyObject
]
let firebasePost = DataService.ds.REF_BASE.child("posts").child("\(stringPassed)")
firebasePost.updateChildValues(post)
}
The check I'm doing before this to hide the boxes are..
if passImage2 == nil {
photo2.isHidden = true
photoInfo2.isHidden = true
}
and so on.
I was hoping I could do a check to see if the photo is not nil then if so append the "post" dictionary rather than have them all already coded in.
If you don't need your photoInfo to be continuos in numbering, you can use
let filtered = post.filter { (key, value) -> Bool in
guard let stringValue = value as? String else { return false }
return stringValue != ""
}
If you need it to be continuos you can filter an array of label texts and then with using of enumarated and reduce, you can create a new dictionary.
Related
I have a function downloading JSON. It does it fine however in some cases a object may not contain a certain key. On that occasion it still trys to add a value to the array. I want to make it so if the key is not present then a value of nil is added to the array. Would appreciate it if someone could help. Thanks alot.
if let link = itemDict.value(forKey: "link") {
if link != nil {
self.linkArray.append(link as! String)
}
}
Just do not use the force unwrapping. Also your if let is wrong for what you want to achieve. Copy paste version:
let link = itemDict.value(forKey: "link") as? String
self.linkArray.append(link)
I have a dictionary in which I'm adding values like so...
var mydictionary = ["id": "", "quantity": "","sellingPrice":""] as [String : Any]
dictionary["id"] = product?.id
dictionary["quantity"] = product?.quantity
dictionary["sellingPrice"] = product?.theRate
And these values I added to an array like so...
self.arrayOfDictionary.append(mydictionary)
But if arrayOfDictionary already contains mydictionary, I don't want to add it. Else, I want to add it.
The basic idea here is to add data from collection view items to array of dictionary. When I click on the buttons that I have on each collection view item the data on it is added to an array of dict. while at the same time showing those data in a tableviewcell. But when I navigate back from the tableview & visit the collectionview items again and click on some other collecn.view item, so as to add them to the array of dictionary as before, then the item that was added initially to the array of dictionary gets added again. This has to be somehow prevented.
As suggested by another SO user something like this was tried to prevent this duplication...
if self.arrayOfDictionary.contains(where: { (dict) -> Bool in
"\(dict["id"] ?? "")" != "\(dictionary["id"] ?? "")"}) {
self.arrayOfDictionary.append(dictionary)
}
But this doesn't seem to work. With this nothing is added to the array and its totally empty. Hope somebody can help...
Try this code to avoid duplication
I hope "id" value will be unique in your dictionary.
var mydictionary = ["id": "1", "quantity": "","sellingPrice":""] as [String : Any]
var arrayOfDictionary = [Dictionary<String, Any>]() //declare this globally
let arrValue = arrayOfDictionary.filter{ (($0["id"]!) as! String).range(of: mydictionary["id"]! as! String, options: [.diacriticInsensitive, .caseInsensitive]) != nil }
if arrValue.count == 0 {
arrayOfDictionary.append(mydictionary)
}
I've got better idea then every time you perform loop to check unique ness.
Maintain one Bool array of same size of your collectionView Items array with predefined false values each.
When you click on button of collection View item, change flag of Bool array with same index. And simultaneously you can disable the button also(if you want). Otherwise whenever user clicks on button, just check flag from Bool array and add Dictionary to new array as needed.
Here, your new array will be performed and you will same process and time of looping also.
One way to approach the problem could be to construct a structure which contains product details:
/// Details Of A Product
struct ProductDetails{
var id: String!
var quantity: Int!
var sellingPrice: Int!
}
Then create a dictionary which stores the product details with the key being the "ID" e.g:
var products = [String: ProductDetails]()
You could then create a product like so:
let productA = ProductDetails(id: "1", quantity: 100, sellingPrice: 10)
To add a unique product to your dictionary you could use a function like this:
/// Adds A Product To The Products Dictionary
///
/// - Parameter product: ProductDetails
func addProductDetails(_ product: ProductDetails){
//1. If A Product Exists Ignore It
if products[product.id] != nil{
print("Product With ID \(product.id!) Already Exists")
}else{
//2. It Doesn't Exist So Add It To The Dictionary
products[product.id] = product
}
}
I tested this quickly and it won't allow products which have duplicate ID's. although of course you could change the parameter as needed.
if let name = property["Name"] as? String,
let latitude = property["Latitude"] as? NSNumber,
let longitude = property["Longitude"] as? NSNumber,
let image = property["Image"] as? String {
// To learn how to read data from plist file, check out our Saving Data
// in iOS Video Tutorial series.
// Code goes here
}
When I converted my code to current swift version, I am facing 'Type any has no subscript error'. I am fairly new to swift and stuck at this point. Saw other related posts, but no where I could find a solution for this. Please help.
I poured over the many questions with duplicate titles and couldn't find one that clearly covered your case, so I'm going to explain what is going on.
Your variable property is clearly declared as type Any, which is a variable that can hold any type. The error message is telling you that you can't use subscripting [] with it. It is clear that you believe that it is a Dictionary with a key that is a String and a value that can be anything. In Swift 3, anything is typed Any. So your property is expected to be [String : Any]. If you cast property to [String : Any], then you will be able to use [] with it.
The first thing you should do is attempt to cast property to [String : Any] and then proceed if that works:
if let property = property as? [String : Any],
let name = property["Name"] as? String,
let latitude = property["Latitude"] as? NSNumber,
let longitude = property["Longitude"] as? NSNumber,
let image = property["Image"] as? String {
// To learn how to read data from plist file, check out our Saving Data
// in iOS Video Tutorial series.
// Code goes here
}
I get JSON response from the web and I successfully printing items out on the console using below code:
for game in listOfRecentGames {
if let statInfoForTheGame = game["stats"] as? [String : String] {
var text = statInfoForTheGame["info"]
}
}
There are multiple number of games in listOfRecentGames. I get a string value of information for each game and I would like to show every of them in one UITextView. How can I achieve this? I was thinking about putting in array and printing out in UITextView but not sure.
You actually won't need an array to do that. You already have the textView's existing text, so you just can append the new text to the existing one.
Code
let listOfRecentgames = [
["stats": ["info": "Something1"]],
["stats": ["info": "Something2"]],
["stats": ["info": "Something3"]]
]
var textViewText = yourTextView.text
for game in listOfRecentgames {
if let statInfoForTheGame = game["stats"] {
if let text = statInfoForTheGame["info"] {
textFieldText.appendContentsOf(" \(text)")
}
}
}
// Now you have the whole information from each game so just bind it to the textView
yourTextView.text = textViewText
Playground output
I would like to save and retrieve features to and from Firebase into a TableView.
The child I would like to save them under is the uid (unique user id)
so a feature would look like this in the database:
Firebase database
The ideal situation, is how the "derde" is saved, so the uid as a key and "derde" as the value.
#IBAction func saveButtonPressed(sender: AnyObject) {
let featureContents = addFeatureTextField.text
if featureContents != "" {
// Build the new Feature.
let newFeature: String = featureContents!
let ref = DataService.dataService.FEATURE_REF.childByAppendingPath(uid)
ref.setValue(newFeature)
where uid is a String, retrieved from authdata somewhere else in the code.
If I save it like this, it saves it to the specific uid path. If I want to add another feature by clicking on the + in the TableViewController, it saves it to the same path, so the Firebase database is updated with the new value and so instead of two features you only end up with one updated feature.
You can prevent this by working with the chilByAutoId() method, to save a list of items. The code would look like this:
#IBAction func saveButtonPressed(sender: AnyObject) {
let featureContents = addFeatureTextField.text
if featureContents != "" {
// Build the new Feature.
let newFeature: String = featureContents!
let ref = DataService.dataService.FEATURE_REF.childByAutoId().childByAppendingPath(uid)
ref.setValue(newFeature)
via this way, a feature is saved, as you can see in the above image at: "vierde"
This allows you to save multiple features with all the same uid, but different autoId.
But, if I save it like this, my tableView stays empty. The TableViewController is like this:
DataService.dataService.FEATURE_REF.observeEventType(.Value, withBlock: { snapshot in
// The snapshot is a current look at our features data.
print("The features in the tableView should be \(snapshot.value)")
self.features = []
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
for snap in snapshots {
// Make our features array for the tableView.
if let postDictionary = snap.value as? String {
print("All in")
let key = snap.key
let feature = Feature(key: key, value: postDictionary)
// Items are returned chronologically, but it's more fun with the newest features first.
self.features.insert(feature, atIndex: 0)
}
}
}
// Be sure that the tableView updates when there is new data.
self.tableView.reloadData()
})
}
Problem lies in this code: if let postDictionary = snap.value as? String {
This conditional binding does not succeed, because the value is not a String, but the autoId key has no value, only the child under it which is the uid has a value "vierde"
Two possible solutions which I am asking you guys:
1) How can I save multiple features with the same uid without using the autoId?
2) If I am obliged to use the autoId, how can I make sure it observes the value of the uid key under the autoId, instead of the non existing value of the autoId.
Thanks for your help!
I think the answer to the question is to build a dictionary out of the key:value pairs of data and store that as a child of your uid node
let featureDict = [ "feature_0": "cool feature", "feature_1": "great feature"]
let ref = DataService.dataService.FEATURE_REF.childByAppendingPath(uid)
ref.setValue(featureDict)
results in
the_uid
feature_0: "cool feature"
feature_1: "great feature"
The limitation here is the key's names, and then the ability to add even more data about each feature.
Here's a potentially better option
the_uid
auto_id_0
feature_name: #"cool feature"
summary: "Everything you'd ever want to know about this feature"
auto_id_1
feature_name: #"great feature"
summary: "Info about this great feature"
The auto_id_x is generated by autoId and allows you to add however many features you want, change their names and summaries. etc. The children of each auto_id_x are (or could be) stored in a dictionary and saved per the above example.