How to make permanent storage for an iOS app? - ios

I am doing an application where user can select buildings on the map and save them in the list. Everything is fine but I need to save users selection after app closing.
func action(_ gestureRecognizer:UIGestureRecognizer) { // GESTURE RECOGNIZER FUNCTION
if gestureRecognizer.state == UIGestureRecognizerState.began {
let touchPoint = gestureRecognizer.location(in: self.map)
let newCoordinate = self.map.convert(touchPoint, toCoordinateFrom: self.map)
let location = CLLocation(latitude: newCoordinate.latitude, longitude: newCoordinate.longitude)
CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
var title = ""
if (error == nil) {
if let p = placemarks?[0] {
var subThoroughfare:String = ""
var thoroughfare:String = ""
if p.subThoroughfare != nil {
subThoroughfare = p.subThoroughfare!
}
if p.thoroughfare != nil {
thoroughfare = p.thoroughfare!
}

You can save the building ID into an array, and save that array into UserDefaults.
UserDefaults.standard.set(value: buildingIds, forKey: "myBuildingIds")
When you open the app, simply ask:
if let myBuildingArrays = UserDefaults.standard.value(forKey: "myBuildingIds"){
//And here you iterate each value of the array to load them into your list.
}
More information: https://developer.apple.com/reference/foundation/userdefaults

Related

How to delete the last annotation made on a MapView?

#IBAction func addPin(_ sender: UILongPressGestureRecognizer) {
let location = sender.location(in: self.mapView)
let locCoord = self.mapView.convert(location, toCoordinateFrom: self.mapView)
let annotation = MKPointAnnotation()
annotation.coordinate = locCoord
annotation.title = titleTextField.text
var annotationsArray: [[String:Any]]!
var annotationsData = UserDefaults.standard.data(forKey: "StoredAnnotations")
//If the data is nil, then set the new annotation as the only element in the array
if annotationsData == nil {
annotationsArray = [newAnnotationDict]
} else {
//If it isn't nil, then convert the data into an array of dicts
do {
//Convert this data into an array of dicts
annotationsArray = try JSONSerialization.jsonObject(with: annotationsData!, options: []) as! [[String:Any]]
annotationsArray.append(newAnnotationDict)
} catch {
print(error.localizedDescription)
}
}
do {
//Use JSONSerialization to convert the annotationsArray into Data
let jsonData = try JSONSerialization.data(withJSONObject: annotationsArray, options: .prettyPrinted)
//Store this data in UserDefaults
UserDefaults.standard.set(jsonData, forKey: "StoredAnnotations")
} catch {
print(error.localizedDescription)
}
}
#IBAction func deleteLast(sender: UIButton){
}
I am currently creating annotations on a MapView using the code given above. I would like to delete the previous annotation that was created by the user. How would I do this by using the deleteLast button?

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.

How to Force Default Language to English in CLLocationManager?

SO, I am new to swift and I made the conversion from current Lat and Long to City name and Country, it works fine like that:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
if didFindLocation == false
{
didFindLocation = true
locationManager.stopUpdatingLocation()
userLocation = locations[0]
long = userLocation.coordinate.longitude;
lat = userLocation.coordinate.latitude;
print("\(lat),\(long)")
converLocationToCity()
}
}
func converLocationToCity()
{
let geoCoder = CLGeocoder()
userLocation = CLLocation(latitude: self.lat, longitude: self.long)
geoCoder.reverseGeocodeLocation(userLocation, completionHandler:
{
(placemarks, error) -> Void in
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
if let city = placeMark.addressDictionary!["State"] as? String
{
self.city = city as String
} else
{
self.city = ""
}
if let country = placeMark.addressDictionary!["Country"] as? String
{
self.country = country as String
} else
{
self.country = ""
}
self.currentCity.name = ("\(self.city), \(self.country)" as String)
print("\(self.currentCity.name)")
self.fetchWeather.performCurrentWeatherFetch(forSelectedCity: self.currentCity.name)
DispatchQueue.main.async()
{
(self.superview as! UICollectionView).reloadData()
}
})
}
But when the device is set to other language, Russian for example it returns me the City Name and Country in Russian characters, but I need it to be only in english, please anybody some ideas or suggestions? Thank you!
Here is My Solution
While getting the location data i change `UserDefaults.standard.set(["base"], forKey: "AppleLanguages")'
and once I have received the dictionary in English i remove the Object
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
which then sets applelanguage to default value
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
// Call stopUpdatingLocation() to stop listening for location updates,
// other wise this function will be called every time when user location changes.
// manager.stopUpdatingLocation()
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
//location.accessibilityLanguage = "en-US"
UserDefaults.standard.set(["base"], forKey: "AppleLanguages")
geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else {
return
}
print(addressDict)
// Print each key-value pair in a new row
addressDict.forEach { print($0) }
// Print fully formatted address
if let formattedAddress = addressDict["FormattedAddressLines"] as? [String] {
print(formattedAddress.joined(separator: ", "))
}
// Access each element manually
if let locationName = addressDict["Name"] as? String {
print(locationName)
}
if let street = addressDict["Thoroughfare"] as? String {
print(street)
}
var myCity:String = ""
if let city = addressDict["City"] as? String {
print(city)
if(city != "" ){
myCity = city
}
}
if let zip = addressDict["ZIP"] as? String {
print(zip)
}
var myCountry:String = ""
if let country = addressDict["Country"] as? String {
print(country)
if(country != "" ){
myCountry = country
}
MyGenericFunctions.sharedInstance.saveCountry(country: country)
}
manager.stopUpdatingLocation()
if(myCity != "" && myCountry != "" && self.isCurrLocAPICalled != true){
print("API Called")
self.isCurrLocAPICalled = true
self.callLocationSearch(strCity: myCity, strCountry: myCountry)
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
}
})
//manager.stopUpdatingLocation()
}

Changing a variable value inside reverseGeocodeLocation (Swift2)

I am having an issue changing a string variable value the code looks like this
self.mapView is an MKMapView
what the code does: It grabs the finger touch point and put an annotation in its place and by using CLGeocoder().reverseGeocodeLocation I want to grab the person Address but when I try to store the address in a variable it just returns nil meanwhile directly using annotation.title inside CLGeocoder().reverseGeocodeLocation it does change its value what's the deal here?
func longPressAction(gestureRecognizer: UIGestureRecognizer) {
if (gestureRecognizer.state == UIGestureRecognizerState.Began) {
let touchPoint = gestureRecognizer.locationInView(self.mapView);
let annotation = MKPointAnnotation();
let newCoords = self.mapView.convertPoint(touchPoint, toCoordinateFromView: self.mapView);
let location = CLLocation(latitude: newCoords.latitude, longitude: newCoords.longitude);
var address:String = ""; // Doesn't store here!
CLGeocoder().reverseGeocodeLocation(location) { (placemarks, error) -> Void in
if (error != nil) {
} else {
if let p = placemarks?[0] {
if (p.subThoroughfare != nil) {
address = p.subThoroughfare!;
} else if (p.subThoroughfare == nil && p.thoroughfare != nil) {
address = p.thoroughfare!;
} else if (p.subThoroughfare == nil && p.thoroughfare == nil && p.country != nil) {
address = p.country!;
} else if (p.subThoroughfare == nil && p.thoroughfare == nil && p.country == nil) {
address = "New Place";
}
}
}
}
print(address)
annotation.title = address;
annotation.coordinate = newCoords;
self.mapView.addAnnotation(annotation);
sharedAnnotations.addObject(annotation);
}
Output of print(address) is nil
This happens because you're setting address inside a block. This block is called asynchronously by CLGeocoder().reverseGeocodeLocation when it finishes getting the reverse location. That's why when you try to print it right away in your function, it's empty. If you print it inside the block, you'll see it has the correct value.
Your function could look something like this:
func longPressAction(gestureRecognizer: UIGestureRecognizer) {
if (gestureRecognizer.state == UIGestureRecognizerState.Began) {
let touchPoint = gestureRecognizer.locationInView(self.mapView)
let annotation = MKPointAnnotation()
let newCoords = self.mapView.convertPoint(touchPoint, toCoordinateFromView: self.mapView)
let location = CLLocation(latitude: newCoords.latitude, longitude: newCoords.longitude)
CLGeocoder().reverseGeocodeLocation(location) { [weak self] (placemarks, error) -> Void in
if let strongSelf = self{
if (error != nil) {
} else {
if let p = placemarks?[0] {
var address:String = ""
if (p.subThoroughfare != nil) {
address = p.subThoroughfare!
} else if (p.subThoroughfare == nil && p.thoroughfare != nil) {
address = p.thoroughfare!
} else if (p.subThoroughfare == nil && p.thoroughfare == nil && p.country != nil) {
address = p.country!
} else if (p.subThoroughfare == nil && p.thoroughfare == nil && p.country == nil) {
address = "New Place"
}
print(address)
annotation.title = address
annotation.coordinate = newCoords
strongSelf.mapView.addAnnotation(annotation)
strongSelf.sharedAnnotations.addObject(annotation)
}
}
}
}
}
I show you my example
func returnPlaceMark(coordinate: CLLocationCoordinate2D, completaion: (superPlace: MKPlacemark) -> ()) {
let geocoder = CLGeocoder()
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
geocoder.reverseGeocodeLocation(location) { (arrayPlaceMark, error) -> Void in
if error == nil {
let firstPlacemark = arrayPlaceMark!.first!
completaion(superPlace: MKPlacemark(placemark: firstPlacemark))
}
}

MKMapView Select Annotation

I'm new to swift and currently trying to figure out how to get data about the annotation that the user has selected. I have a localsearch function that will add the annotations, and after the user selects one I'd like to be able to access that. I'm trying to use selectedAnnotations, but it doesn't seem to be working.
Localsearch:
func performSearch(){
matchingItems.removeAll()
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchTextField.text
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler({(response:
MKLocalSearchResponse!,
error: NSError!) in
if error != nil {
println("Error occured in search: \(error.localizedDescription)")
} else if response.mapItems.count == 0 {
println("No matches found")
} else {
println("Matches found")
for item in response.mapItems as [MKMapItem] {
println("Name = \(item.name)")
println("Phone = \(item.phoneNumber)")
self.matchingItems.append(item as MKMapItem)
println("Matching items = \(self.matchingItems.count)")
var annotation = MKPointAnnotation()
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name
annotation.subtitle = item.placemark.title
self.mapView.addAnnotation(annotation)
}
}
})
From there I'm trying to use
var selectedAnnotations: [MKPointAnnotation]!
// print signout location
println(selectedAnnotations)
To access the annotation, but this is just returning "nil"
Method for annotation:
#IBAction func signoutToLocationButton(sender: AnyObject) {
// saves current user location
PFGeoPoint.geoPointForCurrentLocationInBackground {
(geoPoint: PFGeoPoint!, error: NSError!) -> Void in
if error == nil {
// do something with the new geoPoint
println(geoPoint)
var signoutLocation = PFObject(className: "SignoutLocation")
signoutLocation["Location"] = geoPoint
signoutLocation.saveInBackgroundWithBlock {
(success: Bool, error: NSError!)-> Void in
if (success) {
// has been saved
}
else {
//went wrong
}
}
}
// get location of where they are signing out to
self.mapView.selectedAnnotations(AnyObject)
// print signout location
// println(selectedAnnotations)
}
Here's an example of how to use the selectedAnnotations property:
if self.mapView.selectedAnnotations?.count > 0 {
if let ann = self.mapView.selectedAnnotations[0] as? MKAnnotation {
println("selected annotation: \(ann.title!)")
let c = ann.coordinate
println("coordinate: \(c.latitude), \(c.longitude)")
//do something else with ann...
}
}
(Though whether you need to or want to do this inside your // has been saved block instead of outside is something you'll have to figure out.)

Resources