How to continually compare the current time with a timestamp in firebase? - swift

Currently, there is an expiration date in the database. I would like the app to delete the post after the expiration date is passed, even while the user is currently using the app. Currently, the code checks the feed, collects the time, and if the time is less than the current time, the post is deleted from the feed. However, this does not continually compare the time; rather, it only compares the current time with the time in the database only when the value in the database updates or the app is restarted. How can I make it such that the app will continually compare the time interval since 1970 with the time in the database, even while the app is running? I.e even when the user is using the app, if the current time is greater than the expiration time it will delete it from the database?
func deletePosts() {
let userID = Auth.auth().currentUser?.uid
let deletionReference = Database.database().reference().child("feeds").child(userID!)
deletionReference.observe(.childAdded) { (snapshot) in
let feedPostID = snapshot.key
print(feedPostID)
let deletionPostTimeReference = Database.database().reference().child("postStatus").child(feedPostID)
deletionPostTimeReference.observe(.value, with: { (snapshot) in
print("damn")
if let value = snapshot.value as? NSDictionary {
print("shit")
let timeOfDeletion = value["timeOfDeletion"] as? Int ?? 0
print(timeOfDeletion)
if timeOfDeletion == 0 {
print("we aint deleting")
}
else {
print("time TO DELETE")
let date = Date()
let currentTime = UInt64(floor(date.timeIntervalSince1970 * 1000))
if currentTime >= timeOfDeletion {
deletionReference.child(feedPostID).removeValue()
if let index = self.postList.index(where: {$0.postID == feedPostID}) {
self.postList.remove(at: index)
self.orderedPostList = self.postList.sorted(by: { $0.revealDate! > $1.revealDate! })
self.tableView.reloadData()
}
}
}
}
})
}
}

Related

Pausing a while loop so Firebase Data can finish Pulling and appending to array

I need to wait for completion so that the data from cabinetComponents will finish pulling from firebase before continuing onto the next function. I tried implementing sleep and dispatch groups but I could not figure out a method to make it work. This is my code right now. How can I create a completion with Firebase or change this function to be async?
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
var cabinetComponents: Array<String> = Array()
var cabinetSelectionArray: Array<String> = Array(cabinetSelection)
var counter = 0
var indexP = 0
while counter < cabinetSelectionArray.count{
//Pulls the cabinet components from a specfic index from the array
cabinetComponents.removeAll()
//Array to the serial numbers
ref.child("Tested Cabinets").child(cabinetSelectionArray[indexP]).observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children{
let snap = child as! DataSnapshot
let key = snap.key
cabinetComponents.append("\(key)")
}
print("These are the cabinet components \(cabinetComponents)")
}
//Pause the loop to wait for cabinetSelectionArray to be populated !!!THIS IS WHERE I NEED THE CODE TO WAIT FOR THE ARRAY TO FINISH POPULATING!!!
print("This is the components array where it needs to be used \(cabinetComponents)")
self.ref.child("Tested Cabinets").child(cabinetSelectionArray[indexP]).child(cabinetComponents[indexP]).getData(completion: { error, snapshot in
guard error == nil else {
print(error!.localizedDescription)
return;
let partQuantity = snapshot?.value as? Int ;
}
let partQuantity = snapshot?.value as? Int ;
print("before database push\(counter)")
self.ref.child("Kits").child("Site Location: \(siteName)").child("Cabinets Used").child(cabinetSelectionArray[indexP]).child(cabinetComponents[indexP]).setValue(partQuantity)
indexP += 1
});
counter += 1
}
}

Swift: ARKit TimeInterval to Date

