How come my array of CLLocation instances is empty? - ios

I'm currently trying to construct a way that calculates the the distance in feet between my current location and other hard coded locations. I will eventually display these distances as strings in a table view. These are the steps that I'm taking:
1) I have a hard coded library of dictionaries that each hold a "latitude" and "longitude" key-value pair.
2) to extract this information from a struct as follows:
struct BarDetials {
// other properties
....
var lat: CLLocationDegrees?
var long CLLocationDegrees?
init(index: Int) {
// initialize other properties
// ...
lat = specificBarDetail["latitude"] as! CLLocationDegrees!
long = specificBarDetail["longitude"] as! CLLocationDegrees!
}
}
3) I use another struct to create an array of CLLocation instances from these coordinates as follows:
struct ConstructLocationsToCompare {
var latitude : [CLLocationDegrees?] = []
var longitude : [CLLocationDegrees?] = []
var barLocations : [CLLocation?] = []
init(){
for index in 0...21 {
var data = BarDetails(index: index)
if data.lat != nil {
latitude.append(data.lat!)
}
if data.long != nil {
longitude.append(data.long!)
}
barLocations[index] = CLLocation(latitude: latitude[index]!, longitude: longitude[index]!)
}
}
}
4) I then calculate the distances in my MasterViewController().
var latestLocation : AnyObject? , var currentLocation : CLLocation!, and var distanceStringArray : [String?] = [] are all properties of my MasterViewController() class.
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placemarks, error) -> Void in
// Get latest location and store it as a property of MasterViewController
if self.latestLocation != nil {
self.latestLocation = locations[locations.count - 1]
// Print out the latest locaiton for debuging
println("Latest Location")
println("---------------")
println(self.latestLocation)
// Create an instance of ContructLocationsToCompare to get all locaiton data
var getBarLocations = ConstructLocationsToCompare()
// Get loop to calculate the distance you are away from each Bar
var distanceDoubleArray : [Double?] = []
for index in 0...21 {
var distanceBetween : CLLocationDistance = self.latestLocation!.distanceFromLocation(getBarLocations.barLocations[index])
var distanceInFeet = distanceBetween * 3.28084
distanceDoubleArray[index] = distanceInFeet
self.distancesStringArray.append( String(format: "%6.0 ft", distanceDoubleArray[index]!))
}
}
//println(distanceBetween) or get error if it exists
if error != nil {
println("Error: " + error.localizedDescription)
return
}
if placemarks.count > 0 {
let pm = placemarks[0] as! CLPlacemark
self.displayLocationInfo(pm)
}
})
}
5) Lastly, to display my distance from each location (in my -cellForRowAtIndexPath):
println("")
println("")
println("List of Locations")
println("=================")
println(self.distancesStringArray)
println("")
println("")
println("")
println("")
if self.distancesStringArray[indexPath.row]!.isEmpty == true{
futureCell.distanceAway?.text = "-" // I crash at this line ^^^^^^
} else {
futureCell.distanceAway?.text = self.distancesStringArray[indexPath.row]
}
*** My distanceStringArray is always empty therefore I get a fatal error and the app crashes. How do I fix this? Is the fact that I declare var latestLocation : AnyObject? , var currentLocation : CLLocation!, and var distanceStringArray : [String?] = [] properties of my MasterViewController() class bad practice? If not do these properties need to be casted as a different type/ declared in a different manner?

Related

Why does a GeoFire query sometimes use data from a previous load?

