Pushing data from Firebase into class object Swift - swift

I have been building my first firebase application and I have run into issues attempting to retrieve data and display it on the map as an annotation.
Currently my logic is as follows : Fetch data and put it into Note Class i then try to push the information into an array which is located in NoteBrain the information appears to be being placed inside the class when i debug with print statements the data shows up. The only issue is i am having trouble pushing the data to the view controller. For some reason my loadallcoordinates is not working, i even try to type print statements and it is not showing up.
My question
Is this the best way to structure my data? Is there any way I can make my current implementation work?
Thankyou
Note Class
import Foundation
import MapKit
class noteClass : NSObject, MKAnnotation{
var title : String?
var subtitle: String?
var coordinate: CLLocationCoordinate2D
var longitude : Double
var latitude : Double
init(dictionary: [String: Any]) {
self.title = dictionary["title"] as? String ?? ""
self.subtitle = dictionary["subtitle"] as? String ?? ""
self.longitude = dictionary["longitude"] as! Double
self.latitude = dictionary["latitude"] as! Double
self.coordinate = CLLocationCoordinate2DMake((dictionary["longitude"] as? Double)!, (dictionary["latitude"] as? Double)!)
}
}
noteBrain or model class
import Foundation
import UIKit
import MapKit
import CoreLocation
import Firebase
import FirebaseDatabase
class noteBrain: UIViewController, CLLocationManagerDelegate{
var noteArray = [noteClass]()
var newNote = false
var outsideToggle = true
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.0025, 0.0025)
var manager = CLLocationManager()
func getNotes() -> Array <noteClass>
{
return noteArray
}
func appendArray(newNote : noteClass){
noteArray.append(newNote)
}
func checkInside( s1 : Int ) -> noteClass{
return noteArray[0]
}
func checkToggle() -> Bool{
return outsideToggle
}
func fetchUser() {
FIRDatabase.database().reference().child("notes").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = noteClass(dictionary: dictionary)
self.appendArray(newNote: user)
}
}, withCancel: nil)
}
}
Viewcontroller
import UIKit
import MapKit
import CoreLocation
import Firebase
import FirebaseDatabase
struct PreferencesKeys{
static let savedItems = "savedItems"
}
class ViewController: UIViewController, CLLocationManagerDelegate{
let manager = CLLocationManager()
var coordinotes : [noteClass] = []
var latitude = Double()
var noteTime = noteBrain()
//Map
#IBOutlet weak var map: MKMapView!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations[0]
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, noteTime.span)
map.setRegion(region, animated: true)
self.map.showsUserLocation = true
}
override func viewDidLoad()
{
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handleLogout))
if FIRAuth.auth()?.currentUser?.uid == nil {
perform(#selector(handleLogout), with: nil, afterDelay: 0)
}
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
noteTime.fetchUser()
appendThisTest()
loadAllCoordinotes()
}
func handleLogout() {
do {
try FIRAuth.auth()?.signOut()
} catch let logoutError {
print(logoutError)
}
let loginController = LoginController()
present(loginController, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func loadAllCoordinotes() {
for x in coordinotes
{
add(coordinote: x)
}
}
func add(coordinote: noteClass) {
map.addAnnotation(coordinote)
print(coordinote.coordinate)
print("test")
print(coordinote.title! as String)
}
func appendThisTest(){
coordinotes = noteTime.getNotes()
}
}

Related

How to set tableview to place stores nearest to my current location using geopoint in Cloud Firestore?

What im trying to do is organize my cells by the store nearest my current location using Geopoint in Cloud Firestore
I have looked all over stack, but I can't find how to set the tableview in the ViewController to show the flower store nearest me from my current location using geopoint in Cloud Firestore
this is what I have currently to set up the data to pass to the VC for it to organize the data from Firestore to the stores nearest to my current location
below I have an image of the collection in my Firestore and an Image of my ViewController for how my app is set up
import Foundation
import UIKit
class Store {
var id: String
var storeName: String
var imageUrl: String
var location: ??
init(id: String,
storeName: String,
imageUrl: String,
location:??) { //
self.id = id
self.storeName = storeName
self.imageUrl = imageUrl
self.location = location //
}
convenience init(dictionary: [String : Any]) {
let id = dictionary["id"] as? String ?? ""
let storeName = dictionary["storeName"] as? String ?? ""
let imageUrl = dictionary["imageUrl"] as? String ?? ""
let location = dictionary["location"] as? String ?? "" //
self.init(id: id,
storeName: storeName,
imageUrl: imageUrl,
location: location) //
}
}
import UIKit
import CoreLocation
import Firebase
import FirebaseFirestore
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
#IBOutlet weak var tableView: UITableView!
var stores: [Store] = []
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestWhenInUseAuthorization()
tableView.dataSource = self
tableView.delegate = self
fetchStores { (stores) in
self.stores = stores
self.tableView.reloadData()
}
}
func fetchStores(_ completion: #escaping ([Store]) -> Void) {
let ref = Firestore.firestore().collection("storeName")
ref.addSnapshotListener { (snapshot, error) in
guard error == nil, let snapshot = snapshot, !snapshot.isEmpty else {
return
}
completion(snapshot.documents.compactMap( {Store(dictionary: $0.data())} ))
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self){
if CLLocationManager.isRangingAvailable() {
// do Stuff
}
}
}
}
}
I believe this answer should work, though I may have missed something.
What you'll want to do is make the location property in Store of type CLLocationCoordinate2D. This requires importing CoreLocation. You also want to add a property called distanceFromUser that makes use of CLLocation.distance(from:) method to find the distance in meters between the user's current location and the store's location:
import Foundation
import UIKit
import CoreLocation
import Firebase
import FirebaseFirestore
class Store {
var id: String
var storeName: String
var imageUrl: String
var location: CLLocationCoordinate2D
var distanceFromUser: Double
init(id: String,
storeName: String,
imageUrl: String,
location: CLLocationCoordinate2D) {
self.id = id
self.storeName = storeName
self.imageUrl = imageUrl
self.location = location
self.distanceFromUser = (CLLocationManager().location?.distance(from: CLLocation(latitude: location.latitude, longitude: location.longitude)))!
}
convenience init(dictionary: [String : Any]) {
let id = dictionary["id"] as? String ?? ""
let storeName = dictionary["storeName"] as? String ?? ""
let imageUrl = dictionary["imageUrl"] as? String ?? ""
//We now have to convert Firestore's "location" property from a GeoPoint to a CLLocationCoordinate2d
let geoPoint = dictionary["location"] as! GeoPoint
let latitude = geoPoint.latitude
let longitude = geoPoint.longitude
let location = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
self.init(id: id,
storeName: storeName,
imageUrl: imageUrl,
location: location)
}
}
Then, you'll need to sort your stores by distanceFromUser in your ViewController's viewDidLoad:
import UIKit
import CoreLocation
import Firebase
import FirebaseFirestore
class ViewController: UIViewController, CLLocationManagerDelegate {
...
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestWhenInUseAuthorization()
tableView.dataSource = self
tableView.delegate = self
fetchStores { (stores) in
self.stores = stores.sorted(by: { $0.distanceFromUser < $1.distanceFromUser })
self.tableView.reloadData()
}
}
...
}