The currentFrame (ARFrame) of ARSession has a timestamp attribute of type TimeInterval which represents the uptime at the moment the frame has been captured.
I need to convert this TimeInterval to the current time domain of the device.
If my assumption about timestamp is correct, adding the kernel BootTime and timestamp together would give me the correct date.
Problem: Adding the kernel BootTime and timestamp together gives me an Date that is not correct. (depending on the device`s last boot time up to 2 days variance)
Current Code:
func kernelBootTime() -> Date {
var mib = [CTL_KERN, KERN_BOOTTIME]
var bootTime = timeval()
var bootTimeSize = MemoryLayout<timeval>.stride
if sysctl(&mib, UInt32(mib.count), &bootTime, &bootTimeSize, nil, 0) != 0 {
fatalError("Could not get boot time, errno: \(errno)")
}
return Date(timeIntervalSince1970: Double(bootTime.tv_sec) + Double(bootTime.tv_usec) / 1_000_000.0)
}
public func checkTime(_ session: ARSession) {
guard let frame = session.currentFrame else { return }
print(Date(timeInterval: frame.timestamp, since: kernelBootTime()))
}
I found the solution.
var refDate = Date.now - ProcessInfo.processInfo.systemUptime
gives you the date of the last device restart
public func checkTime(_ session: ARSession) {
guard let frame = session.currentFrame else { return }
print(Date(timeInterval: frame.timestamp, since: refDate))
}
prints the exact time when the image was taken. (in UTC time)

Instead of running a snapshot for all users, how do you set up multiple queries to limit the number of users sent to the device?

What I have: A snapshot of all users with a bunch of if statements that eventually returns an array of users that get displayed.
What I need: The array of end users to be used in a .query in the line preceding the snapshot.
Why do I need this: This line is so that the entire database of users is not run on the client.
More specifically, what do I need to query for: A) Users who have a child "caption"(timestamp) with a timestamp that is in today, AND, B) who are 3000 miles from the current user.
JSON of DB
"people" : {
"02PdiNpmW3MMyJt3qPuRyTpHLaw2" : {
"Coordinates" : {
"latitude" : -25.809620667034363,
"longitude" : 28.321706241781342
},
"PhotoPosts" : "https://firebasestorage.googleapis.com/v0/b/daylike-2f938.appspot.com/o/images%2F02PdiNpmW3MMyJt3qPuRyTpHLaw2%2FPhotoPosts?alt=media&token=24fee778-bcda-44e3-aa26-d7c2f8509740",
"caption" : 1602596281762, /// this is timestamp
"postID" : "02PdiNpmW3MMyJt3qPuRyTpHLaw2"
},
"e1" : “cvvvv666",
"e2" : "aol.com",
" "postID" : "0RnqWV7Gd9Z0bUW9nUvizMQOjK73",
"users" : "cvvvv666#aol.com"
},
.
var dict = CLLocation()
...
dict = CLLocation(latitude: lat, longitude: lon)
...
let thisUsersUid = Auth.auth().currentUser?.uid
//this line below is where the refArtists2 query should go. in other words send all users to device that meet the 2 if statements, which is represented by self.people.append(peopl)//
let refArtists2 = Database.database().reference().child("people").queryOrdered(byChild: "caption").queryEqual(toValue: ANY Timestamp in today).queryOrdered(byChild:Coordinates). queryEqual(toValue:ThoseCoordinates which make the distance to current user less than 3000 miles)
refArtists2.observe(DataEventType.value, with: { snapshot in
if snapshot.childrenCount>0{
self.people.removeAll()
for people in snapshot.children.allObjects as! [DataSnapshot] {
if people.key != thisUsersUid {
let peopleObject = people.value as? [String: AnyObject]
let peopleCoordinates = peopleObject?["Coordinates"] as? String
let peoplecaption = peopleObject?["caption"] as? Int //is timestamp
let peoplepostID = peopleObject?["postID"] as? String
let coordSnap = people.childSnapshot(forPath: "Coordinates")
guard let lat = coordSnap.childSnapshot(forPath: "latitude").value as? CLLocationDegrees else { return }
guard let lon = coordSnap.childSnapshot(forPath: "longitude").value as? CLLocationDegrees else { return }
let locCoord = CLLocation(latitude: lat, longitude: lon)
let coordSnap12 = people.childSnapshot(forPath: "caption").value as? Int ?? 0
let date = Date(timeIntervalSince1970: TimeInterval(coordSnap12)/1000.0)
//let secondsInDay = 86400
**if Calendar.current.isDateInToday(date)** {
let distance = locCoord.distance(from: self.dict)
print(distance, "distancexy")
**if distance/1609.344 < 3000**{
let peopl = Userx(Coordinates: peopleCoordinates, distance:distance, caption: peoplecaption, postID: peoplepostID)
self.people.append(peopl)
let d = people.key as! String
self.printPersonInfo(uid:d) ///////This is used to reload the data
} else {
print ("w")
}
} else {
print ("alphaaa")
}
}
print("aaaaaaaa", self.people.map {$0.distance})
}
self.people.sort { ($0.distance ?? 0) < ($1.distance ?? 0) } ////////This sorting with distance is used with returning the cell. people is used as uid array to return the cell.
}
})
} else {
print("no")
}
})
Ancillary caveat: the self.people.sort { ($0.distance ?? 0) < ($1.distance ?? 0) }sorting is important, so the queries should not impede that. I am a bit concerned with using queryOrdered in that it orders the array of users in the wrong order. If it does, a C) query should be: The order of the users must be with the closest users to the logged in user first. The furthest from the logged in user must go last in the array.
Another way of asking this would be: Instead of running a snapshot of all users, how do you query the snapshot's 'end result sort' when making the snapshot?
The timestamp is seconds since 1970
My attempt at the date query below. I took the code and tried to put the code that gets the date before the actual query(currently the code that gets the date is after the snapshot of all users).
var ppp: String! ////this should be the uid of all users in db
let people = Database.database().reference().child("people").child(self.ppp).child("captions")
people.observe(DataEventType.value, with: { snapshot in
let captionss = snapshot.value as? Int ?? 0
let date = Date(timeIntervalSince1970: TimeInterval(captionss)/1000.0)
let query1 = Database.database().reference().child("people").queryOrdered(byChild: "caption").where?(isDateInToday(date))
Edit: This answer is in Firestore, not Realtime Database. However, the concepts are the same.
The question is several questions in one; asking about distance, compound queries and how to query Firebase in general. I will post this answer to address the second two and distance queries are addressed in the comment to the question.
Once the query pattern is understood, they become easier and most importantly; it becomes more obvious that how your data is structured depends on what queries you want to run against that data.
Suppose we have a users collection with user documents - each documentId is the users uid
users
uid_0
name: "Leroy"
and then we have the posts for the users - each post contains some text, a timestamp of the post, the uid of the user that posted it, what the topic is and a url of a picture that appears in the post. Notice I am storing posts in a separate collection; why read in a bunch of user data when we only want to know about their post.
posts
post_id
postText: "pretty flowers"
postDate: "20201103"
postUrl: "www....."
postUid: "uid_0"
postTopic: "flowers"
Let suppose we want to get posts from today that are about flowers, and then also get the posters name and output who posted the message and what they said.
To do this we will need a compound query and then a subquery to retrieve the posters name as well.
func getTodaysPostsAboutFlowers() {
let postsCollection = self.db.collection("posts")
let query = postsCollection.whereField("postDate", isEqualTo: "20201103").whereField("postTopic", isEqualTo: "flowers")
query.getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = snapshot?.documents else { return }
for doc in docs {
let postText = doc.get("postText") as? String ?? "No text"
guard let postersUid = doc.get("postUid") as? String else { return }
self.outputPostTextAndUserName(withText: postText, andUid: postersUid)
}
})
}
The above performs a compound query on both the postDate field as the postTopic field.
The above then calls another function to retrieve the users name and output both the name and what they said
func outputPostTextAndUserName(withText: String, andUid: String) {
let usersCollection = self.db.collection("users")
let theUserDoc = usersCollection.document(andUid)
theUserDoc.getDocument(completion: { documentSnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
if let doc = documentSnapshot {
let postersName = doc.get("name") as? String ?? "No Name"
print("\(postersName) posted: \(withText)")
}
})
}
and the output
Leroy posted: pretty flowers
As you can see, there's no need to load all of the users, no need to iterate over results etc. Even if you have a billion users, this will only return a subset of that data which is a best practice when working with huge data sets; only get the data you're interested in.
Edit. The OP is asking about querying for nodes containing today. The simple solution is to have one child node containing a timestamp which would contains specific date data and then another child node just containing today data in YYYYMMDD format.
people
uid_x
timetamps: 9023490823498 //Date(timeIntervalSince1970:
todaystamp: "20201106" // yyyymmdd format
that makes querying for nodes that contain today very simple.

How to generate a randomString for every 1 hour in swift?

In my app I am we are sending TimeStamp along with parameters for API calls to get data from. I could use any random string to pass with the values but I am using TimeStamp because it will be different everytime so that I will get fresh data everytime without cashing. Now our requirement is to update TimeStamp every hour so that everytime when I make an API call instead of showing fresh data everytime, data will be updated every hour.
My API looks like this:
let url = "https://myurl.api.getthisapidata?id=\(someID)&nocache=\(timeStamp)"
Now in the place of TimeStamp I want to send some random string like "\(arc4random())". But I want this random string to be changed only after an hour so that I go to some other view or closes the app and opens it before an hour this random string should remain same.
I hope you understand what I am trying to convey.
TimeStamp extenstion:
extension Date {
var ticks: UInt64 {
let timeStamp = UInt64((self.timeIntervalSince1970 + 62_135_596_800) * 10_000_000)
return timeStamp
}
}
Usage:
print(Date().ticks)
Get current hour value from current date using component(_:from:) method. Then append a random string with it.
class RandomStringGenerator: NSObject {
static let shared = RandomStringGenerator()
var oldString:String {
get {
return UserDefaults.standard.string(forKey: "oldString") ?? ""
}
set {
UserDefaults.standard.set(newValue, forKey: "oldString")
}
}
func getString() -> String {
let random = Int64.random(in: 10_000_00..<10_000_000)
let hour = Calendar.current.component(.hour, from: Date())
if !oldString.hasPrefix("\(hour)") {
oldString = "\(hour)" + "\(random)"
}
return oldString
}
}

databaseReference.observe(DataEventType.value, with:{(DataSnapshot) not working properly all the time

func checkPaid(utilityId : String) -> Int{
var amount:String = ""
var status = 0
print("inside new function ")
print ("\(utilityId) inside new function ")
self.databaseRefPayment.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for payments in DataSnapshot.children.allObjects as! [DataSnapshot]{
var paymentsObject = payments.value as? NSDictionary
/*
if(paymentsObject!["month"] as! String == monthCheck && paymentsObject!["year"] as! String == monthCheck && paymentsObject!["utilityid"] as! String == utilityId as! String){ */
if(paymentsObject!["utilityId"] as! String == utilityId){
amount = paymentsObject!["amount"] as! String
print(amount)
print("Ypur program is working perfect")
status = 1
}
}
}
})
return status
}
The above function is filtering the data present in payments node based on the value for utilityId getting passed in the function . But the strange thing is observe(DataEventType.value, with:{(DataSnapshot) this event is not getting triggered all the time . Its just skipping that portion unnecessarily . I am very new to firebase and getting really mad with these kind of unpredicted behaviours . Please help me in this . feel free to ask for any clarifications .
The firebase executes firebase query functions in different thread , so after u call check paid(), it runs the checkpaid() firebase query in another thread,and it will return from the function , eventhough ur query is running in the background..so it will seem like,checkpaid() is not working , but actually it's running on another thread.
I think you first fetch all the required data from payment, and store it in a list , and then use that list to compare with utility.
Every time this function is called it adds/resets the Key-Value Observer for whichever child node you are observing it doesn't actually check the value unless it is changed. I believe it is your intention to call checkPaid(utilityId:) to check the child is 'paid' by some means. There is no need to add a KVO if you are directly reading the value for a single snapshot. consider the following:
func checkPaid(utilityId: String) -> Bool {
//Assume it is not paid if we cannot verify it.
var isPaid = false
//Create a new reference to Firebase Database
var ref: DatabaseReference!
ref = Database.database().reference().child(utilityId)
//Get the values for the child, test if it is paid or not.
ref.queryOrderedByValue().observeSingleEvent(of: .value) { (snapshot) in
if (snapshot.value is NSNull) {
print("No Child With \(utilityId) Exists")
} else {
//child with utilityId exists, in case multiple utilityId's exist with the same value..
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let values = child.value as? [String : AnyObject] {
let uid = child.key //utilityId
var month:String = ""
var year:String = ""
var amount:String = ""
//var amount:Double = 0.0
//get values from parent
if let m = values["month"] as? String {
month = m
}
if let y = values["year"] as? String {
year = y
}
if let a = values["amount"] as? String {
amount = a
}
/*
if let a = values["amount"] as? Double {
amount = a
}
*/
//??
if ((month == monthCheck) && (year == monthCheck)) {
isPaid = true
}
}
}
}
return isPaid
}
I am making one assumption here; that utilityId is the key for the child.
if you have parent nodes to utilityId you'll have to transverse those as well when you reference the database:
ref = Database.database().reference().child(utilities).child(utilityId) ..etc
If you need a KVO to update a local property I suggest adding/calling it in viewDidLoad, it's completion handler should take care of updating whichever properties are updated when they change in Firebase.