Sending and receiving push notification swift - ios

I created a small messenger app using Parse as my database in swift. I wanted each time any user sends a message only other users within 10 meter to get the notification. The app is working but the push notification is not. I did some research it looks like my code is similar to the one i found but still not working. Please help me. Thank you
var CurrentLocation : PFGeoPoint = PFGeoPoint(latitude: 44.6854, longitude: -73.873) // assume the current user is here
let userQuery = PFUser.query()
userQuery?.whereKey("Location", nearGeoPoint: CurrentLocation, withinMiles: 10.0)
let pushQuery = PFInstallation.query()
pushQuery?.whereKey("username", matchesQuery: userQuery!)
let push = PFPush()
push.setQuery(pushQuery)
push.setMessage(" New message")
push.sendPushInBackground()

Your problem is the line pushQuery?.whereKey("username", matchesQuery: userQuery!). According to the parse docs Warning: This only works where the key’s values are PFObjects or arrays of PFObjects. Read more here: https://parse.com/docs/osx/api/Classes/PFQuery.html#//api/name/whereKey:matchesQuery
Instead change it to chain the query by first performing the first query and then using the strings of the userIds for the second query which is performed inside the first's completionBlock.
Update
Also, just as a side note you are not abiding by the camel case rules of swift and also the rules set out by Parse. You should follow conventions. (See my code for the correct case for keys in Parse and variable names).
Example:
var currentLocation : PFGeoPoint = PFGeoPoint(latitude: 44.6854, longitude: -73.873) // assume the current user is here
let userQuery = PFUser.query()
userQuery?.whereKey("location", nearGeoPoint: currentLocation, withinMiles: 10.0) // Note I changed Location to location
userQuery?.findObjectsInBackground({ results, error in
let usernames = (results as! [PFObject]).map { $0.username }
let pushQuery = PFInstallation.query()
pushQuery?.whereKey("username", containedIn: usernames)
let push = PFPush()
push.setQuery(pushQuery)
push.setMessage("New message")
push.sendPushInBackground()
})
Also note that you may need to change your structure because the docs for PFInstallation.query note that you must use one of three query parameters and you are using none (you might have to save the installation object id to a field on the user. Then instead of making an array with username make an array with the installation object ids and query PFInstallation like that. However, this may still work so try it first, you never know.

Related

iOS Storing/Updating and retrieving location data from user document Firebase Firestore

we recently started developing an uber like app for iOS using Firebase Firestore and we need to store and constantly update the user's location on firebase and make it so that the other clients can retrieve it as well.
We are trying to store the user location in Users own document on firestore database, such like:
Collections > Users > "AutoGeneratedDocument" > UserLocation
with two key values latitude and longitude.
We've made some progress with the code, it writes the location data when we explicitly input a document path to it but we still don't know how to run a query and find the user's own document.
In order to save the location data to the users own document we need to be able to get the auto generated user document path for the currently signed in user.
So far we've come up with this:
func updateUserLocation() {
let db = Firestore.firestore()
let locman = CLLocationManager()
locman.requestWhenInUseAuthorization()
var loc:CLLocation!
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse || CLLocationManager.authorizationStatus() == .authorizedAlways{
loc = locman.location
}
let lat:Double = loc.coordinate.latitude
let long:Double = loc.coordinate.longitude
let geo = GeoPoint.init(latitude: lat, longitude: long)
let currentUID = currentUserUID
let val = db.collection("users").whereField("uid", isEqualTo: currentUserUID)
db.collection("users").document(val).updateData(["currentUserLocation" : geo])
}
We expect to get the path to the document which contains the signed in user's UID then store the user location key values in that specific document. But with our code, in the last line the document(val) part expects a string but our output with the "whereField" method apparently is a query.
You're going through users twice:
db.collection("users").whereField("uid", isEqualTo: currentUserUID).updateData(["currentUserLocation" : geo])
You can find many useful tips, tutorials on the official website.
Perform simple and compound queries in Cloud Firestore

CoreData Predicate with Function Argument

I am attempting to include a function in a Predicate definition. Is this possible?
Let's say you have a Core Data entity of Places with attributes for
latitude and longitude.
I would like to add annotations to a mapview of those Places within a
specified distance from the user location. I can, of course, loop through the entire database and calculate the distance between each Place and the
user location but I will have about 35000 places and it would seem that
it would be more efficient to have a predicate in the
fetchedResultsController setup.
I tried the code below but I receive an error message of "Problem with
subpredicate BLOCKPREDICATE(0x2808237b0) with userInfo of (null)"
func myDistanceFilter(myLat : CLLocationDegrees, myLong : CLLocationDegrees, cdLat : CLLocationDegrees, cdLong : CLLocationDegrees) -> Bool {
let myLocation = CLLocation(latitude: myLat, longitude: myLong)
let cdLocation = CLLocation(latitude: cdLat, longitude: cdLong)
if myLocation.distance(from: cdLocation) < 5000.0 {
return true
} else {
return false
}
}//myDistancePredicate
And inside the fetchedResultsController:
let distancePredicate = NSPredicate {_,_ in self.myDistanceFilter(myLat: 37.774929, myLong: -122.419418, cdLat: 38.0, cdLong: -122.0)}
If it is possible to have a block/function inside a predicate how do you get a reference to an attribute for the entity object being evaluated?
Any guidance would be appreciated.
An additional observation for anyone else struggling with a similar issue.
Considering the suggestions of pbasdf and Jerry above, at least in my case, there is no reason why a region has to be round. I'll craft a name indicating a nearly rectangular area. I ran some tests with latitude and longitude values. These latitude and longitude values can be scaled to a rectangle enclosing a circular radius specified by the user. One degree of latitude is about 69 miles and one degree of longitude at the latitude of Chicago is about 51 miles. I used the following:
var myUserLatitude : CLLocationDegrees!
var myUserLongitude : CLLocationDegrees!
In the init file for the view:
guard let userLatitude = locationManager.location?.coordinate.latitude else {return}
guard let userLongitude = locationManager.location?.coordinate.longitude else {return}
myUserLatitude = userLatitude
myUserLongitude = userLongitude
Inside the fetchedResultsController variable creation:
let latitudeMinPredicate = NSPredicate(format: "latitude >= %lf", myUserLatitude - 1.0)
let latitudeMaxPredicate = NSPredicate(format: "latitude <= %lf", myUserLatitude + 1.0)
let longitudeMinPredicate = NSPredicate(format: "longitude >= %lf", myUserLongitude - 1.0)
let longitudeMaxPredicate = NSPredicate(format: "longitude <= %lf", myUserLongitude + 1.0)
var compoundPredicate = NSCompoundPredicate()
compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [anotherUnrelatedPredicate,latitudeMinPredicate,latitudeMaxPredicate,longitudeMinPredicate, longitudeMaxPredicate])
fetchRequest.predicate = compoundPredicate
Obviously I will create another property to scale the 1.0 value per the user desired region. Initial tests seem to work and best of all I can't believe how fast it it. Literally, the tableView is populated by the time the viewController segues to the enclosing mapView.
Well, yes it is possible. NSPredicate does have +predicateWithBlock: and init(block:).
However if you scroll down on that page you see the bad news:
Core Data supports block-based predicates in the in-memory and atomic stores, but not in the SQLite-based store.
So, whether you use an in-memory store, or do the filtering in code, either way you need to bring these 35,000 items into memory, and performance of a brute force approach will be poor.
There is a point of complexity at which SQL is no longer appropriate – you get better performance with real code. I think your requirement is far beyond that point. This will be an interesting computer science project in optimization. You need to do some pre-computing, analagous to adding an index to your database. Consider adding a region attribute to your place entities, then write your predicate to fetch all places within the target location's region and all immediate neighbors. Then filter through those candidates in code. I'm sure this has been done by others – think of cells in a cell phone network – but Core Data is not going to give you this for free.