Getting user's location not working after Swift 4 update

I've just updated to the Xcode 9 and Swift 4 and it has broken at lot of my functions. One of which being how I get the user's location, zoom in on it on launch, detecting annotation selection and various other tasks. They don't work at all. My view controller class code is below.
import UIKit
import MapKit
import Firebase
import FirebaseDatabase
import Pulley
import CoreLocation
class ChildMapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
let manager = CLLocationManager()
var isInitialized = false
func startBackgroundLocationUpdates() {
self.manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
manager.pausesLocationUpdatesAutomatically = true
manager.activityType = .fitness
manager.allowsBackgroundLocationUpdates = true
manager.startMonitoringSignificantLocationChanges()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
if !isInitialized {
isInitialized = true
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
}
}
struct loc {
let title: String
let latitude: Double
let longitude: Double
}
var locs = [
loc(title: "New York, NY", latitude: 40.713054, longitude: -74.007228),
]
func placeAnnotations() {
let annotations = locs.map { location -> MKAnnotation in
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
annotation.title = location.title
return annotation
}
mapView.addAnnotations(annotations)
}
var position:PulleyPosition!
func mapView(_ didSelectmapView: MKMapView, didSelect view: MKAnnotationView) {
Shared.shared.annotation = view.annotation!
if Shared.shared.annotation is MKUserLocation {
Shared.shared.nameString = "My Location"
} else {
loadData()
}
}
func mapView(_ mapView: MKMapView, didDeselect view:
MKAnnotationView) {
let drawerContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DrawerViewController")
if let drawer = self.parent?.parent as? PulleyViewController {
drawer.setDrawerContentViewController(controller: drawerContent, animated: false)
drawer.setDrawerPosition(position: PulleyPosition.partiallyRevealed, animated: true)
}
}
func loadData() {
let number = (Shared.shared.annotation?.title)!
if let number = number {
let ref = FIRDatabase.database().reference()
ref.child("safehouses").child("\(number)").observeSingleEvent(of: .value, with: { snapshot in
let snapDict = snapshot.value as? NSDictionary
let name = snapDict?["name"] as? String
let phone = snapDict?["phone"] as? String
let current = CLLocation(latitude: (self.manager.location?.coordinate.latitude)!, longitude: (self.manager.location?.coordinate.longitude)!)
let destination = CLLocation(latitude: Shared.shared.annotation.coordinate.latitude, longitude: Shared.shared.annotation.coordinate.longitude)
let distance2 = current.distance(from: destination)
let distance1 = round(distance2)
if distance1 >= 1000 {
if distance1 < 10000 {
let distance3 = distance1/1000
let distance = Double(round(10*distance3)/10)
Shared.shared.typeString = "Safehouse · " + String(distance) + " km"
} else {
let distance3 = distance1/1000
let distance = round(distance3)
Shared.shared.typeString = "Safehouse · " + String(distance) + " km"
}
} else {
let distance = self.forTailingZero(temp: distance1)
Shared.shared.typeString = "Safehouse · " + distance + " m"
}
if let name = name {
if let phone = phone {
Shared.shared.nameString = name
Shared.shared.phoneString = phone
let drawerContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NewDrawerViewController")
if let drawer = self.parent?.parent as? PulleyViewController {
drawer.setDrawerContentViewController(controller: drawerContent, animated: false)
drawer.setDrawerPosition(position: PulleyPosition.partiallyRevealed, animated: true)
}
}
}
})
}
}
func forTailingZero(temp: Double) -> String{
var tempVar = String(format: "%g", temp)
return tempVar
}
func loop() {
let ref = FIRDatabase.database().reference()
ref.child("safehouses").observeSingleEvent(of: .value, with: { snapshot in
let snapDict = snapshot.value as? NSDictionary
let ii = snapDict?["number"] as? Int
let iii = Int(ii!)
var i = 1
while i <= iii{
let name = String(i)
ref.child("safehouses").child("\(i)").observeSingleEvent(of: .value, with: { snapshot in
let snapDict = snapshot.value as? NSDictionary
let longitude = snapDict?["longitude"]
as? String
let latitude = snapDict?["latitude"] as? String
if let longitude = longitude {
if let latitude = latitude {
let latitude1 = Double(latitude)
if let latitude1 = latitude1 {
let longitude1 = Double(longitude)
if let longitude1 = longitude1 {
self.locs.append(loc(title: name, latitude: latitude1, longitude: longitude1))
self.placeAnnotations()
}
}
}
}
})
i = i + 1
}
})
}
#IBOutlet weak var menuButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
self.revealViewController().rearViewRevealWidth = 175
self.revealViewController().toggleAnimationType = SWRevealToggleAnimationType.easeOut
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
loop()
mapView.delegate = self
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestAlwaysAuthorization()
Shared.shared.mapView = self.mapView
startBackgroundLocationUpdates()
}
}
final class Shared {
static let shared = Shared()
var nameString : String!
var phoneString : String!
var annotation : MKAnnotation!
var mapView : MKMapView!
var typeString : String!
}
First add these property in info.plist
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>description1</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>description2</string>
<key>NSLocationUsageDescription</key>
<string>description3</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>accept to get location</string>
<key>UIBackgroundModes</key>
And then after copy this view controller in your project
import UIKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
var locationManager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
determineMyCurrentLocation()
// Do any additional setup after loading the view.
}
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager!.allowsBackgroundLocationUpdates = true
locationManager!.pausesLocationUpdatesAutomatically = false
let value = locationManager.startMonitoringSignificantLocationChanges()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
print("ankur :- \(region)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Error \(error)")
}
}
Last step :- Open Capabilities -> In background modes -> click on location updates
If any query comment it
You need to add these keys in info plist :
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>

