Trying to modify ckrecord in swift - ios

I want the users' location to be updated in cloudkit but my code just save a new record every time. How can the existing record be modified or replaced by the new one?
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
self.mapView.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
locationRecord.setObject(location, forKey: "location")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(locationRecord) { record, error in
}
if error == nil
{
print("Location saved")
self.loc1 = location!
}
}

To Modify a record, you need to pull in your orignal record - update the details and then save the record.
I would do this by caching a reference to the original recordID eg
var locationRecordforUser = (whatever your CKrecord was when you created it)
then in your location method above, just grab it, make changes and perform save
record.setValue(locationbits, forKey: locationField)
self. publicData.saveRecord(record, completionHandler: { (savedRecord, error) in
dispatch_async(dispatch_get_main_queue()) {
completion(savedRecord, error)
}
})

Related

upload MapKit region to firebase

I try to save a map with the users location to a post, but I get Value of type 'MKMapView?' has no member 'MKMapView' as an error all the time...
The following shows my code but I leave out any background code to the images and labels as everything there works fine, I just include them in here so you know how I save the post informations... Do you know what my error is and how I can solve it?
var takenMap: MKMapView!
#IBAction func postPressed(_ sender: Any) {
if textView.text != "" && takenImage != nil && userLocation.text != "" {
// Create and save a new job
let newJob = Job(text: textView.text, jobImage: takenImage!, addedByUser: (userLabel?.text)!, userImage: UserImage, location: userLocation.text, map: takenMap.MKMapView)
newJob.save()
}
//MARK:- CLLocationManager Delegates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geoCoder = CLGeocoder()
let center = CLLocationCoordinate2D(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
map.setRegion(region, animated: true)
self.map = takenMap
geoCoder.reverseGeocodeLocation(lastLocation) { (placeMarks, error) in
if error == nil {
if let firstLocation = placeMarks?[0] {
self.locationManager.stopUpdatingLocation()
if let cityName = firstLocation.locality,
let street = firstLocation.thoroughfare {
self.scanLocation = "\(street), \(cityName)"
print("This is the current city name", cityName)
print("this is the current street address", street)
self.takenLocation = self.scanLocation!
self.userLocation.text = self.takenLocation
}
}
}
}
}
}
Job.swift:
var map: String?
init(map: String? = nil) {
self.map = map
ref = Database.database().reference().child("jobs").childByAutoId()
}
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
map = value["location"] as? String
}
}
func save() {
let newPostKey = ref.key
// save jobImage
if let imageData = jobImage?.jpegData(compressionQuality: 0.5) {
let storage = Storage.storage().reference().child("jobImages/\(newPostKey)")
storage.putData(imageData).observe(.success, handler: { (snapshot) in
self.downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
let postDictionary = [
"map" : self.map!
] as [String : Any]
self.ref.setValue(postDictionary)
})
}
}
I left out any code for labels or whatever out so the snippet won't be too long
The code takenMap.MKMapView should probably just be takenMap.

Swift Geofencing working with GPX file but not on the real device

when I run the code below testing using my phone connected to my mac I have a GPX file and on the debugger I select "Locations" everything works fine, every region I have on the GPX file gets triggered, the notification comes up with no problem.
The issue is when I take my phone for a car ride and I pass the exactly regions I have on my GPX file , the notifications don't get triggered just the first one.
On the method
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
print("Hi \(region.identifier)")
}
I can see all the regions had been started for monitoring
I've been stuck on this for quite few days now... Could you please someone help me .
Code follows below.
func GetAllILocations(){
if let url = URL(string: "http://www.goemobile.com/mobile/liquidnitro/getlocations.php"){
var request = URLRequest(url:url)
request.httpMethod = "POST";// Compose a query string
let postString = ""
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with:request) { data, response, error in
if error != nil{
return
}
do {
if let convertedJson = try JSONSerialization.jsonObject(with: data!, options: []) as? [[String:Any]] {
for location in convertedJson {
if ((location["locationid"] as? Int)! > 0) {
let latitude = location["latitude"] as! Double
let longitude = location["longitude"] as! Double
let title = location["locationtitle"] as! String
let subtitle = location["locationsubtitle"] as? String
let annotation = MKPointAnnotation()
annotation.title = title
annotation.subtitle = subtitle
annotation.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
self.mapView.addAnnotation(annotation)
let center = CLLocationCoordinate2DMake(latitude, longitude)
let region = CLCircularRegion.init(center: center, radius: 0.5, identifier: title)
region.notifyOnEntry = true;
region.notifyOnExit = false;
self.locationManger.startMonitoring(for: region)
}
}
}
}
catch let error as NSError {
print(error.localizedDescription)
}
}
task.resume()
}
}
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
print("Hi \(region.identifier)")
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
lblRegion.text = region.identifier
if region is CLCircularRegion {
scheduleNotification(inSeconds: 0.5, storeName:region.identifier, completion: {success in
if !success{
let alert = UIAlertController(title: "Error Alert", message: region.identifier, preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
})
}
}

Trying to update CLLocation if NSUserDefault Exists in Swift

Originally I was trying to save a CLLocation as a NSUserDefault value before I stored it as a CKRecord in CLoudkit but I got the error: "defaults.setObject(locationRecord.recordID, forKey: "locationRecordID")" with the reason being "Attempt to set a non-property-list object as an NSUserDefaults/CFPreferences value for key locationRecordID". So now I am trying to save the lat and long as a default and replace the old location in Cloudkit. I am currently getting a 'Thread 1 SIGABRT' error on the line "publicDB.fetchRecordWithID((defaults.objectForKey("Location") as! CKRecordID),completionHandler: " with the reason being "Could not cast value of type '__NSCFDictionary' (0x1a1bec968) to 'CKRecordID'."
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
self.loc1 = location!
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.004, longitudeDelta: 0.004))
self.mapView.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
self.locationManager.startMonitoringSignificantLocationChanges()
self.getRecordToUpdate(locations)
let lat: CLLocationDegrees = center.latitude
self.lat1 = lat
let long: CLLocationDegrees = center.longitude
self.long1 = long
locationRecord.setObject(location, forKey: "location")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(locationRecord) { record, error in
}
if error == nil
{
print("Location saved")
}
}
func getRecordToUpdate(locations:[CLLocation])
{
let defaults = NSUserDefaults.standardUserDefaults()
var locationRecord:CKRecord
if defaults.objectForKey("Location") == nil{
locationRecord = CKRecord(recordType: "location")
let locationDict = ["lat": lat1, "lng": long1]//
defaults.setObject(locationDict, forKey: "Location")//
self.updateLocationRecord(locationRecord, locations: locations)
print("new")
}else{
let publicDB = CKContainer.defaultContainer().publicCloudDatabase
publicDB.fetchRecordWithID((defaults.objectForKey("Location") as! CKRecordID),completionHandler: {
(record, error) in
if error == nil{
self.updateLocationRecord(record!, locations: locations)
print("fetched record")
}else{
print("Error fetching previous record")
}
})
}
}
func updateLocationRecord(locationRecord:CKRecord, locations:[CLLocation])
{
let location = locations.last
locationRecord.setObject(location, forKey: "location")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(locationRecord) { record, error in
}
if error == nil
{
print("Location saved")
}
}
Try this
You can save NSData to NSUserDefaults. So all you need is to convert your CLLocation object to NSData as follow:
// saving your CLLocation object
let locationData = NSKeyedArchiver.archivedDataWithRootObject(your location here)
NSUserDefaults.standardUserDefaults().setObject(locationData, forKey: "locationData")
// loading it
if let loadedData = NSUserDefaults.standardUserDefaults().dataForKey("locationData") {
if let loadedLocation = NSKeyedUnarchiver.unarchiveObjectWithData(loadedData) as? CLLocation {
println(loadedLocation.coordinate.latitude)
println(loadedLocation.coordinate.longitude)
}
}

