iOS charts what to do with empty dataset - ios

I use charts for my project. However, I now run into a problem that if a dataset is empty I will all report error when I see debug area.
Now I am trying to find a solution to be able to stop creating the chart if the dataset is empty.
the error message I get 25x :
[32865:1852701] [Unknown process name] CGAffineTransformInvert:
singular matrix.
for now I have this solution but I don't know if there is a better one.
var gewichtenHond: [GewichtHond] = [] {
didSet {
if self.gewichtenHond.count == 0 {
self.lineChartView.noDataText = "Geen data beschikbaar om een grafiek te tekenen."
} else {
grafiekData(animatieSnelheid: 1.0, typeAnimatie: .easeInBounce)
}
tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getGewichtenHond()
}
func getGewichtenHond() {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let context = appDelegate?.persistentContainer.viewContext
let hondFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Hond")
hondFetch.predicate = NSPredicate(format: "hondId = %#", hondId)
let honden = try! context?.fetch(hondFetch)
let hond: Hond = honden?.first as! Hond
self.gewichtenHond = hond.gewichten?.allObjects as! [GewichtHond]
}

for now I have this solution but I don't know if there is a better one.
I can see from your code that you are trying to get the data each time you open the scene, if so try this approach
class UIViewController : UIViewController{
var gewichtenHond: [GewichtHond] = [] {
didSet {
grafiekData(animatieSnelheid: 1.0, typeAnimatie: .easeInBounce)
tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let context = appDelegate?.persistentContainer.viewContext
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.lineChartView.noDataText = "Geen data beschikbaar om een grafiek te tekenen."
getGewichtenHond()
}
func getGewichtenHond() {
let hondFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Hond")
hondFetch.predicate = NSPredicate(format: "hondId = %#", hondId)
let honden = try! context?.fetch(hondFetch)
let hond: Hond = honden?.first as! Hond
self.gewichtenHond = hond.gewichten?.allObjects as! [GewichtHond]
}
}
if you are just trying to get them just once as you open the scene i suggest moving
those two line
self.lineChartView.noDataText = "Geen data beschikbaar om een grafiek te tekenen."
getGewichtenHond()
inside the viewDidLoad method because viewWillAppear will trigger every time you leave the scene and get back to it

Related

How to detect jump with Core Motion?

I want to make jump rope counter, but I don't know how to detect jump and I did't find any solution for it. Please, help!
final class WorkoutViewController: UIViewController {
private var motionManager = CMMotionManager()
private var jumpCounter = 0
func jump() {
let xAxis = motionManager.deviceMotion!.userAcceleration.x
let yAxis = motionManager.deviceMotion!.userAcceleration.y
let zAxis = motionManager.deviceMotion!.userAcceleration.z
let xGravity = motionManager.deviceMotion!.gravity.x
let yGravity = motionManager.deviceMotion!.gravity.y
let zGravity = motionManager.deviceMotion!.gravity.z
// I don't know how to detect a jump here
}
override func viewDidLoad() {
super.viewDidLoad()
motionManager.deviceMotionUpdateInterval = self.updateDataInterval
motionManager.startDeviceMotionUpdates(to: .main) { (_, _) in
self.jump()
}
}
}

NSMetadataQuery isUpdating breaks after Reachability changes to none Swift 4

I have coded a UIViewController that handles the upload (Only) for files to iCloud. So far it works well but I was trying to make it better with Network Changes using Reachability.
The way I handle the changes is:
lazy var searchQuery:NSMetadataQuery = {
let searchQueryTemp = NSMetadataQuery()
searchQueryTemp.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope]
let searchPredicate = NSPredicate.init(format: "%K BEGINSWITH %# && NOT %K.pathExtension = ''", argumentArray: [NSMetadataItemPathKey,trackFileManager.appICloudExportedMusic!.path,NSMetadataItemFSNameKey])
searchQueryTemp.predicate = searchPredicate
return searchQueryTemp
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("\(logClassName): viewWillAppear")
appDelegate.appReachabilityDelegate = self
NotificationCenter.default.addObserver(self, selector: #selector(updateDataWithNotification), name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: searchQuery)
NotificationCenter.default.addObserver(self, selector: #selector(updateDataWithNotification), name: NSNotification.Name.NSMetadataQueryDidUpdate, object: searchQuery)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateSearch()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("\(logClassName): viewWillDisappear")
NotificationCenter.default.removeObserver(self)
}
#objc func updateDataWithNotification(notification: NSNotification){
print("\(logClassName): updateDataWithNotification \(searchQuery.results.count)")
if let queryObject = notification.object as? NSMetadataQuery{
if searchQuery == queryObject{
print("\(logClassName): Query count = \(queryObject.results.count)")
var iCloudAlbumArrayTemp = [FileIcloudAlbumHeader]()
/* Get the query results [URL] only files */
for result in queryObject.results{
/* Get Metadata for file */
if let metadataItem = result as? NSMetadataItem{
if let urlItem:URL = metadataItem.value(forKey: NSMetadataUbiquitousItemURLInLocalContainerKey) as? URL{
if var urlItemPath = urlItem.path.components(separatedBy: "Exported Music").last{
urlItemPath = String(urlItemPath.dropFirst())
let urlItemArray = urlItemPath.components(separatedBy: "/")
if urlItemArray.count == 3{
var albumICloudTrack = AlbumICloudTrack(url: urlItem)
let isUpdated:Bool = metadataItem.value(forKey: NSMetadataUbiquitousItemIsUploadedKey) as! Bool
let isUpdating: Bool = metadataItem.value(forKey: NSMetadataUbiquitousItemIsUploadingKey) as! Bool
let isDownloading:Bool = metadataItem.value(forKey: NSMetadataUbiquitousItemIsDownloadingKey) as! Bool
if isUpdated{
albumICloudTrack.status = .available
}
else{
if isUpdating{
let perInt = Int(metadataItem.value(forKey: NSMetadataUbiquitousItemPercentUploadedKey) as! Double)
print("\(logClassName): isUpdating: PerInt = \(perInt)")
let perDouble = metadataItem.value(forKey: NSMetadataUbiquitousItemPercentUploadedKey) as! Double
print("\(logClassName): isUpdating: PerInt = \(perDouble)")
albumICloudTrack.percentatge = Int(metadataItem.value(forKey: NSMetadataUbiquitousItemPercentUploadedKey) as! Double)
albumICloudTrack.status = .updating
}
else if isDownloading{
albumICloudTrack.status = .downloading
}
else{
albumICloudTrack.status = .notAvailable
}
}
/* Find Album */
var tempUrl = urlItem.deletingLastPathComponent()
let albumName = tempUrl.lastPathComponent
tempUrl = tempUrl.deletingLastPathComponent()
let artistName = tempUrl.lastPathComponent
//print("\(logClassName): Artist Name = \(artistName) && Album Name = \(albumName)")
let albumHeaderInex = findAlbumHeader(withArtistName: artistName, andAlbum: albumName, in: iCloudAlbumArrayTemp)
if albumHeaderInex != -1{
//print("\(logClassName): Appending Already exists")
iCloudAlbumArrayTemp[albumHeaderInex].urlTrackArray.append(albumICloudTrack)
}
else{
//print("\(logClassName): Creating New Header Album")
var albumHeader = FileIcloudAlbumHeader(artistName: artistName, albumName: albumName, url: urlItem.deletingLastPathComponent())
albumHeader.urlTrackArray.append(albumICloudTrack)
iCloudAlbumArrayTemp.append(albumHeader)
}
}
else{
print("\(logClassName): Discarting Item = \(urlItemPath)")
}
}
}
}
}
/* Copy content for updating Expanded status */
for iCloudAlbumIndex in iCloudAlbumArray.indices{
for iCloudAlbumTempIndex in iCloudAlbumArrayTemp.indices{
if iCloudAlbumArray[iCloudAlbumIndex].artistName == iCloudAlbumArrayTemp[iCloudAlbumTempIndex].artistName && iCloudAlbumArray[iCloudAlbumIndex].albumName == iCloudAlbumArrayTemp[iCloudAlbumTempIndex].albumName{
iCloudAlbumArrayTemp[iCloudAlbumTempIndex].isSelected = iCloudAlbumArray[iCloudAlbumIndex].isSelected
}
}
}
iCloudAlbumArray.removeAll()
for iCloudAlbumTempIndex in iCloudAlbumArrayTemp.indices{
iCloudAlbumArray.append(iCloudAlbumArrayTemp[iCloudAlbumTempIndex])
iCloudAlbumArray[iCloudAlbumTempIndex].urlTrackArray.sort {
return $0.trackName < $1.trackName
}
}
/* Reload table */
iCloudExportsTableView.reloadData()
}
}
}
The main problem here is that I see the file is updating in "Files" but
let isUpdating: Bool = metadataItem.value(forKey: NSMetadataUbiquitousItemIsUploadingKey) as! Bool
returns false
What am I not taking in consideration?
Thank you in advance
I have realised that despite NSMetadataUbiquitousItemIsUploadingKey returns false, NSMetadataUbiquitousItemPercentUploadedKey stills returns a number so:
let perDouble = metadataItem.value(forKey: NSMetadataUbiquitousItemPercentUploadedKey) as? Double ?? 100.00
if isUpdated{
albumICloudTrack.status = .available
}
else{
if isUpdating || perDouble < 100.00{
print("\(logClassName): isUpdating: perDouble = \(perDouble)")
albumICloudTrack.percentatge = Int(metadataItem.value(forKey: NSMetadataUbiquitousItemPercentUploadedKey) as! Double)
albumICloudTrack.status = .updating
}
Any other thoughts?

Getting a nil in Swift 3?

I am creating an app where I have annotation view showing that when you click it shows on the DetailsViewController that annotation data. However, I get "Name", and "Address" data but for phone Number I am getting set as nil. So, if you guys can see my code & help me solve it, I will be appreciated it.
Here is my code:
import UIKit
import MapKit
protocol UserLocationDelegate {
func userLocation(latitude :Double, longitude :Double)
}
class NearMeMapViewController: ARViewController, ARDataSource, MKMapViewDelegate, CLLocationManagerDelegate {
var nearMeIndexSelected = NearMeIndexTitle ()
var locationManager : CLLocationManager!
var nearMeARAnnotations = [ARAnnotation]()
var nearMeRequests = [NearMeRequest]()
var delegate : UserLocationDelegate!
var place: Place?
override func viewDidLoad() {
super.viewDidLoad()
self.title = nearMeIndexSelected.indexTitle
self.locationManager = CLLocationManager ()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLHeadingFilterNone
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.dataSource = self
self.headingSmoothingFactor = 0.05
self.maxVisibleAnnotations = 30
getNearMeIndexSelectedLocation()
}
func getNearMeIndexSelectedLocation()
{
let nearMeRequest = MKLocalSearchRequest()
nearMeRequest.naturalLanguageQuery = nearMeIndexSelected.indexTitle
let nearMeregion = MKCoordinateRegionMakeWithDistance(self.locationManager.location!.coordinate, 250, 250)
nearMeRequest.region = nearMeregion
let nearMeSearch = MKLocalSearch(request: nearMeRequest)
nearMeSearch.start { (response : MKLocalSearchResponse?, error :Error?) in
for requestItem in (response?.mapItems)! {
let nearMeIndexRequest = NearMeRequest ()
nearMeIndexRequest.name = requestItem.name
nearMeIndexRequest.coordinate = requestItem.placemark.coordinate
nearMeIndexRequest.address = requestItem.placemark.addressDictionary?["FormattedAddressLines"] as! [String]
nearMeIndexRequest.street = requestItem.placemark.addressDictionary?["Street"] as! String!
nearMeIndexRequest.city = requestItem.placemark.addressDictionary?["City"] as! String
nearMeIndexRequest.state = requestItem.placemark.addressDictionary?["State"] as! String
nearMeIndexRequest.zip = requestItem.placemark.addressDictionary?["ZIP"] as! String
self.nearMeRequests.append(nearMeIndexRequest)
print(requestItem.placemark.name)
}
for nearMe in self.nearMeRequests {
let annotation = NearMeAnnotation(nearMeRequest: nearMe)
self.nearMeARAnnotations.append(annotation)
self.setAnnotations(self.nearMeARAnnotations)
}
}
}
func ar(_ arViewController: ARViewController, viewForAnnotation: ARAnnotation) -> ARAnnotationView {
let annotationView = NearMeARAnnotationView(annotation: viewForAnnotation)
// annotationView.delegate = self
annotationView.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapBlurButton(_:)))
annotationView.addGestureRecognizer(tapGesture)
return annotationView
}
func tapBlurButton(_ sender: UITapGestureRecognizer) {
if let annotationView = sender.view as? NearMeARAnnotationView {
if let detailsVc = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController")
as? DetailsViewController {
detailsVc.annotation = annotationView.annotation
if let annotation = annotationView.annotation as? Place {
detailsVc.place = annotation
}
self.navigationController?.pushViewController(detailsVc, animated: true)
}
}
}
}
Just looking over your code quickly:
"\(nearMeAnnotation.nearMeRequest.phone)"
All the other ones have a forced unwrap, this one doesn't. Most probably the value is nil and since you ask for a string representation of a wrapped var that might really be nil sometimes.
I think you should use a default value everywhere instead of a forced unwrap, like:
"\(nearMeAnnotation.nearMeRequest.phone ?? "")"
but also:
"\(nearMeAnnotation.nearMeRequest.street ?? "") \(nearMeAnnotation.nearMeRequest.state ?? "") \(nearMeAnnotation.nearMeRequest.state ?? "") \(nearMeAnnotation.nearMeRequest.zip ?? "")"
With forced unwraps your application will crash if a certain value is not set. This could be handled more elegantly if they're really required, for example already in the constructor of your object. There's the root cause of the optionals you're seeing. In this case NearMeAnnotation.

How do i use mapbox's new MGLOfflinePackDelegate correctly?

I'm creating an app which needs an offline map. I'm testing with MapBox, which supports offline maps since today (yay!). The code I have now seems to work for downloading the map, but the delegate to report on progress never triggers, and I don't have a clue why this is.
I have this class for my mapView:
import UIKit
import Mapbox
class MapController: UIViewController, MGLMapViewDelegate, UIPopoverPresentationControllerDelegate {
#IBOutlet var mapView: MGLMapView!
override func viewDidLoad() {
super.viewDidLoad()
downloadIfNeeded()
mapView.maximumZoomLevel = 18
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func downloadIfNeeded() {
MGLOfflineStorage.sharedOfflineStorage().getPacksWithCompletionHandler { (packs, error) in guard error == nil else {
return
}
for pack in packs {
let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
if userInfo["name"] == "London" {
// allready downloaded
return
}
}
// define the download region
let sw = CLLocationCoordinate2DMake(51.212120, 4.415906)
let ne = CLLocationCoordinate2DMake(51.223781, 4.442401)
let bounds = MGLCoordinateBounds(sw: sw, ne: ne)
let region = MGLTilePyramidOfflineRegion(styleURL: MGLStyle.streetsStyleURL(), bounds: bounds, fromZoomLevel: 10, toZoomLevel: 12)
let userInfo = ["name": "London"]
let context = NSKeyedArchiver.archivedDataWithRootObject(userInfo)
MGLOfflineStorage.sharedOfflineStorage().addPackForRegion(region, withContext: context) { (pack, error) in
guard error == nil else {
return
}
// create popup window with delegate
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let downloadProgress: MapDownloadController = storyboard.instantiateViewControllerWithIdentifier("MapDownloadController") as! MapDownloadController
downloadProgress.modalPresentationStyle = .Popover
downloadProgress.preferredContentSize = CGSizeMake(300, 150)
let popoverMapDownloadController = downloadProgress.popoverPresentationController
popoverMapDownloadController?.permittedArrowDirections = .Any
popoverMapDownloadController?.delegate = self
popoverMapDownloadController?.sourceView = self.mapView
popoverMapDownloadController?.sourceRect = CGRect(x: self.mapView.frame.midX, y: self.mapView.frame.midY, width: 1, height: 1)
self.presentViewController(downloadProgress, animated: true, completion: nil)
// set popup as delegate <----
pack!.delegate = downloadProgress
// start downloading
pack!.resume()
}
}
}
}
And the MapDownloadController is a View which is displayed as popup (see code above) and has the MGLOfflinePackDelegate:
import UIKit
import Mapbox
class MapDownloadController: UIViewController, MGLOfflinePackDelegate {
#IBOutlet var progress: UIProgressView!
#IBOutlet var progressText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func offlinePack(pack: MGLOfflinePack, progressDidChange progress: MGLOfflinePackProgress) {
// this function is never called, but why? <----
let completed = progress.countOfResourcesCompleted
let expected = progress.countOfResourcesExpected
let bytes = progress.countOfBytesCompleted
let MB = bytes / 1024
let str: String = "\(completed)/\(expected) voltooid (\(MB)MB)"
progressText.text = str
self.progress.setProgress(Float(completed) / Float(expected), animated: true)
}
func offlinePack(pack: MGLOfflinePack, didReceiveError error: NSError) {
// neither is this one... <----
let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
let strError = error.localizedFailureReason
}
func offlinePack(pack: MGLOfflinePack, didReceiveMaximumAllowedMapboxTiles maximumCount: UInt64) {
// .. or this one <----
let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
}
}
This is all pretty much taken from the documentation, so why are the delegate's functions (func offlinePack) never called? I did test with breakpoints so i am sure it is not. Still, the popup is shown and the region gets downloaded. (Checked with observing network traffic and with other code which lists the offline packs.)
Here’s an extremely simple implementation of Minh’s answer, using the current v3.2.0b1 example code. Expect this answer to become outdated quickly, as we’re still working on the v3.2.0 release.
import UIKit
import Mapbox
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate, MGLOfflinePackDelegate {
#IBOutlet var mapView: MGLMapView!
// Array of offline packs for the delegate work around (and your UI, potentially)
var offlinePacks = [MGLOfflinePack]()
override func viewDidLoad() {
super.viewDidLoad()
mapView.maximumZoomLevel = 2
downloadOffline()
}
func downloadOffline() {
// Create a region that includes the current viewport and any tiles needed to view it when zoomed further in.
let region = MGLTilePyramidOfflineRegion(styleURL: mapView.styleURL, bounds: mapView.visibleCoordinateBounds, fromZoomLevel: mapView.zoomLevel, toZoomLevel: mapView.maximumZoomLevel)
// Store some data for identification purposes alongside the downloaded resources.
let userInfo = ["name": "My Offline Pack"]
let context = NSKeyedArchiver.archivedDataWithRootObject(userInfo)
// Create and register an offline pack with the shared offline storage object.
MGLOfflineStorage.sharedOfflineStorage().addPackForRegion(region, withContext: context) { (pack, error) in
guard error == nil else {
print("The pack couldn’t be created for some reason.")
return
}
// Set the pack’s delegate (assuming self conforms to the MGLOfflinePackDelegate protocol).
pack!.delegate = self
// Start downloading.
pack!.resume()
// Retain reference to pack to work around it being lost and not sending delegate messages
self.offlinePacks.append(pack!)
}
}
func offlinePack(pack: MGLOfflinePack, progressDidChange progress: MGLOfflinePackProgress) {
let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
let completed = progress.countOfResourcesCompleted
let expected = progress.countOfResourcesExpected
print("Offline pack “\(userInfo["name"])” has downloaded \(completed) of \(expected) resources.")
}
func offlinePack(pack: MGLOfflinePack, didReceiveError error: NSError) {
let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
print("Offline pack “\(userInfo["name"])” received error: \(error.localizedFailureReason)")
}
func offlinePack(pack: MGLOfflinePack, didReceiveMaximumAllowedMapboxTiles maximumCount: UInt64) {
let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
print("Offline pack “\(userInfo["name"])” reached limit of \(maximumCount) tiles.")
}
}
(Cross-posted from this GitHub issue.)
This is a bug in the SDK. The workaround is for the completion handler to assign the passed-in MGLOfflinePack object to an ivar or other strong reference in the surrounding MapDownloadController class (example).

Swift - fatal error: unexpectedly found nil while unwrapping an Optional value on MKCoordinateRegionMakeWithDistance

I'm trying to get my map to show directions to a local searched location from the current user location.
I'm getting an EXC_BAD_INSTRUCTION on the line:
let region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 2000, 2000)`
And on the line:
else {
self.showRoute(response)
}
I have a feeling the nil it's receiving is from the user location, which I'm not sure why it would be receiving a nil there.
Here is the full code for my view controller if needed:
class RouteViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var routeMap: MKMapView!
var destination = MKMapItem?()
override func viewDidLoad() {
super.viewDidLoad()
routeMap.showsUserLocation = true
routeMap.delegate = self
self.getDirections()
}
func getDirections() {
let request = MKDirectionsRequest()
request.setSource(MKMapItem.mapItemForCurrentLocation())
request.setDestination(destination!)
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler({(response:
MKDirectionsResponse!, error: NSError!) in
if error != nil {
println("Error getting directions")
} else {
self.showRoute(response)
}
})
}
func showRoute(response: MKDirectionsResponse) {
for route in response.routes as! [MKRoute] {
routeMap.addOverlay(route.polyline,
level: MKOverlayLevel.AboveRoads)
for step in route.steps {
println(step.instructions)
}
}
let userLocation = routeMap.userLocation
let region = MKCoordinateRegionMakeWithDistance(
userLocation.location.coordinate, 2000, 2000)
routeMap.setRegion(region, animated: true)
}
func mapView(mapView: MKMapView!, rendererForOverlay
overlay: MKOverlay!) -> MKOverlayRenderer! {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blueColor()
renderer.lineWidth = 5.0
return renderer
}
}
And the segue code:
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
let routeViewController = segue.destinationViewController
as! RouteViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let row = indexPath?.row
routeViewController.destination = mapItems[row!]
}
Any help would be appreciated, thanks!
The problem is this line:
let userLocation = routeMap.userLocation
The result, userLocation might be nil, and its location might be nil, because the map might not have been told to, or succeeded in acquiring, the user's location. You are not taking into account that possibility.
The way to do that is to unwrap the Optionals safely and proceed only if the unwrapping succeeded:
if let userLocation = routeMap.userLocation, loc = userLocation.location {
let region = MKCoordinateRegionMakeWithDistance(
loc.coordinate, 2000, 2000)
routeMap.setRegion(region, animated: true)
}
We don't need the userLocation separately for anything, so we can collapse that into a single test:
if let loc = routeMap.userLocation.location {
let region = MKCoordinateRegionMakeWithDistance(
loc.coordinate, 2000, 2000)
routeMap.setRegion(region, animated: true)
}

Resources