So sometimes someone in entered the search radius is from before, ie someone who was in search radius, but based on the current data in the database is not in the radius. Other times, someone who wasn't in the search radius before but now is, doesn't get printed.
This only happens once each time, ie if I load the app for the second time after the erroneous inclusion or exclusion, the correct array prints.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let databaseRef = Database.database().reference()
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
print("locations = \(locValue.latitude) \(locValue.longitude)")
latestLocation = ["latitude" : locValue.latitude, "longitude" : locValue.longitude]
let lat = locValue.latitude
let lon = locValue.longitude
dict = CLLocation(latitude: lat, longitude: lon)
print("dict", dict)
if let locationDictionary = latestLocation {
databaseRef.child("people").child(uid).child("Coordinates").setValue(locationDictionary)
let geofireRef = Database.database().reference().child("Loc")
let geoFire = GeoFire(firebaseRef: geofireRef)
print(CLLocation(latitude: lat, longitude: lon),"GGG")
geoFire.setLocation(CLLocation(latitude: lat, longitude: lon), forKey: uid)
}
manager.stopUpdatingLocation()
}
Override func ViewdidLoad() {
super.viewDidLoad()
guard let uid = Auth.auth().currentUser?.uid else { return }
let geofireRef = Database.database().reference().child("Loc")
let geoFire = GeoFire(firebaseRef: geofireRef)
geoFire.getLocationForKey(uid) { (location, error) in
if (error != nil) {
print("An error occurred getting the location for \"Coordinates\": \(String(describing: error?.localizedDescription))")
} else if (location != nil) {
print("Location for \"Coordinates\" is [\(location?.coordinate.latitude), \(String(describing: location?.coordinate.longitude))]")
} else {
print("GeoFire does not contain a location for \"Coordinates\"")
}
}
let query1 = geoFire.query(at: self.dict, withRadius: 3)
query1.observe(.keyEntered, with: { key, location in
print("Key: " + key + "entered the search radius.") ///**this prints keys of users within 3 miles. This is where I see the wrong inclusions or exclusions**
do {
self.componentArray.append(key)
}
print(self.componentArray,"kr")
}
)
}
Here's what I would do for testing and maybe a solution. This is similar to your code but takes some of the unknowns out of the equation; I think we maybe running into an asynchronous issue as well, so give this a try.
In viewDidLoad get the current users position. That position will be used as the center point of the query
self.geoFire.getLocationForKey(uid) { (location, error) in
if (error != nil) {
print("An error occurred getting the location for \"Coordinates\": \(String(describing: error?.localizedDescription))")
} else if (location != nil) {
self.setupCircleQueryWith(center: location) //pass the known location
} else {
print("GeoFire does not contain a location for \"Coordinates\"")
}
}
Once the location var is populated within the closure (so you know it's valid) pass it to a function to generate the query
func setupCircleQueryWith(center: CLLLocation) {
var circleQuery = self.geoFire.queryAtLocation(center, withRadius: 3.0)
self.queryHandle = self.circleQuery.observe(.keyEntered, with: { key, location in
print("Key '\(key)' entered the search area and is at location '\(location)'")
self.myKeyArray.append(key)
})
}
self.queryHandle is a class var we can use to remove the query at a later time. I also set up self.geoFire as a class var that points to Loc.
EDIT
At the very top of your class, add a class var to store the keys
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
var ref: DatabaseReference!
var myKeyArray = [String]()
let queryHandle: DatabaseHandle!
and remember to also add a .keyExited event so you will know when to remove a key from the array when the key exits the area.

I get an empty CLLocationCoordinates array when loading data from user defaults

I'm trying to store to UserDefaults an array of CCLocationCoordinates from the tracking portion of my app paired with the name of the tracked route as key, to be able to recall it later on to use it within a function.
The problem is that when I call that function I get the index out of range error. I checked and the array is empty.
As I'm new to user defaults I tried to see other similar posts but they're all about NSUserDefaults and didn't find a solution.
Heres the code for the functions for storing and recalling the array:
func stopTracking2() {
self.trackingIsActive = false
self.trackigButton.backgroundColor = UIColor.yellow
locationManager.stopUpdatingLocation()
let stopRoutePosition = RouteAnnotation(title: "Route Stop", coordinate: (locationManager.location?.coordinate)!, imageName: "Route Stop")
self.actualRouteInUseAnnotations.append(stopRoutePosition)
print(actualRouteInUseCoordinatesArray)
print(actualRouteInUseAnnotations)
drawRoutePolyline() // draw line to show route
// checkAlerts2() // check if there is any notified problem on our route and marks it with a blue circle, now called at programmed checking
saveRouteToUserDefaults()
postRouteToAnalitics() // store route anonymously to FIrebase
}
func saveRouteToUserDefaults() {
// save actualRouteInUseCoordinatesArray : change for function
// userDefaults.set(actualRouteInUseCoordinatesArray, forKey: "\(String(describing: userRoute))")
storeCoordinates(actualRouteInUseCoordinatesArray)
}
// Store an array of CLLocationCoordinate2D
func storeCoordinates(_ coordinates: [CLLocationCoordinate2D]) {
let locations = coordinates.map { coordinate -> CLLocation in
return CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
}
let archived = NSKeyedArchiver.archivedData(withRootObject: locations)
userDefaults.set(archived, forKey: "\(String(describing: userRoute))")
userDefaults.synchronize()
}
func loadRouteFromUserDefaults() {
// gets entry from userRouteArray stored in userDefaults and append them into actualRouteInUseCoordinatesArray
actualRouteInUseCoordinatesArray.removeAll()
actualRouteInUseCoordinatesArray = userDefaults.object(forKey: "\(String(describing: userRoute))") as? [CLLocationCoordinate2D] ?? [CLLocationCoordinate2D]() // here we get the right set of coordinates for the route we are about to do the check on
// load route coordinates from UserDefaults
// actualRouteInUseCoordinatesArray = loadCoordinates()! //error found nil
}
// Return an array of CLLocationCoordinate2D
func loadCoordinates() -> [CLLocationCoordinate2D]? {
guard let archived = userDefaults.object(forKey: "\(String(describing: userRoute))") as? Data,
let locations = NSKeyedUnarchiver.unarchiveObject(with: archived) as? [CLLocation] else {
return nil
}
let coordinates = locations.map { location -> CLLocationCoordinate2D in
return location.coordinate
}
return coordinates
}
}
extension NewMapViewController {
// ALERTS :
func checkAlerts2() {
loadRouteFromUserDefaults() //load route coordinates to check in
// CHECK IF ANY OBSTACLE IS OUN OUR ROUTE BY COMPARING DISTANCES
while trackingCoordinatesArrayPosition != ( (actualRouteInUseCoordinatesArray.count) - 1) {
print("checking is started")
print(actualRouteInUseCoordinatesArray)
let trackingLatitude = actualRouteInUseCoordinatesArray[trackingCoordinatesArrayPosition].latitude
let trackingLongitude = actualRouteInUseCoordinatesArray[trackingCoordinatesArrayPosition].longitude
let alertLatitude = alertNotificationCoordinatesArray[alertNotificationCoordinatesArrayPosition].latitude
let alertLongitude = alertNotificationCoordinatesArray[alertNotificationCoordinatesArrayPosition].longitude
let coordinateFrom = CLLocation(latitude: trackingLatitude, longitude: trackingLongitude)
let coordinateTo = CLLocation(latitude: alertLatitude, longitude: alertLongitude)
let coordinatesDistanceInMeters = coordinateFrom.distance(from: coordinateTo)
// CHECK SENSITIVITY: sets the distance in meters for an alert to be considered an obstacle
if coordinatesDistanceInMeters <= 10 {
print( "found problem")
routeObstacle.append(alertNotificationCoordinatesArray[alertNotificationCoordinatesArrayPosition]) // populate obstacles array
trackingCoordinatesArrayPosition = ( trackingCoordinatesArrayPosition + 1)
}
else if alertNotificationCoordinatesArrayPosition < ((alertNotificationCoordinatesArray.count) - 1) {
alertNotificationCoordinatesArrayPosition = alertNotificationCoordinatesArrayPosition + 1
}
else if alertNotificationCoordinatesArrayPosition == (alertNotificationCoordinatesArray.count - 1) {
trackingCoordinatesArrayPosition = ( trackingCoordinatesArrayPosition + 1)
alertNotificationCoordinatesArrayPosition = 0
}
}
findObstacles()
NewMapViewController.checkCounter = 0
displayObstacles()
}
In the extension you can see the function that uses the array.
Right after the print of the array I get the index out of range error.
Thanks as usual to the community.
After trying various solutions offered I decided to rewrite the whole thing.
So after finding a post on how to code/decode my array to string I decided it was the way to go. It shouldn't be heavy on the system as it's a string that gets saved. Please let me know what you think of this solution.
Thank to #Sh_Khan to point out it was a decoding issue, and to #Moritz to point out I was performing a bad practice.
So the code is:
func storeRoute() {
// first we code the CLLocationCoordinate2D array to string
// second we store string into userDefaults
userDefaults.set(encodeCoordinates(coords: actualRouteInUseCoordinatesArray), forKey: "\(String(describing: NewMapViewController.userRoute))")
}
func loadRoute() {
//first se load string from user defaults
let route = userDefaults.string(forKey: "\(String(describing: NewMapViewController.userRoute))")
print("loaded route is \(route!))")
//second we decode it into CLLocationCoordinate2D array
actualRouteInUseCoordinatesArray = decodeCoordinates(encodedString: route!)
print("decoded route array is \(actualRouteInUseCoordinatesArray))")
}
func encodeCoordinates(coords: [CLLocationCoordinate2D]) -> String {
let flattenedCoords: [String] = coords.map { coord -> String in "\(coord.latitude):\(coord.longitude)" }
let encodedString: String = flattenedCoords.joined(separator: ",")
return encodedString
}
func decodeCoordinates(encodedString: String) -> [CLLocationCoordinate2D] {
let flattenedCoords: [String] = encodedString.components(separatedBy: ",")
let coords: [CLLocationCoordinate2D] = flattenedCoords.map { coord -> CLLocationCoordinate2D in
let split = coord.components(separatedBy: ":")
if split.count == 2 {
let latitude: Double = Double(split[0]) ?? 0
let longitude: Double = Double(split[1]) ?? 0
return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
} else {
return CLLocationCoordinate2D()
}
}
return coords
}
Rather than using heavy-weight objectiv-c-ish NSKeyed(Un)Archiver and making a detour via CLLocation I recommend to extend CLLocationCoordinate2D to adopt Codable
extension CLLocationCoordinate2D : Codable {
public init(from decoder: Decoder) throws {
var arrayContainer = try decoder.unkeyedContainer()
if arrayContainer.count == 2 {
let lat = try arrayContainer.decode(CLLocationDegrees.self)
let lng = try arrayContainer.decode(CLLocationDegrees.self)
self.init(latitude: lat, longitude: lng)
} else {
throw DecodingError.dataCorruptedError(in: arrayContainer, debugDescription: "Coordinate array must contain two items")
}
}
public func encode(to encoder: Encoder) throws {
var arrayContainer = encoder.unkeyedContainer()
try arrayContainer.encode(contentsOf: [latitude, longitude])
}
}
and replace the methods to load and save data with
func storeCoordinates(_ coordinates: [CLLocationCoordinate2D]) throws {
let data = try JSONEncoder().encode(coordinates)
UserDefaults.standard.set(data, forKey: String(describing: userRoute))
}
func loadCoordinates() -> [CLLocationCoordinate2D] {
guard let data = UserDefaults.standard.data(forKey: String(describing: userRoute)) else { return [] }
do {
return try JSONDecoder().decode([CLLocationCoordinate2D].self, from: data)
} catch {
print(error)
return []
}
}
storeCoordinates throws it hands over a potential encoding error
Load the data with
actualRouteInUseCoordinatesArray = loadCoordinates()
and save it
do {
try storeCoordinates(actualRouteInUseCoordinatesArray)
} catch { print(error) }
Your problem is that you save it as data and try to read directly without unarchiving , You can try
let locations = [CLLocation(latitude: 123, longitude: 344),CLLocation(latitude: 123, longitude: 344),CLLocation(latitude: 123, longitude: 344)]
do {
let archived = try NSKeyedArchiver.archivedData(withRootObject: locations, requiringSecureCoding: true)
UserDefaults.standard.set(archived, forKey:"myKey")
// read savely
if let data = UserDefaults.standard.data(forKey: "myKey") {
let saved = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! [CLLocation]
print(saved)
}
}
catch {
print(error)
}

Return Type Nill

func getAddressFromLatLon() {
var locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
var currentLocation = CLLocation()
if( CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorized){
currentLocation = locManager.location!
}
var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
let lat: Double = Double(currentLocation.coordinate.latitude)
//21.228124
let lon: Double = Double(currentLocation.coordinate.longitude)
//72.833770
let ceo: CLGeocoder = CLGeocoder()
center.latitude = lat
center.longitude = lon
let geoCoder = CLGeocoder()
var placemark: AnyObject
var error: NSError
geoCoder.reverseGeocodeLocation(locManager.location!, completionHandler: { (placemark, error) -> Void in
if error != nil {
print("Error: \(error?.localizedDescription)")
return
}
if (placemark?.count)! > 0 {
let pm = (placemark?[0])! as CLPlacemark
//self.addressString = pm.locality! + pm.country!
let adress = pm.locality! + " " + pm.country!
print(adress)
} else {
print("Error with data")
}
})
}
Sorry about this very basic question. I am fairly new to swift and I am trying to reverse geocode a latitude and longitude. I am trying to return the adress from the reverse geocoding, but it seems to be returning nil. The function in question is getAddressFromLatLon() and I have tried adding a return type but it is returning nil. When I print in the function itself, the correct value is printed but for some reason I am having difficulty getting the adress to return so I can pass it to other classes/functions.
You need to hold the value which you get from reserver geocoder and just need to use it.
geoCoder.reverseGeocodeLocation(locManager.location!, completionHandler: { (placemark, error) -> Void in
if error != nil {
print("Error: \(error?.localizedDescription)")
return
}
if (placemark?.count)! > 0 {
let pm = (placemark?[0])! as CLPlacemark
//self.addressString = pm.locality! + pm.country!
let adress = pm.locality! + " " + pm.country!
// Store value into global object and you can use it...
// Another option, you can call one function and do necessary steps in it
mainLocality = pm.locality
mainCountry - pm.country
updateGeoLocation() // Call funcation
print(adress)
} else {
print("Error with data")
}
})
func updateGeoLocation(){
// You can use both object at here
// mainLocality
// mainCountry
// Do your stuff
}
You need to initilise CLLocation object in ViewDidLoad and get location coordinatites in it delegate method
Declate variable for current location
var currentLocation : CLLocation
In ViewDidLoad
var locManager = CLLocationManager()
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
// ask permission - NOT NECESSARY IF YOU ALREADY ADDED NSLocationAlwaysUsageDescription IT UP INFO.PLIST
locationManager.requestAlwaysAuthorization()
// when in use foreground
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
CLLocation's Delegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Get first location item returned from locations array
currentLocation = locations[0]
self.getAddressFromLatLon() // you can call here or whenever you want
}
Your method as below
func getAddressFromLatLon() {
var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
let lat: Double = Double(currentLocation.coordinate.latitude)
//21.228124
let lon: Double = Double(currentLocation.coordinate.longitude)
//72.833770
let ceo: CLGeocoder = CLGeocoder()
center.latitude = lat
center.longitude = lon
let geoCoder = CLGeocoder()
var placemark: AnyObject
var error: NSError
geoCoder.reverseGeocodeLocation(locManager.location!, completionHandler: { (placemark, error) -> Void in
if error != nil {
print("Error: \(error?.localizedDescription)")
return
}
if (placemark?.count)! > 0 {
let pm = (placemark?[0])! as CLPlacemark
//self.addressString = pm.locality! + pm.country!
let adress = pm.locality! + " " + pm.country!
print(adress)
} else {
print("Error with data")
}
})
}

