0

I have an automatic location function in my app but since I updated to swift 2 I started to get the "Optional String" that appears before displaying the location details for each section, any suggestions on how to fix this?

Here is what it shows:

Optional("Cupertino")
Optional("95014")
Optional("CA")
Optional("United States")
--------------------
*** location: ***
Optional(<+37.33233141,-122.03121860> +/- 5.00m (speed 0.00 mps / course -1.00) @ 10/11/15, 11:05:28 PM British Summer Time)

Here is my code below

import UIKit
import Foundation
import MapKit
import CoreLocation
import SystemConfiguration
import MobileCoreServices

class GeoLocation: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    // GeoLocation IBOutlets Set

    @IBOutlet weak var mapView: MKMapView!
    @IBOutlet weak var myAddressView: UITextField!
    @IBOutlet weak var myLocationVIew: UITextField!

    @IBOutlet weak var BUTTONA: UIButton!

    @IBAction func BrightnessIncrease(sender: AnyObject) {

        UIScreen.mainScreen().brightness = CGFloat(1.0)
    }

    @IBAction func BrightnessDecrease(sender: AnyObject) {

        UIScreen.mainScreen().brightness = CGFloat(0.4)
    }

    var locationManager:CLLocationManager!

    override func viewDidLoad() {
        super.viewDidLoad()

        myAddressView.hidden = true
       myLocationVIew.hidden = true

        locationManager = CLLocationManager()
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
        locationManager.startUpdatingLocation()

        mapView.showsUserLocation = true
        mapView.delegate = self
        mapView.mapType = MKMapType.Hybrid
        CLLocationManager().requestAlwaysAuthorization()

        self.myAddressView.borderStyle = UITextBorderStyle.RoundedRect

        self.myLocationVIew.borderStyle = UITextBorderStyle.RoundedRect

        // Do any additional setup after loading the view, typically from a nib.
    }

    @IBAction func DATA2(sender: AnyObject) {

        if myAddressView.text != ""
        {
            SECTIONB = myAddressView.text
        }
        else
        {
            performSegueWithIdentifier("Transferfile", sender: sender)

            }
    }

    @IBAction func DATA3(sender: AnyObject) {

        if myLocationVIew.text != ""
        {
            SECTIONC = myLocationVIew.text
        }
        else
        {
            performSegueWithIdentifier("Transferfile", sender: sender)

        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // Map Display and Region Zoom

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let regionToZoom = MKCoordinateRegionMake(manager.location!.coordinate, MKCoordinateSpanMake(0.005, 0.005))

        mapView.setRegion(regionToZoom, animated: true)

        myLocationVIew.text = "\(locationManager.location)"

        CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: { (placemarks, error) -> Void in
            if error != nil {
                print("Error: " + error!.localizedDescription)
                return
            }
            if placemarks!.count > 0 {
            let pm = placemarks?[0] 
            self.displayLocationInfo(pm!)

            }
        })
    }

    func displayLocationInfo(placemark: CLPlacemark) {

        // This Section stops updating location constantly.

        //   self.locationManager.stopUpdatingLocation()

        // This Section display address parameters in a column on UILabel
        //            var address = (
        //                (placemark.subThoroughfare),
        //                (placemark.thoroughfare),
        //                (placemark.subLocality),
        //                (placemark.locality),
        //                (placemark.postalCode),
        //                (placemark.administrativeArea),
        //                (placemark.country))

        //          myAddressView.text = "\(address)"


        myAddressView.text = " \(placemark.subThoroughfare) \(placemark.thoroughfare) \r \(placemark.subLocality) \r \(placemark.locality) \(placemark.administrativeArea) \(placemark.postalCode) \r \(placemark.country)"

        print("-----START UPDATE-----")
        print(placemark.subThoroughfare)
        print(placemark.thoroughfare)
        print(placemark.locality)
        print(placemark.postalCode)
        print(placemark.administrativeArea)
        print(placemark.country)
        print("--------------------")
        print("*** location: ***")
        print(locationManager.location)
        print("--------------------")
        print("*** addressDictionary: ***")
        print(placemark.addressDictionary)
        print("-----END OF UPDATE-----")

    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("Error: " + error.localizedDescription)

    }
}