Deleting CKRecord from Cloudkit in swift

I am trying to have the users' location be updated by this function. Originally it was saving the users' location every time, but I added the code that will be below the comment. How can I make sure the record that is being deleted is the old location?
let locationRecord = CKRecord(recordType: "location")
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
self.mapView.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()//
locationRecord.setObject(location, forKey: "location")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(locationRecord) { record, error in
}
if error == nil
{
print("Location saved")
self.loc1 = location!
}
//testing code below
let operation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: [locationRecord.recordID])
operation.savePolicy = .AllKeys
operation.modifyRecordsCompletionBlock = { added, deleted, error in
if error != nil {
print(error)
} else {
print("location updated")
}
}
CKContainer.defaultContainer().publicCloudDatabase.addOperation(operation)
}
The best way to do this would be to update the existing one, as #Michael pointed out. The easy way to do this would be the first time you create the record, save the recordID to the defaults. For example,
func getRecordToUpdate(locations:[CLLocation])->CKRecord?{
let defaults = NSUserDefaults.standardUserDefaults()
var locationRecord:CKRecord
if defaults.objectForKey("locationRecordID") == nil{
//Create a new record and save its id
locationRecord = CKRecord(recordType: "location")
defaults.setObject(locationRecord.recordID, forKey: "locationRecordID")
self.updateLocationRecord(locationRecord, locations)
}else{
//Fetch the already existing record
let publicDB = CKContainer.defaultContainer().publicCloudDatabase
publicDB.fetchRecordWithID((defaults.objectForKey("locationRecordID") as! CKRecordID), completionHandler{
(record, error) in
if error == nil{
self.updateLocationRecord(record, locations)
}else{
print("Error fetching previous record")
}
}
}
}
Create a new function,
func updateLocationRecord(locationRecord:CKRecord, locations:[CLLocation]){
//Put your code from "let location..." to just above the testing code
//You may want to fix the block syntax in the call to saveRecord
}
Then in the original locationManager method, take out everything and put
self.getRecordsToUpdate(locations)

How to extract a function variable value and initialize it to another variable in a different function?

These are my two function and i want to transform the information from function to the other without need to declare a variable to store the information because always when i start my app the initial variable is the one that show up
func action(gestureRecognizer: UIGestureRecognizer) {
var touchPoint = gestureRecognizer.locationInView(self.mapView)
var location:CLLocationCoordinate2D = mapView.convertPoint(touchPoint, toCoordinateFromView: self.mapView)
var latDelta:CLLocationDegrees = 0.01
var lonDelta:CLLocationDegrees = 0.01
var span:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta:latDelta, longitudeDelta: lonDelta)
var region:MKCoordinateRegion = MKCoordinateRegion(center: location, span: span)
mapView.setRegion(region, animated: true)
var annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = ""
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var userLocation:CLLocation = locations[0] as! CLLocation
CLGeocoder().reverseGeocodeLocation(userLocation, completionHandler: { (placeMarks, error) -> Void in
if error != nil
{
println("error: \(error)")
}
else {
let place = CLPlacemark(placemark: placeMarks?[0] as! CLPlacemark)
}
})
}

Resources