iOS - How can I return latitude and longitude?

I can't return latitude and longitude. I always get a 0.0 and 0.0.
What can I do to get these values?
Code :
func forwardGeocoding (address: String) -> (Double, Double) {
let geoCoder = CLGeocoder()
var latitude: Double = 0.0
var longitude: Double = 0.0
geoCoder.geocodeAddressString(address) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
if placemarks!.count > 0 {
let placemark = placemarks![0] as CLPlacemark
let location = placemark.location
latitude = Double((location?.coordinate.latitude)!)
longitude = Double((location?.coordinate.longitude)!)
print("before : \(latitude, longitude)")
}
}
}
print("after : \(latitude, longitude)")
return (latitude, longitude)
}
This my viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
forwardGeocoding("New York, NY, United States")
}
Result:
after : (0.0, 0.0)
before : (40.713054, -74.007228)
The problem with your code is that geocoding requests are asynchronous, so the return statement is executed before the geocoding results are actually retrieved.
I'd probably use one of two options to fix this. First, instead of returning a tuple, make your own completion handler, and call it after the placemark is found:
func forwardGeocoding (address: String, completion: (CLLocationCoordinate2D) -> Void) {
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
if placemarks!.count > 0 {
let placemark = placemarks![0] as CLPlacemark
let location = placemark.location
completion(location.coordinate)
}
}
}
}
Now when you call this function you can provide the completion with the relevant values from wherever you're calling the function.
If the function is actually a method in a class and it never needs to be called from another class, you could have it set properties of the class, and those properties could have didSet blocks. For example:
class SomeClass {
var coordinates: CLLocationCoordinate2D {
didSet {
doSomethingWithCoordinates()
}
}
private func forwardGeocoding (address: String, completion: (CLLocationCoordinate2D) -> Void) {
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
if placemarks!.count > 0 {
let placemark = placemarks![0] as CLPlacemark
let location = placemark.location
self.coordinates = location.coordinate
}
}
}
}
}
The first options is probably more versatile, but the second avoids having completion blocks withing completion blocks, which can sometimes become confusing to keep track of in your code.
It's quite obvious that your function returns always (0.0, 0.0). It's because geoCoder.geocodeAddressString() returns placemarks asynchronously.
It's time-consuming operation so you have to modify your code to handle that.
One of possible solutions is to modify your function:
func forwardGeocoding (address: String, completion: (Bool, CLLocationCoordinate2D!) -> () ) {
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if error != nil {
print(error?.localizedDescription)
completion(false,nil)
} else {
if placemarks!.count > 0 {
let placemark = placemarks![0] as CLPlacemark
let location = placemark.location
completion(true, location?.coordinate)
}
}
}
And call it:
self.forwardGeocoding(YourAdress, completion {
success, coordinate in
if success {
let lat = coordinate.lattitude
let long = coordinate.longitude
// Do sth with your coordinates
} else {
// error sth went wrong
}
}
Notice that your function returns nothing.
Hope that helps.

Save to parse manually after data has been queried

I am using Parse pinInBackground feature to pin data (image, text, date, coordinates) in the background and that data is queried every time the app is opened.The app is used to log a photo and the location and coordinates.So every entry you make is queried and displays in a tableview (only the count of entries yet).
I want to be able to let the user manually sink with Parse.com and not use the saveEventually feature.
Meaning I want a button and when pressed the queried data must sink with Parse and the be in pinned.
Here is how my data is pinned
#IBAction func submitButton(sender: AnyObject) {
locationLogs["title"] = log.title
locationLogs["description"] = log.descriptionOf
println("log = \(log.title)")
println("log = \(log.descriptionOf)")
locationLogs.pinInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if (success) {
}else{
println("error = \(error)")
}}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
let location:CLLocationCoordinate2D = manager.location.coordinate
let altitude: CLLocationDistance = manager.location.altitude
println("new long= \(location.longitude)")
println("new lat= \(location.latitude)")
println("new altitude= \(altitude)")
println("new timestamp = \(timestamp)")
locationLogs["longitude"] = location.longitude
locationLogs["latitude"] = location.latitude
locationLogs["altitude"] = altitude
locationLogs["timestamp"] = timestamp
}
And here I query it
var result : AnyObject?
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className:"LocationLogs")
query.fromLocalDatastore()
query.findObjectsInBackgroundWithBlock( { (NSArray results, NSError error) in
if error == nil && results != nil {
self.result = results
println("count = \(self.result!.count)")
self.loggedItemsTableView.reloadData()
}else{
println("ERRRROOOORRRR HORROOORRR= \(error)")
}
})
}
I have tried to use this command:
result?.saveAllInBackground()
But this only gave back an error.
Can someone please give me the correct code on how to do this or give me a link showing me how.
Here is a full code explanation on how I solved it:
//create an Array
var tableData: NSArray = []
func queryAll() {
let query = PFQuery(className:"LocationLogs")
query.fromLocalDatastore()
query.findObjectsInBackgroundWithBlock( { (NSArray results, NSError error) in
if error == nil && results != nil {
println("array = \(results)" )
self.tableData = results!
self.loggedItemsTableView.reloadData()
}else{
println("ERRRROOOORRRR HORROOORRR= \(error)")
}
})
}
//Call save on the class and not the object
PFObject.saveAllInBackground(self.tableData as [AnyObject], block: { (success: Bool, error: NSError?) -> Void in
if (success) {
//Remember to unpin the data if it will no longer be needed
PFObject.unpinAllInBackground(self.tableData as [AnyObject])
println("Pinned Data has successfully been saved")
}else{
println("error= \(error?.localizedDescription)")
}
})
Use this to safely cast/checked your object
if let results = self.result { // this will verify if your self.result is a non-nil array of object
// if it has a value then it will be passed to results
// you can now safely proceed on saving your objects
PFObject.saveAllInBackground(results, block: { (succeeded, error) -> Void in
// additional code
if succeeded {
// alert, remove hud ......
}else{
if let reqError = error {
println(reqError.localizedDescription)
}
}
})
}
struct log {
// this is dummy datastructure that imitate some part of your code ... you dont need it
var title = ""
var descriptionOf = ""
}
struct LocationInfo{
var title:String!
var description:String!
var location:PFGeoPoint!
var timestamp:NSDate!
var username:String
}
var locationManager = CLLocationManager()
var arrayOfLocations = [LocationInfo]()
func submitButton(sender: AnyObject) {
var logData = log()
var point = OneLocation(locationManager)
let username = PFUser.currentUser()?.username
var locationLogs = PFObject(className: "")
locationLogs["title"] = logData.title
locationLogs["description"] = logData.descriptionOf
locationLogs["Location"] = point
locationLogs["username"] = username
locationLogs["TimeStamp"] = locationManager.location.timestamp
locationLogs.saveInBackgroundWithBlock { (success:Bool, error:NSError?) -> Void in
if error == nil
{
println("data was save")
}
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation (manager.location, completionHandler: {(placemarks, error)->Void in
var UserCurrentLocation:CLLocationCoordinate2D = manager.location.coordinate
// println("User's parking Location : \(UserCurrentLocation.latitude) \(UserCurrentLocation.longitude)")
if (error != nil) {
println("Error")
return
}
locationManager.stopUpdatingLocation()
})
}
func OneLocation(Manager:CLLocationManager)->PFGeoPoint{
var latitude = Manager.location.coordinate.latitude
var longitude = Manager.location.coordinate.longitude
var point = PFGeoPoint(latitude: latitude, longitude: longitude)
return point
}
func QueryFromParse(){
var query = PFQuery(className: "")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let new_objects = objects as? [PFObject]
{
for SingleObject in new_objects
{
// with this single object you could get the description, title , username,etc
var location = SingleObject["Location"] as! PFGeoPoint
var username = SingleObject["username"] as! String
var title = SingleObject["title"] as! String
var time = SingleObject["TimeStamp"] as! NSDate
var description = SingleObject["description"] as! String
var singleLocationInfo = LocationInfo(title: title, description: description, location: location, timestamp: time, username: username)
arrayOfLocations.append(singleLocationInfo)
// reload data for the tableview
}
}
}
else
{
println("error")
}
}
}
Therefore you have an arrayoflocations that can be used to populate data in your tableView
Struct log is a dummy datastructure
And also I don't know if that was what you wanted ... but you should get the idea..
Hope that helps..

Resources