I have a messaging app that I built, and I want to display the city in which a user is using my app from. The app works fine collecting and displaying the location data, however it only stores one instance of location data at a time. For example, if I post to the app from Seattle the app will say I am posting from Seattle. However if someone else posts to the app from New York, Seattle's location data will be overwritten and it will be as though every user is in New York. This is obviously a significant problem.
I thought I could use a Firebase database (which I am already using to handle messaging) to store the location data for each unique user, and then retrieve it from the database when I am posting but I have been trying to find a solution to my problem with no success.
Once again, I am having no problem collecting location data, I just want to store a unique location for each unique user.
Here is some of my code below (Note: Sorry it is so long, but I didn't want to omit any code that could possibly help somebody answer my question):
class ChatViewController: JSQMessagesViewController, CLLocationManagerDelegate {
// MARK: Properties
//Location
var city: String = ""
var state: String = ""
var country: String = ""
var locationManager = CLLocationManager()
func getLocation() -> String {
if country == ("United States") {
return (self.city + ", " + self.state)
}
else {
return (self.city + ", " + self.state + ", " + self.country)
}
}
//Firebase
var rootRef = FIRDatabase.database().reference()
var messageRef: FIRDatabaseReference!
var userLocation: FIRDatabaseReference!
//JSQMessages
var messages = [JSQMessage]()
var outgoingBubbleImageView: JSQMessagesBubbleImage!
var incomingBubbleImageView: JSQMessagesBubbleImage!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
//collect user's location
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestLocation()
locationManager.startUpdatingLocation()
}
title = "Group Chat"
setupBubbles()
// No avatars
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSizeZero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero
// Remove file upload icon
self.inputToolbar.contentView.leftBarButtonItem = nil;
messageRef = rootRef.child("messages")
userLocation = rootRef.child("locations")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
observeMessages()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//--- CLGeocode to get address of current location ---//
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if let pm = placemarks?.first
{
self.displayLocationInfo(pm)
}
})
}
func displayLocationInfo(placemark: CLPlacemark?)
{
if let containsPlacemark = placemark
{
self.city = (containsPlacemark.locality != nil) ? containsPlacemark.locality! : ""
self.state = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea! : ""
self.country = (containsPlacemark.country != nil) ? containsPlacemark.country! : ""
print(getLocation())
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error while updating location " + error.localizedDescription)
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = messages[indexPath.item] // 1
if message.senderId == senderId { // 2
return outgoingBubbleImageView
} else { // 3
return incomingBubbleImageView
}
}
override func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {
return nil
}
private func setupBubbles() {
let factory = JSQMessagesBubbleImageFactory()
outgoingBubbleImageView = factory.outgoingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleBlueColor())
incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleLightGrayColor())
}
func addMessage(id: String, text: String) {
let message = JSQMessage(senderId: id, displayName: "", text: text)
messages.append(message)
}
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAtIndexPath: indexPath)
as! JSQMessagesCollectionViewCell
let message = messages[indexPath.item]
if message.senderId == senderId {
cell.textView!.textColor = UIColor.whiteColor()
} else {
cell.textView!.textColor = UIColor.blackColor()
}
return cell
}
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!,
senderDisplayName: String!, date: NSDate!) {
let itemRef = messageRef.childByAutoId() // 1
let messageItem = [ // 2
"text": text,
"senderId": senderId
]
itemRef.setValue(messageItem) // 3
// Start storing location data
let locRef = userLocation.childByAutoId()
let locItem = [
"location": getLocation()
]
// Set location data in Firebase
locRef.setValue(locItem)
// 4
JSQSystemSoundPlayer.jsq_playMessageSentSound()
// 5
finishSendingMessage()
}
private func observeMessages() {
// 1
let messagesQuery = messageRef.queryLimitedToLast(25)
// 2
messagesQuery.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in
// 3
let id = snapshot.value!["senderId"] as! String
let text = snapshot.value!["text"] as! String
// 4
self.addMessage(id, text: text)
// 5
self.finishReceivingMessage()
}
}
override func textViewDidChange(textView: UITextView) {
super.textViewDidChange(textView)
}
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item] // 1
// This is where I need help retrieving the data from firebase
let text = "From: " + getLocation()
if message.senderId == senderId { // 2
return nil
} else { // 3
return NSAttributedString(string: text)
}
}
override func collectionView(collectionView: JSQMessagesCollectionView, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout, heightForCellBottomLabelAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return kJSQMessagesCollectionViewCellLabelHeightDefault
}
}
In Firebase, store each user's location separately: that way they cannot be overwritten by another user.
Add a new branch to the database structure e.g. locations
Next, add a child called e.g. people
Finally, add another child for each user with the attribute location.
Next, simply query this branch to get user information. When you update a user's location, it will only overwrite their previous location.
Please read the docs to do with querying Firebase:
https://firebase.google.com/docs/database/ios/retrieve-data#read_data_once
and the docs to do with updating data:
https://firebase.google.com/docs/database/ios/save-data#basic_write
in order to learn more about Firebase.