2 Answers2

1

Unwrap the strings with guard let or if let. If you option click on a placemark attribute like subThoroughfare it will say it is an optional string.

You can also unwrap like explained here

let subThoroughfare = placemark.subThoroughfare ?? ""

or

let subThoroughfare = placemark.subThoroughfare ?? "Default subThoroughfare"

Full example :

    let subThoroughfare = placemark.subThoroughfare ?? ""
    let thoroughfare = placemark.thoroughfare ?? ""
    let subLocality = placemark.subLocality ?? ""
    let locality = placemark.locality ?? ""
    let administrativeArea = placemark.administrativeArea ?? ""
    let postalCode = placemark.postalCode ?? ""
    let country = placemark.country ?? ""

    myAddressView.text = " \(subThoroughfare) \(thoroughfare) \r \(subLocality) \r \(locality) \(administrativeArea) \(postalCode) \r \(country)"

When unwrapping strings it makes sense to use the Nil Coalescing Operator. Follow the first link for more info. It allows you to easily unwrap the optional string or use an empty/default string when it is nil. For almost all other optionals you will want to use an if let or guard let statement.

Never force unwrap optional (using ! is called force unwrapping) values returned from an Framework like Core Location. There is no way for you to know all possible return values and when they will or will not be nil. Only ever force unwrap optionals when you created them and are absolutely sure they are not nil. For example, just after assigning a value it might be ok to force unwrap.


About optionals: optionals

About guard: guard keyword

Community
  • 1
  • 1
R Menke
  • 8,183
  • 4
  • 35
  • 63
  • @R Menke How do I unwrap myAddressView.text = " (placemark.subThoroughfare) (placemark.thoroughfare) \r (placemark.subLocality) \r (placemark.locality) (placemark.administrativeArea) (placemark.postalCode) \r (placemark.country)" and myLocationVIew.text = "\(locationManager.location)" ? – Gurvier Singh Dhillon Oct 12 '15 at 20:01
  • @R Menke I am new to swift, could you show me how to implement this function so that the two UITextfields don't show the optional string cheers. – Gurvier Singh Dhillon Oct 12 '15 at 20:21
  • @GurvierSinghDhillon No, I used your exact code. there is no implementation. SO is not a Fix-Your-Code-Squad. We are happy to help you with your specific problem. Copy pasting it back into your own code is your job. (because seriously, try the copy and paste.....) – R Menke Oct 12 '15 at 20:23
  • @R Menke Thanks for your help and time Sir, it is working now! Most Appreciated. – Gurvier Singh Dhillon Oct 12 '15 at 20:30
  • @GurvierSinghDhillon I knew you could do it! welcome to swift and SO! – R Menke Oct 12 '15 at 20:31
  • @R Menke I will keep note of this for future reference, many thanks again for your assistance. – Gurvier Singh Dhillon Oct 12 '15 at 20:38
0

It print the Optional String because the variable you try to print is the Optional variable, it means that variable may or may not hold an value.

If you sure the variable has held the value, just put an ! at the end of the variable to unwrap it.

    print(placemark.subThoroughfare!)
    print(placemark.thoroughfare!)
    print(placemark.locality!)
    print(placemark.postalCode!)
    print(placemark.administrativeArea!)

If you NOT sure, try check if it equal nil (by using Nil Coalescing Operator), and print the default value if it's nil:

    print(placemark.subThoroughfare ?? "")
    print(placemark.thoroughfare ?? "")
    print(placemark.locality ?? "")
    print(placemark.postalCode ?? "")
    print(placemark.administrativeArea ?? "")
t4nhpt
  • 5,264
  • 4
  • 34
  • 43
  • @t4nhp The second approach worked however my location details also displays in a 2 uitextfields, these are not unwrapping too. for example myAddressView.text = " \(placemark.subThoroughfare) \(placemark.thoroughfare) \r \(placemark.subLocality) \r \(placemark.locality) \(placemark.administrativeArea) \(placemark.postalCode) \r \(placemark.country)" and myLocationVIew.text = "\(locationManager.location)" – Gurvier Singh Dhillon Oct 12 '15 at 19:40