locationManager not being recognized as a class variable

I am trying to implement the locationManager methods and the let locationManager = CLLocation() variable is not being recognized anywhere in the code. The errors showed up when I added the didUpdateLocations and didFailWithError methods. Im trying to get the current location from the user as the view loads. Without the methods mentioned above the app breaks on the centerMapOnLocation method.
Here is my code:
import Foundation
import UIKit
import Firebase
import FirebaseDatabase
import CoreLocation
import MapKit
class MainViewController: UIViewController, CLLocationManagerDelegate{
var ref: FIRDatabaseReference!
var refHandle: UInt!
let locationManager: CLLocationManager = CLLocationManager()
let regionRadius: CLLocationDistance = 1000
var currentLocation: CLLocation!
let geoCoder = CLGeocoder()
var placemark: CLPlacemark?
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var userEmailLabel: UILabel!
#IBOutlet weak var pickUpAddress: UITextField!
override func viewDidLoad() {
ref = FIRDatabase.database().reference()
////
locationManager.delegate = self
locationManager.requestLocation()
print(locationManager.requestLocation())
////
////
refHandle = ref.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]
print((dataDict))
})
let userID: String = FIRAuth.auth()!.currentUser!.uid
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let userEmail = snapshot.value!["email"] as! String
self.userEmailLabel.text = userEmail
})
////
////
centerMapOnLocation()
super.viewDidLoad()
////
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
print("locations: \(locations)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("error: \(error)")
}
}
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status != CLAuthorizationStatus.Denied{
locationManager.startUpdatingLocation()
}
}
func centerMapOnLocation() {
currentLocation = CLLocation.init(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
let coordinateRegion = MKCoordinateRegionMakeWithDistance(locationManager.location!.coordinate, regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
}
func translateLocation() {
geoCoder.reverseGeocodeLocation(currentLocation, completionHandler: { placemarks, error in
if error == nil && placemarks!.count > 0 {
self.placemark = placemarks![0] as CLPlacemark
self.pickUpAddress.text = self.stringFromPlacemark(self.placemark!)
}
})
}
func stringFromPlacemark(placemark: CLPlacemark) -> String { // 1
var line1 = ""
if let s = placemark.subThoroughfare {
line1 += s + " "
}
if let s = placemark.thoroughfare {
line1 += s
}
var line2 = ""
if let s = placemark.locality {
line2 += s + " "
}
if let s = placemark.administrativeArea {
line2 += s + " "
}
if let s = placemark.postalCode {
line2 += s }
return line1 + "\n" + line2
}
#IBAction func pickCurrentLocation(sender: AnyObject) {
translateLocation()
}
#IBAction func signOutButton(sender: AnyObject) {
try! FIRAuth.auth()!.signOut()
if let storyboard = self.storyboard {
let viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.presentViewController(viewController, animated: false, completion: nil)
}
}
}
I have not found an answer after a while now. Help is going to be really appreciated.
Thanks.
Remove the top-level closing brace somewhere on line 75 and everything will be fine, it builds for me, the thing is it sees these functions as global ones.

