How do you retrieve userData through a sender in Swift 4? - swift

I can't seem to retrieve sent sender data without getting an error "Value of 'Any' has no subscripts".
It seems like a new error since this is the way I've always done it, and I can't find information on how to fix it.
let selector = #selector(self.updatePoint(sender:))
Timer.scheduledTimer(timeInterval: 1,
target: self,
selector: selector,
userInfo: ["index": 3, "tempPoint": tempPoints[3]],
repeats: false)
#objc func updatePoint(sender: Timer) {
guard let index = sender.userInfo?["index"] as? Int else {return} // Error: Value of 'Any' has no subscripts
...
}

userInfo is of type Any you need to cast it first to [String:Any]
guard let info = sender.userInfo as? [String:Any],let index = info["index"] as? Int else {return}

Related

Realtime update array from Firestore

I am trying to download information from a Firebase Firestore document that is then appended to an array in realtime. Everytime I add a document, delete or edit a document I would like the app to update and sync the array to match the data. I was able to retrieve the data with the following code:
database.collection("_Products").getDocuments(completion: { (snapshot, error) in
if error != nil {
print(error as Any)
}else{
for document in (snapshot?.documents)! {
let Name = document.data()["Item Name"] as! String
let Price = document.data()["Item Price"] as! String
let Number = document.data()["Item Number"] as! String
let Brand = document.data()["Item Brand"] as! String
let Quantity = document.data()["Quantity"] as! String
let Category = document.data()["Item Category"] as! String
DispatchQueue.main.async {
if instoreCheckOutArray.contains(where: {$0.Number == Number && $0.Brand == Brand}){
return
}else{
instoreCheckOutArray.append(checkOutArrayInfo(Brand: Brand, Name: Name, Number: Number, Price: Price, Quantity: Quantity, Category: Category))
self.searchForCoupon()
}
}
}
}
})
I then use the following code to run the function every second to fetch the data from the database:
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCart), userInfo: nil, repeats: true)
Running the previous code perfectly fetches me new data but I cannot get the app to update the existing array to match the database and when I remove a document from the database it stays in the array.
Thank you in advanced.
1.Declare scrollingTimer variable
var scrollingTimer = Timer()
2.Initialize scrollingTimer with your function
scrollingTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCart), userInfo: nil, repeats: true)
3.Fire the scrollingTimer(fire any where you want)
scrollingTimer.fire()
To stop the scrolling timer use the below line(Stop any where you want)
scrollingTimer.invalidate()
If any Please refer the link (DevelopersDocument)

How to get Count values when changes Happen in FireBase DataBase using Swift?

I am new to swift Programming and FireBase,I have Implemented Chat Application,Which I stored the Message Count using sender and Receiver ID ,In receiver side getting count Perfectly,But when new count is added in FireBase I want to get that new count, for that I used timer to call a function every 10 seconds,i am getting count perfectly ,But My problem is Timer running continuously, App getting Hang and slow,After sometimes I doesn't response, can anyone suggest me how to call the function every 10 seconds or how to use timer.
Here I have tried this code,
var timer = Timer()
override func viewWillAppear(_ animated: Bool) {
MessageCountingFunction()
}
func MessageCountingFunction(){
//getting count details
keyvalue.removeAllObjects()
countarray.removeAllObjects()
let ref = FIRDatabase.database().reference()
ref.child("CountDetails").child(AuthManager.User.id.value).observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
if let cakeSnapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for cakes in cakeSnapshot {
print(cakes)
if let cakeDictionary = cakes.value as? Dictionary <String, Any> {
print(cakeDictionary)
let count = cakeDictionary["Count"]
let key = cakes.key as String
//your class that takes a key as String and a Dictionary
print(count as Any)
print(key)
self.keyvalue.add(key)
self.countarray.add(count!)
}
}
DispatchQueue.global().sync {
print(self.keyvalue)
print(self.countarray)
self.tableView.reloadData()
}
}
})
DispatchQueue.main.async {
self.timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.MessageCountingFunction), userInfo: nil, repeats: true)
}
}
My json data structure for new message is :-
{ "status" : "sent", "sender" : "ayush ", "timeStamp" :
1525760473513 }
we are maintaining the status key for checking new message. after reading we are updating value of status key to read and looking for sent status for new message.
var channelRef: DatabaseReference = Database.database().reference().child(FIREBASE_CONSULTATION_DBKEY)
channelRef.child("channelKey").observe(DataEventType.value, with: { (snapshot) -> Void in // 1
if let channelData = snapshot.value as? Dictionary<String, AnyObject>{ // 2
let id = snapshot.key
self.chatMessageArray.removeAll()
for (key,obj) in channelData{
if status == "Sent"{
}
}
}
}
}