Receive limited query with custom start point

I'm building a basic chat app with swift for iOS with firebase realtime database.
The Messages are observed with a limit for the least 10.
Now, I want to implement the functionality of loading earlier send messages. Currently I'm trying to achieve this by using this function:
let query = threadRef.child("messages").queryOrderedByKey().queryStarting(atValue: "2").queryLimited(toLast: 2)
Which returns this query:
(/vYhNJ3nNQlSEEXWaJAtPLhikIZi1/messages {
i = ".key";
l = 2;
sp = 2;
vf = r;
})
And this should give me the data:
query.observeSingleEvent(of: .value, with: { (snap) in
But it just limits the query and not set the start point to a specific position.
Here is the firebase database structure:
messages
-Kgzb3_b26CnkTDglNd8
date:
senderId:
senderName:
text:
-Kgzb4Qip6_jQdKRWFey
-Kgzb4ha0KZkLZeBIaxW
-Kgzb577KlNKOHxsQo9W
-Kgzb5cqIVMhRmU019Jf
Anyone have an idea on how to implement a feature like that?
Okay I finally found a way to do what I wanted.
First of all I misunderstood the way to access data from Firebase.
This is now how I get the query:
let indexValue = messages.first?.fireBaseKey
let query = messageRef.queryOrderedByKey().queryEnding(atValue:indexValue).queryLimited(toLast: 3)
1) get the FireBase key I previously saved to my custom chat messages
2) construct the query:
order it by key
set the ending to oldest message
limit the array to query to desired length
Then to actually get the query I used:
query.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children.dropLast().reversed() {
let fireSnap = (child as! FIRDataSnapshot)
//do stuff with data
}
})
1) get the query as a single event
2) iterate over children and I needed to dropLast() to make sure I don't have any duplicated messages and reverse it to get the correct order.
3) cast the current child as a FIRDataSnapshot to access the data
Since I couldn't find a simple example for this so I thought I leave my solution here incase other people running into the same problem.