How to find the user's location in swift?

Hello I am developing an application for ios and one of it's functions has to find the user's current location. Here is my code:
import UIKit
import MapKit
import CoreLocation
import SwiftyJSON
struct City {
let name : String
let location : CLLocation
let description :String
let imageName : String
func distanceTo(location:CLLocation) -> Int
{
let distanceMeters = location.distanceFromLocation(self.location)
let distanceKilometers = distanceMeters / 1000.00
return Int(round(100 * distanceKilometers) / 100)
}
}
class FirstViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var LabelTest: UILabel!
#IBOutlet weak var Slider: UISlider!
#IBOutlet weak var LabelValueSlider: UILabel!
var MySliderCurrentValue = Double()
var manager = CLLocationManager()
var userLoc: CLLocationCoordinate2D!
{
willSet{
self.orderCitysByProximity()
self.filterCitysByProximity(Int(self.Slider.value))
self.LocateMe(manager)
}
}
var cities = [City]()
var nearbyCities = [City]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let path: String = NSBundle.mainBundle().pathForResource("cities", ofType: "json") as String!
let jsonData = NSData(contentsOfFile: path) as NSData!
//let ReadableJSON = JSON ( data:jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil )
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
for city in jsonObject["cities"] as! [[String:AnyObject]] {
//let coordinates = position["Position"] as! [String:CLLocationDegrees]
let cityName = city["Name"] as! String
let latitude = city["Latitude"] as! Double
let longitude = city["Longitude"] as! Double
let description = city["Description"] as! String
let image = city["Image"] as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let city = City(name: cityName, location: location,description: description, imageName: image)
cities.append(city)
// print(cities)
}
self.orderCitysByProximity()
self.filterCitysByProximity(Int(self.Slider.value))
} catch let error as NSError {
print(error)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func SliderChange(sender: UISlider) {
let MySliderCurrentValue: String = String(Int(sender.value))
LabelValueSlider.text = MySliderCurrentValue
self.filterCitysByProximity(Int(sender.value))
}
#IBAction func goToTableView()
{
if let tableViewController = self.storyboard?.instantiateViewControllerWithIdentifier("tableViewController") as? TableViewController
{
tableViewController.cities = self.nearbyCities
self.navigationController?.pushViewController(tableViewController, animated: true)
}
}
func filterCitysByProximity(kilometers:Int)
{
self.nearbyCities.removeAll()
for city in self.cities {
if(city.distanceTo(self.userLoc) <= kilometers*2)
{
self.nearbyCities.append(city)
}
}
self.LabelTest.text = "you have \(self.nearbyCities.count) cities nearby"
let OrderedArray = self.nearbyCities.sort({ $0.distanceTo(self.userLoc) < $1.distanceTo(self.userLoc) })
self.nearbyCities = OrderedArray
}
func orderCitysByProximity()
{
let OrderedArray = self.cities.sort({ $0.distanceTo(self.userLoc) < $1.distanceTo(self.userLoc) })
self.cities = OrderedArray
}
#IBAction func LocateMe(sender: AnyObject) {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userlocation: CLLocation = locations[0] as CLLocation
manager.stopUpdatingLocation()
let location = CLLocationCoordinate2D(latitude: userlocation.coordinate.latitude, longitude: userlocation.coordinate.longitude)
self.userLoc = location
}
}
On the bottom you will see the func locationManager, where I am trying to find the user's current location. Would you please advise me how to start this function so that the app recognise when I type userLoc. userLoc are the coordinations of the user and I have to use them in other functions like func filterCitysByProximity, and the rest, which you can see in the code.
Reason of your problem :-
When you call the delegate of the CLLocationManager in viewDidLoad() it takes some time to retrieve the usersCurrent Location , but even before usersLocation could relay back to you, your functions requiring usersCurrent Location were being triggered as you had called them in viewDidLoad() thus causing the error!
your Code should look like this:-
import UIKit
import MapKit
import CoreLocation
import SwiftyJSON
struct City {
let name : String
let location : CLLocation
let description :String
let imageName : String
func distanceTo(location:CLLocation) -> Int
{
let distanceMeters = location.distanceFromLocation(self.location)
let distanceKilometers = distanceMeters / 1000.00
return Int(round(100 * distanceKilometers) / 100)
}
}
class FirstViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var LabelTest: UILabel!
#IBOutlet weak var Slider: UISlider!
#IBOutlet weak var LabelValueSlider: UILabel!
var MySliderCurrentValue = Double()
var locationManager = CLLocationManager()
var userLoc : CLLocation!
var cities = [City]()
var nearbyCities = [City]()
override func viewDidLoad() {
super.viewDidLoad()
let path: String = NSBundle.mainBundle().pathForResource("cities", ofType: "json") as String!
let jsonData = NSData(contentsOfFile: path) as NSData!
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
for city in jsonObject["cities"] as! [[String:AnyObject]] {
//let coordinates = position["Position"] as! [String:CLLocationDegrees]
let cityName = city["Name"] as! String
let latitude = city["Latitude"] as! Double
let longitude = city["Longitude"] as! Double
let description = city["Description"] as! String
let image = city["Image"] as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let city = City(name: cityName, location: location,description: description, imageName: image)
cities.append(city)
}
} catch let error as NSError {
print(error)
}
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
if locationManager.respondsToSelector(#selector(locationManager.requestWhenInUseAuthorization)) {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
else {
locationManager.startUpdatingLocation()
}
}
#IBAction func SliderChange(sender: UISlider) {
let MySliderCurrentValue: String = String(Int(sender.value))
LabelValueSlider.text = MySliderCurrentValue
if userLoc != nil{
self.filterCitysByProximity(Int(sender.value), location: userLoc)
}else{
let alertC : UIAlertController = UIAlertController(title: "Iwin", message: "UserLocation Not Updated wait a while", preferredStyle: .Alert)
let okAction : UIAlertAction = UIAlertAction(title: "oK", style: .Default, handler: { (okActn) in
print("User pressed ok")
})
alertC.addAction(okAction)
alertC.popoverPresentationController?.sourceView = view
alertC.popoverPresentationController?.sourceRect = view.frame
self.presentViewController(alertC, animated: true, completion: nil)
}
}
//Segue to .. :-
#IBAction func goToTableView()
{
if let tableViewController = self.storyboard?.instantiateViewControllerWithIdentifier("tableViewController") as? TableViewController
{
tableViewController.cities = self.nearbyCities
print(nearbyCities)
self.navigationController?.pushViewController(tableViewController, animated: true)
}
}
#IBAction func LocateMe(sender: AnyObject) {
}
//Class Functions call :-
func filterCitysByProximity(kilometers:Int, location: CLLocation)
{
self.nearbyCities.removeAll()
for city in self.cities {
if(city.distanceTo(location) <= kilometers*2)
{
self.nearbyCities.append(city)
}
}
self.LabelTest.text = "you have \(self.nearbyCities.count) cities nearby"
let OrderedArray = self.nearbyCities.sort({ $0.distanceTo(location) < $1.distanceTo(location) })
self.nearbyCities = OrderedArray
}
func orderCitysByProximity(location: CLLocation)
{
let OrderedArray = self.cities.sort({ $0.distanceTo(location) < $1.distanceTo(location) })
self.cities = OrderedArray
}
//Location Manager Functions :-
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLoc = locations[0] as CLLocation
locationManager.stopUpdatingLocation()
print(userLoc)
self.orderCitysByProximity(userLoc)
self.filterCitysByProximity(Int(self.Slider.value), location : userLoc)
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
print("authorised call came . . . . ")
locationManager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError){
print(error.localizedDescription)
locationManager.stopUpdatingLocation()
}
}
Add privacy - location usage description : Result to your info.plist
Also if your didUpdateLocations function doesn't get called
reset your location Services by going into settings->General->Reset
Try google, you will find multiple questions