Swift Firebase read children of a child

So I am trying to read the children of the autoID children beneath "Recipes" below is a picture of my Firebase database and beneath that is the method that is supposed to retrieve the value of "Description" and "Name" and insert them into variables.
The error that I am currently getting when running the app is this:
Could not cast value of type '__NSCFString' (0x10ad2afb8) to 'NSDictionary' (0x10ad2bfa8).
ref = Database.database().reference()
databaseHandle = ref?.child("Recipes").observe(.childAdded) { (snapshot) in
for snap in snapshot.children
{
let recipeSnap = snap as! DataSnapshot
let recipeID = recipeSnap.key
let dict = recipeSnap.value as! [String:AnyObject]
let recipeName = dict["Name"] as! String
let recipeDescription = dict["Description"] as! String
print("key = \(recipeID) and name = \(recipeName) and description = \(recipeDescription)")
}
}
The print statement is just there for testing.
Try the following and let me know if it works now:
// SEARCHES FOR SHARING CODE IN DATABASE (ONLINE)
let parentRef = Database.database().reference().child("Recipes")
parentRef.observeSingleEvent(of: .value, with: { snapshot in
// SHOWING WHATEVER WAS RECEIVED FROM THE SERVER JUST AS A CONFIRMATION. FEEL FREE TO DELETE THIS LINE.
print(snapshot)
// PROCESSES VALUES RECEIVED FROM SERVER
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
// DATA WAS FOUND
for user_child in (snapshot.children) {
let user_snap = user_child as! DataSnapshot
let dict = user_snap.value as! [String: String?]
// DEFINE VARIABLES FOR LABELS
let recipeName = dict["Name"] as? String
let recipeDescription = dict["Description"] as? String
print("– – – Data for the recipe \(recipeName) with the description \(recipeDescription) was found successfully! – – –")
}
}
}
If you only want to retrieve the name and description for one specific recipe, you should change the third line to
parentRef.queryEqual(toValue:DefineWhatToSearchForHere).observeSingleEvent(of: .value, with: { snapshot in
If you constantly want to update to reflect changes, you can either call this function every x seconds using a timer and adding it to override func viewDidLoad() such as
time = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(ViewController.updateFBData), userInfo: nil, repeats: true)
after creating a function called func updateFBData() in which you do whatever you want to do to get new data (see above) and calling it in a defined timeInterval
or you can do what Attila Hegedüs in this excellent tutorial.

fatal error: unexpectedly found nil while unwrapping an Optional value. But I can't find nill value in my statement

I've got an error on the let myValue line:
#IBAction func CAttamaran(_ sender: Any) {
// error happens here
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
UserDefaults.standard.set(myValue, forKey:"Shared room")
UserDefaults.standard.synchronize()
if let myOutput2: AnyObject = UserDefaults.standard.object(forKey: "Shared room") as AnyObject? {
// self.appDelegate.propertylabel = "\(myOutput2)" as NSString!
let secondViewController1 = self.storyboard!.instantiateViewController(withIdentifier: "propertyvc")
self.present(secondViewController1, animated: true, completion: nil)
print("property_id = \(myOutput2)")
}
}
This line
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
has three unwrappings in it, any one of which could be your problem
sender may not be castable as AnyObject
titleLabel might be nil especially if sender is an Objective-C object that doesn't have a titleLabel property.
titleLabel.text might be nil.
If you want to find out what the problem is, you need to do the unwrappings one at a time e.g.
guard let sender = sender as WhateverTypeYouThinkItShouldBe else { fatalError("sender is the wrong type") }
guard let titleLabel = sender.titleLabel else { fatalError("Title label is nil") }
if let text = titleLabel.text
{
// Do whatever you need to do
}
else
{
// There is no text in the label
}
The line of code you are pointing on, has a lot of issues:
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
First and first, force unwrapping like that is evil. It's the biggest source for possible crashes.
Why do you try to cast to AnyObject? That one doesn't have titleLabel property, so this wrong.
Why do you cast the text to NSString? Didn't notice any particular NSString API usage.
if let myOutput2: AnyObject = UserDefaults.standard.object(forKey: "Shared room") as AnyObject? {
You read again the same value from UserDefaults that you just saved.
If you rewrite your code in more Swift friendly way, it would be better:
Here is an example of your re-written function:
#IBAction func CAttamaran(_ sender: UIButton) {
guard let myValue = sender.titleLabel?.text else {
// Handle the edge case here
return
}
UserDefaults.standard.set(myValue, forKey:"Shared room")
UserDefaults.standard.synchronize()
guard let storyboard = self.storyboard else {
// Handle the edge case here
return
}
let secondViewController1 = storyboard.instantiateViewController(withIdentifier: "propertyvc")
self.present(secondViewController1, animated: true, completion: nil)
print("property_id = \(myValue)")
}
Any exclamation mark can cause that exception.
in your case
// problematic line is
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as
NSString
as we can see there is multiple chance of getting nil value
like
sender may not be castable as AnyObject
titleLabel might be nil
especially if sender is an Objective-C object that doesn't have a
titleLabel property.
titleLabel.text might be nil.
Solution for this is use guard statement like below
#IBAction func CAttamaran(_ sender: UIButton) {
guard let myValue = sender.titleLabel?.text else {
// Handle the edge case here
return
}
guard let storyboard = self.storyboard else {
// Handle the edge case here
return
}
// Rest of code
}