iOS Swift: retrieve an entry from the database for the user currently logged in

How do I retrieve a value (other than username and user id, which seem easier to get) for the current user from the database.
Ironically, I can set the value as follows and that works just fine:
let databaseRef = FIRDatabase.database().reference()
userID = (FIRAuth.auth()?.currentUser?.uid)! as String
databaseRef.child("users").child(userID!).child("TermCond").setValue("Yes")
But for the life of me I cannot work out what to put instead of setValue if I simply want to retrieve the current TermCond value. I thought just using value as for example in
let DesiredValue = databaseRef.child("users").child(userID!).child("TermCond").value as? String
Would suffice, but nothing works. I am confused why retrieving the value should be more difficult than setting it.
To "read" a value from Firebase, you need to add a reference listener that gets called every time that value changes.
In your case, that could be something like:
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("users").child(userID!).child("TermCond").observe(FIRDataEventType.value, with: { (snapshot) in
let desiredValue = snapshot.value as? String
})
This block of code will get triggered every time your value changes. If you only want to read it once, you can use observeSingleEvent:of:with instead of observe:with.
This is as described in the Firebase documentation: https://firebase.google.com/docs/database/ios/read-and-write
I recommend you read their entire Documentation to get an idea of how Firebase works, as it is very different from traditional databases.
I can also recommend the following tutorial if you'd like to learn a bit more about the Firebase Database and how it works: https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2
I've solved this now (based on Aleksander's reply). The way I did it is as follows.
databaseRef.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
self.desiredValue = value?["TermCond"] as? String ?? ""
self.LabelToShow.text = self.desiredValue!
}) { (error) in
print(error.localizedDescription)
}
This works absolutely fine and shows the value of TermCond in the LabelToShow on my iOS screen.

Why Parse push.setQuery not working on Swift?

I'm trying to send a notification to the owner of a post when another user taps the "favorite" button, but I'm receiving an error:
Can't set both the query and channel (s) properties.
What can it be? My current code:
let query = PFInstallation.query()
if let query = query {
query.whereKey("installationUser", equalTo: self.job!["owner"]! as! String)
let push = PFPush()
push.setChannel("Global")
push.setMessage("Some cool text here")
push.setQuery(query)
push.sendPushInBackground()
}
The app crashes whenever I execute the line push.setQuery(query)
Remove the line push.setChannel("Global")

Resources