Retrieving image from firebase? (swift)

So i have a firebase structure like the pic below
Now i want to retrieve that image file that i've uploaded. to decode the base64String and show it. Every user can make a post and the information that will be sended to firebase has a description etc. and also have an image. now i tried to retrieve it whit this codes but nothing did work.
var REF_LIST = Firebase(url: "\(URL_BASE)/listItems")
REF_LIST.observeEventType(FEventType.Value, withBlock: { snapshot in
let image = snapshot.value.objectForKey("images") as! String
but this already gave me a nil error on that line, so i couldn't even decode. i think i understand why it's giving me a nil error, there is no images in listItems on firebase, you first have the unique ID and then the specs with images in. now i don't now how i can retrieve that information from that unique ID?
UPDATE:
the tableViewController what will receive the data from firebase:
import UIKit
import FBSDKLoginKit
import Alamofire
import Firebase
class ListVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var lists = [List]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
dispatch_async(backgroundQueue, {
self.initObservers()
})
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tableView.reloadData()
}
func initObservers() {
LoadingOverlay.shared.showOverlay(self.view)
DataService.ds.REF_LISTS.observeEventType(.Value, withBlock: { snapshot in
print(snapshot.value)
self.lists = []
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
for snap in snapshots {
print("SNAP: \(snap)")
if let listDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let list = List(listKey: key, dictionary: listDict)
self.lists.insert(list, atIndex:0)
}
}
}
self.tableView.reloadData()
LoadingOverlay.shared.hideOverlayView()
})
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("ListCell") as? ListCell {
let list = self.lists[indexPath.row]
cell.request?.cancel()
cell.configureCell(list)
return cell
} else {
return ListCell()
}
}
}
the addController which post the data to firebase:
import UIKit
import Firebase
import Alamofire
import FBSDKCoreKit
class AddVC: UIViewController, UITextFieldDelegate, UITextViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var addTitle: UITextField!
#IBOutlet weak var addDescription: UITextView!
#IBOutlet weak var addLocation: UITextField!
#IBOutlet weak var placeholderLbl: UILabel!
#IBOutlet weak var freeSwitch: UISwitch!
#IBOutlet weak var tradeSwitch: UISwitch!
#IBOutlet weak var imageSelectorImg: UIImageView!
#IBOutlet weak var overlayView: UIView!
var currentUsername = ""
var imageSelected = false
var imagePicker: UIImagePickerController!
var base64String: NSString = ""
override func viewDidLoad() {
super.viewDidLoad()
addTitle.delegate = self
addDescription.delegate = self
addLocation.delegate = self
imagePicker = UIImagePickerController()
imagePicker.delegate = self
getCurrentUser()
hideKeyboardWhenTappedAround()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
addTitle.text = ""
addDescription.text = ""
addLocation.text = ""
freeSwitch.setOn(false, animated: false)
tradeSwitch.setOn(false, animated: false)
placeholderLbl.hidden = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getCurrentUser() {
DataService.ds.REF_USER_CURRENT.observeEventType(FEventType.Value, withBlock: { snapshot in
let currentUser = snapshot.value.objectForKey("username") as! String
print("Username: \(currentUser)")
self.currentUsername = currentUser }, withCancelBlock: { error in
print(error.description)
})
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
imagePicker.dismissViewControllerAnimated(true, completion: nil)
imageSelectorImg.image = image
dispatch_async(backgroundQueue, {
let uploadImage = image
let imageData = UIImageJPEGRepresentation(uploadImage, 0.5)
self.base64String = imageData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
})
imageSelected = true
}
#IBAction func selectImage(sender: UITapGestureRecognizer) {
presentViewController(imagePicker, animated: true, completion: nil)
}
func postToFirebase() {
// LoadingOverlay.shared.showOverlay(self.overlayView)
var post: Dictionary<String, AnyObject> = ["username": self.currentUsername, "description": self.addDescription.text!, "title": self.addTitle.text!, "location": self.addLocation.text!, "images": self.base64String]
if self.freeSwitch.on && self.tradeSwitch.on {
post["tradeOption"] = "Gratis/Te ruil"
} else if self.freeSwitch.on {
post["tradeOption"] = "Gratis"
} else if self.tradeSwitch.on {
post["tradeOption"] = "Te ruil"
}
let firebasePost = DataService.ds.REF_LISTS.childByAutoId()
firebasePost.setValue(post)
}
#IBAction func postListItem(sender: AnyObject) {
if let addTitle = addTitle.text where addTitle != "", let addDescription = addDescription.text where addDescription != "", let addLocation = addLocation.text where addLocation != "" {
dispatch_async(backgroundQueue, {
self.postToFirebase()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let listVC = storyboard.instantiateViewControllerWithIdentifier("TBC") as! UITabBarController
listVC.selectedIndex = 1
self.presentViewController(listVC, animated: false, completion: nil)
})
})
}
}
func textViewDidBeginEditing(textView: UITextView) {
placeholderLbl.hidden = true
}
func textViewDidEndEditing(textView: UITextView) {
if textView.text == "" {
placeholderLbl.hidden = false
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
and the swift file to configure the cell:
import UIKit
import Alamofire
import Firebase
class ListCell: UITableViewCell {
#IBOutlet weak var listImg: UIImageView!
#IBOutlet weak var listTitle: UILabel!
#IBOutlet weak var listTradeOption: UILabel!
#IBOutlet weak var listLocation: UILabel!
#IBOutlet weak var headImg: UIImageView!
var list: List!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func retrieveImages() {
DataService.ds.REF_LISTS.observeEventType(FEventType.Value, withBlock: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
for snap in snapshots {
let image = snap.value.objectForKey("images") as! String
let decodedData = NSData(base64EncodedString: image, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
let decodedImage = UIImage(data: decodedData!)
self.headImg.image = decodedImage
}
}
})
}
func configureCell(list: List) {
self.list = list
self.listTitle.text = list.listTitle
self.listTradeOption.text = list.listTradeOption
self.listLocation.text = list.listLocation
retrieveImages()
}
}
also the list Model file:
import Foundation
import Firebase
class List {
private var _listTitle: String!
private var _listDescription: String!
private var _listTradeOption: String!
private var _listLocation: String!
private var _listImageURL: String?
private var _listKey: String!
private var _listRef: Firebase!
var listTitle: String? {
return _listTitle
}
var listDescription: String? {
return _listDescription
}
var listTradeOption: String? {
return _listTradeOption
}
var listLocation: String? {
return _listLocation
}
var listKey: String {
return _listKey
}
var listImageURL: String? {
return _listImageURL
}
init(title: String, description: String, tradeOption: String, location: String, listImageURL: String?) {
self._listTitle = title
self._listDescription = description
self._listTradeOption = tradeOption
self._listLocation = location
self._listImageURL = listImageURL
}
init(listKey: String, dictionary: Dictionary<String, AnyObject>) {
self._listKey = listKey
if let title = dictionary ["title"] as? String {
self._listTitle = title
}
if let desc = dictionary ["description"] as? String {
self._listDescription = desc
}
if let trade = dictionary ["tradeOption"] as? String {
self._listTradeOption = trade
}
if let loc = dictionary ["location"] as? String {
self._listLocation = loc
}
if let imgUrl = dictionary["images"] as? String {
self._listImageURL = imgUrl
}
self._listRef = DataService.ds.REF_LISTS.childByAppendingPath(self._listKey)
}
}
i've got also a DataServicefile, where i create a user by unique ID with this code:
var REF_USER_CURRENT: Firebase {
let uid = NSUserDefaults.standardUserDefaults().valueForKey(KEY_UID) as! String
let user = Firebase(url: "\(REF_BASE)").childByAppendingPath("users").childByAppendingPath(uid)
return user!
}
func createFirebaseUser(uid: String, user: Dictionary<String, String>) {
REF_USERS.childByAppendingPath(uid).setValue(user)
}
i know it's a lot but maybe the best way to help :)
Try editing this in List Cell
var imageURL = String()
func retrieveImages() {
let decodedData = NSData(base64EncodedString: imageURL, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
let decodedImage = UIImage(data: decodedData!)
self.headImg.image = decodedImage
}
func configureCell(list: List) {
self.list = list
self.listTitle.text = list.listTitle
self.listTradeOption.text = list.listTradeOption
self.listLocation.text = list.listLocation
self.imageURL = list.listImageURL //you already had the image url for that specific cell
retrieveImages()
}
Storing and accessing images using base64String in firebase is not an
efficient way, instead of that we can use FirebaseStorage (Google cloud storage
bucket) for uploading images to Firebase and it will provide us
download URL for a particular image. We can store that URL into our database simply in a string format and access it whenever we
required and then download the corresponding image from that URL by
using SDWebImage.
Refer below link for integrating FirebaseStorage into your project: https://firebase.google.com/docs/storage/ios/upload-files