Comparing non optional value leads to error

I am on point where I gotta compare non optional value with nil. But I can't do it because Xcode says:
Comparing non-optional value of type 'Int' to nil always returns false
So I created Struct and then made variable: var products: [Product] = []
How I am able to compare it with nil?:
if products[indexPath.row].snusPortions == nil
{
cell.snusPortionsAmountLabel.text = "N/A"
}else
{
cell.snusPortionsAmountLabel.text = String(products[indexPath.row].snusPortions)
}
I've assigned values to them like this:
let ref = FIRDatabase.database().reference().child("Snuses").queryOrdered(byChild: "Brand").queryEqual(toValue: brandName)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
let enumerator = snapshot.children
while let thisProduct = enumerator.nextObject() as? FIRDataSnapshot
{
print(thisProduct.value) // So I may see what the data is like and know how to extract it
// Chances are you'd have to create a dictionary
let thisProductDict = thisProduct.value as! [String:AnyObject]
let productName = thisProductDict["Products"] as! String
let snusPortions = thisProductDict["PortionsCan"] as? Int
let productObject = Product(snusProductTitle: productName, snusNicotine: snusNicotine, snusPortions: snusPortions!, snusFlavor: snusFlavor, snusWeight: snusWeight!, snusShippingWeight: snusShippingWeight, snusProductImageURL: productURL)
self.products.append(productObject)
print(self.products)
}
self.tableView.reloadData()
}
})
This is Product struct:
struct Product {
var snusProductTitle: String
init()
{
snusProductTitle = ""
}
init(snusProductTitle: String){
self.snusProductTitle = snusProductTitle
}
}
While testing it says snusPortions is nil but I said to make it "N/A" if it is nil, why?
It sounds like you are confusing yourself between the local variable snusPortions and the Product property snusPortions.
In your Product definition, the property snusPortions is an Int. It can never be nil. Hence, in this code:
if products[indexPath.row].snusPortions == nil
... this Product's snusPortions will never be nil, and we will never set the text to "N/A".
Now let's look at your other code:
let snusPortions = thisProductDict["PortionsCan"] as? Int
This is a completely different snusPortions. It can be nil, namely, if thisProductDict lacks a "PortionsCan" key or if its value is not castable to Int.