CLLocationManager - fatal error: unexpectedly found nil while unwrapping an Optional value - cllocationmanager

I have a little problem with CLLocationManager. When I click on IBAction "addPlace", the app crash. I don't see the problem, and I don't understand why the app crash.
Thank you in advance for your response.
class Map: UIViewController, CLLocationManagerDelegate {
// Outlets
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var BTNAdd: UIButton!
// Variables
var positionManager = CLLocationManager()
var geoCoder : CLGeocoder!
var placemark : CLPlacemark!
var name: String = ""
var address: String = ""
var longitude: String = ""
var latitude: String = ""
// Actions
#IBAction func myPosition(sender: AnyObject) {
positionManager.delegate = self
positionManager.desiredAccuracy = kCLLocationAccuracyBest
positionManager.requestWhenInUseAuthorization()
positionManager.startUpdatingLocation()
mapView.showsUserLocation = true
}
#IBAction func addPlace(sender: AnyObject) {
///////////////////////////////////////
// BUG HERE
///////////////////////////////////////
let locality = String(self.placemark.locality)
let postalCode = String(self.placemark.postalCode)
let administrativeArea = String(self.placemark.administrativeArea)
let country = String(self.placemark.country)
print(locality + postalCode + administrativeArea + country)
///////////////////////////////////////
///////////////////////////////////////
}
// Functions
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if let pm = placemarks?.first {
self.displayLocationInfo(pm)
}
})
}
func displayLocationInfo(placemark: CLPlacemark?) {
if let containsPlacemark = placemark {
positionManager.stopUpdatingLocation()
let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : ""
let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : ""
let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
let country = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
}
}
}

Your placemark variable is declared as an explicitly unwrapped optional, but since you get this error message it's obvious that sometimes this variable will be nil.
Change the declaration to make it a normal Optional, then use if let to ensure safe unwrapping and handle errors:
var placemark : CLPlacemark?
#IBAction func addPlace(sender: AnyObject) {
if let mark = placemark {
let locality = String(self.mark.locality)
let postalCode = String(self.mark.postalCode)
let administrativeArea = String(self.mark.administrativeArea)
let country = String(self.mark.country)
print(locality + postalCode + administrativeArea + country)
} else {
print("placemark was nil")
}
}

I add the variables in class, and fix my IBAction
var locality: String = ""
var postalCode: String = ""
var administrativeArea: String = ""
var country: String = ""
#IBAction func addPlace(sender: AnyObject) {
let locality = self.locality
let postalCode = self.postalCode
let administrativeArea = self.administrativeArea
let country = self.country
print(locality + postalCode + administrativeArea + country)
}

Related

Cannot assign value of type 'MapViewController' to type 'UberController?'

So I am trying to create a clone of Uber using Firebase but I keep getting an error in my MapViewController this is my code:
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var callUberBtn: UIButton!
#IBOutlet weak var myMap: MKMapView!
private var locationManager = CLLocationManager();
private var userLocation: CLLocationCoordinate2D?;
private var driverLocation: CLLocationCoordinate2D?;
private var timer = Timer();
private var canCallUber = true;
private var riderCanceledRequest = false;
private var appStartedForTheFirstTime = true;
override func viewDidLoad() {
super.viewDidLoad()
initializeLocationManager();
UberHandler.Instance.observeMessagesForRider();
UberHandler.Instance.delegate = self; //Cannot assign value of type 'mapViewController' to type 'UberController?'
}
private func initializeLocationManager() {
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.requestWhenInUseAuthorization();
locationManager.startUpdatingLocation();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if we have the coordinates from the manager
if let location = locationManager.location?.coordinate {
userLocation = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
let region = MKCoordinateRegion(center: userLocation!, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01));
myMap.setRegion(region, animated: true);
myMap.removeAnnotations(myMap.annotations);
if driverLocation != nil {
if !canCallUber {
let driverAnnotation = MKPointAnnotation();
driverAnnotation.coordinate = driverLocation!;
driverAnnotation.title = "Driver Location";
myMap.addAnnotation(driverAnnotation);
}
}
let annotation = MKPointAnnotation();
annotation.coordinate = userLocation!;
annotation.title = "Drivers Location";
myMap.addAnnotation(annotation);
}
}
func updateRidersLocation() {
UberHandler.Instance.updateRiderLocation(lat: userLocation!.latitude, long: userLocation!.longitude);
}
func canCallUber(delegateCalled: Bool) {
if delegateCalled {
callUberBtn.setTitle("Cancel Uber", for: UIControlState.normal);
canCallUber = false;
} else {
callUberBtn.setTitle("Call Uber", for: UIControlState.normal);
canCallUber = true;
}
}
func driverAcceptedRequest(requestAccepted: Bool, driverName: String) {
if !riderCanceledRequest {
if requestAccepted {
alertTheUser(title: "Uber Accepted", message: "\(driverName) Accepted Your Uber Request")
} else {
UberHandler.Instance.cancelUber();
timer.invalidate();
alertTheUser(title: "Uber Canceled", message: "\(driverName) Canceled Uber Request")
}
}
riderCanceledRequest = false;
}
func updateDriversLocation(lat: Double, long: Double) {
driverLocation = CLLocationCoordinate2D(latitude: lat, longitude: long);
}
#IBAction func callUber(_ sender: Any) {
if userLocation != nil {
if canCallUber {
UberHandler.Instance.requestUber(latitude: Double(userLocation!.latitude), longitude: Double(userLocation!.longitude))
timer = Timer.scheduledTimer(timeInterval: TimeInterval(10), target: self, selector: #selector(MapViewController.updateRidersLocation), userInfo: nil, repeats: true);
} else {
riderCanceledRequest = true;
UberHandler.Instance.cancelUber();
timer.invalidate();
}
}
}
private func alertTheUser(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert);
let ok = UIAlertAction(title: "OK", style: .default, handler: nil);
alert.addAction(ok);
present(alert, animated: true, completion: nil);
}
}
UberController? was declared in another Swift file and this is the code for that:
import Foundation
import FirebaseDatabase
protocol UberController: class {
func canCallUber(delegateCalled: Bool);
func driverAcceptedRequest(requestAccepted: Bool, driverName: String);
func updateDriversLocation(lat: Double, long: Double);
}
class UberHandler {
private static let _instance = UberHandler();
weak var delegate: UberController?;
var rider = "";
var driver = "";
var rider_id = "";
static var Instance: UberHandler {
return _instance;
}
func observeMessagesForRider() {
// RIDER REQUESTED UBER
DBProvider.Instance.requestRef.observe(DataEventType.childAdded) { (snapshot: DataSnapshot) in
if let data = snapshot.value as? NSDictionary {
if let name = data[Constants.NAME] as? String {
if name == self.rider {
self.rider_id = snapshot.key;
self.delegate?.canCallUber(delegateCalled: true);
}
}
}
}
// RIDER CANCELED UBER
DBProvider.Instance.requestRef.observe(DataEventType.childRemoved) { (snapshot: DataSnapshot) in
if let data = snapshot.value as? NSDictionary {
if let name = data[Constants.NAME] as? String {
if name == self.rider {
self.delegate?.canCallUber(delegateCalled: false);
}
}
}
}
// DRIVER ACCEPTED UBER
DBProvider.Instance.requestAcceptedRef.observe(DataEventType.childAdded) { (snapshot: DataSnapshot) in
if let data = snapshot.value as? NSDictionary {
if let name = data[Constants.NAME] as? String {
if self.driver == "" {
self.driver = name;
self.delegate?.driverAcceptedRequest(requestAccepted: true, driverName: self.driver);
}
}
}
}
// DRIVER CANCELED UBER
DBProvider.Instance.requestAcceptedRef.observe(DataEventType.childRemoved) { (snapshot:DataSnapshot) in
if let data = snapshot.value as? NSDictionary {
if let name = data[Constants.NAME] as? String {
if name == self.driver {
self.driver = "";
self.delegate?.driverAcceptedRequest(requestAccepted: false, driverName: name);
}
}
}
}
// DRIVER UPDATING LOCATION
DBProvider.Instance.requestAcceptedRef.observe(DataEventType.childChanged) { (snapshot: DataSnapshot) in
if let data = snapshot.value as? NSDictionary {
if let name = data[Constants.NAME] as? String {
if name == self.driver {
if let lat = data[Constants.LATITUDE] as? Double {
if let long = data[Constants.LONGITUDE] as? Double {
self.delegate?.updateDriversLocation(lat: lat, long: long);
}
}
}
}
}
}
}
func requestUber(latitude: Double, longitude: Double) {
let data: Dictionary<String, Any> = [Constants.NAME: rider, Constants.LATITUDE: latitude, Constants.LONGITUDE: longitude];
DBProvider.Instance.requestRef.childByAutoId().setValue(data);
} // request uber
func cancelUber() {
DBProvider.Instance.requestRef.child(rider_id).removeValue();
}
func updateRiderLocation(lat: Double, long: Double) {
DBProvider.Instance.requestRef.child(rider_id).updateChildValues([Constants.LATITUDE: lat, Constants.LONGITUDE: long]);
}
}
My error is: Cannot assign value of type 'mapViewController' to type 'UberController?' in the mapViewController. I don't know what I'm doing wrong. Any ideas?
You have to declare your MapViewController to conform to UberController protocol, just like you declare conformance to MapViewDelegate and CLLocationManagerDelegate protocols.
Either:
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UberController {
...
}
Or, even better:
class MapViewController: UIViewController {
...
}
extension MapViewController: MKMapViewDelegate {
// MKMapViewDelegate methods here
}
extension MapViewController: CLLocationManagerDelegate {
// CLLocationManagerDelegate methods here
}
extension MapViewController: UberController {
// UberController methods here
}

Data not being passed through Segue

I have a slightly confusing issue, I am trying to send location data from a tableview through to a mapkit. My tableview loads the various bits of info to list the names of the location, when I click a cell it segues into a navigation view with the data, however it doesnt seem to be sending the data and errors with found nil issue. I did have this same tableview segueing to a different controller and it worked without issue.
This is my prepareForSegue on my main Viewcontroller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mapLocation" {
if let detailsVC = segue.destination as? mapLocation {
if let tr = sender as? newTracks {
detailsVC.track = tr
}
}
}
}
This is the mapkit view to segue to
import UIKit
import MapKit
import CoreLocation
class mapLocation: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var track: newTracks!
override func viewDidLoad() {
super.viewDidLoad()
let sourceloaction = CLLocationCoordinate2D(latitude: track.lat, longitude: track.lon)
let destinationLocation = CLLocationCoordinate2D(latitude: track.lat, longitude: track.lon)
let sourcePlacemark = MKPlacemark(coordinate: sourceloaction, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
let sourceAnnotation = MKPointAnnotation()
sourceAnnotation.title = track.name
if let location = sourcePlacemark.location {
sourceAnnotation.coordinate = location.coordinate
}
let destinationAnnotation = MKPointAnnotation()
destinationAnnotation.title = track.name
if let location = destinationPlacemark.location {
destinationAnnotation.coordinate = location.coordinate
}
self.mapView.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true)
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.mapView.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 4.0
return renderer
}
}
and this is my NewTracks struct where the data is loaded to/from:
import Foundation
import FirebaseDatabase
struct newTracks {
let name: String!
let trackId: Int!
let postcode: String!
let trackType: String!
let locID: Int!
let lat: Double!
let lon: Double!
let phoneNumber: String!
let email: String!
let rating: Double!
let numrating: Double!
let totalrating: Double!
let ref: FIRDatabaseReference?
init(name: String, trackId: Int, postcode: String, trackType: String, trackURL: String, locID: Int, lat: Double, lon: Double, phoneNumber: String, email: String, rating: Double, numrating: Double, totalrating: Double) {
self.name = name
self.trackId = trackId
self.ref = nil
self.postcode = postcode
self.trackType = trackType
self.locID = locID
self.lat = lat
self.lon = lon
self.phoneNumber = phoneNumber
self.email = email
self.rating = rating
self.numrating = numrating
self.totalrating = totalrating
}
init(snapshot: FIRDataSnapshot) {
let snapshotValue = snapshot.value as! [String: AnyObject]
name = snapshotValue["name"] as! String
trackId = snapshotValue["id"]as! Int
postcode = snapshotValue["postcode"]as! String
trackType = snapshotValue["type"]as! String
locID = snapshotValue["locID"]as! Int
lat = snapshotValue["lat"]as! Double
lon = snapshotValue["long"]as! Double
phoneNumber = snapshotValue["phone"]as! String
email = snapshotValue["email"]as! String
rating = snapshotValue["rating"]as! Double
ref = snapshot.ref
numrating = snapshotValue["numrating"] as! Double
totalrating = snapshotValue["totalrating"] as! Double
}
func toAnyObject() -> Any {
return [
"name": name,
"trackId": trackId,
"postcode": postcode,
"trackType": trackType,
"locID": locID,
"lat": lat,
"lon": lon,
"phoneNumber": phoneNumber,
"email": email,
"rating": rating,
"numrating": numrating,
"totalrating": totalrating
]
}
}
The error happens on the line in the mapLocation VC
let sourceloaction = CLLocationCoordinate2D(latitude: track.lat, longitude: track.lon)
the lat and lon values are not being passed for some reason.
On my previous VC that the segue worked fine with the data all I had to do was to add the
var track: newTracks
and this allowed the communication to happen back to where the data is coming from, but for some reason it doesn't appear to be working now. I know it will be something fundamentally simple I have missed.
This is my original segue code that worked to a normal View COntroller, not a UINavigationController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TrackDetailVC" {
if let detailsVC = segue.destination as? TrackDetailVC {
if let tr = sender as? newTracks {
detailsVC.track = tr
}
}
}
}
Yes, when your viewController is embedded in a UINavigationController you have to work through the parts of the chain until you get to the controller you want. Something like this should work:
override function prepare(for segue: UIStoryBoardSegue, sender: Any?) {
if segue.identifier == “TrackDetailVC” {
if let nav = segue.destinationVC as? UINavigationController {
if let detailsVC = nav.viewControllers[0] as? TrackDetailVC {
if let tr = sender as? newTracks {
detailsVC.track = tr
}
}
}
}
}
sender can't conform to type newTracks. Do some debugging with breakpoints and maybe see why this cast isn't working. Try making a variable called newTracks and explicityly giving it a type, and set it equal to what you want. Then make detailsVc.track = newTracks

Firebase - Save unique ID in a class variable and send its data to another controller - Swift

I created a unique ID with firebase using childByAutoID. I am able to print this ID from within the reference function used to create it, but when I assign the value to a class variable and print it in the console it comes out nil.
I am trying to send this data to another view controller using the override func prepareForSegue method. It works when the data is a string but it does not work when it is the variable holding the uniqueIDKey.
Here is the code:
class MainViewController: UIViewController, CLLocationManagerDelegate{
var ref: FIRDatabaseReference!
var refHandle: UInt!
let locationManager = CLLocationManager()
let regionRadius: CLLocationDistance = 1000
var currentLocation: CLLocation!
var location: CLLocationCoordinate2D!
var latitude: Double!
var longitude: Double!
let geoCoder = CLGeocoder()
var placemark: CLPlacemark?
let date = NSDate()
var currentOrderIDKey = ""
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var userEmailLabel: UILabel!
#IBOutlet weak var pickUpAddress: UITextField!
#IBOutlet weak var deliveryAddress: UITextField!
override func viewDidLoad() {
ref = FIRDatabase.database().reference()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
refHandle = ref.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]
print((dataDict))
})
let userID: String = FIRAuth.auth()!.currentUser!.uid
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let userEmail = snapshot.value!["email"] as! String
self.userEmailLabel.text = userEmail
})
super.viewDidLoad()
print("\(currentLocation)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("didFailWithError \(error)")
if error.code == CLError.LocationUnknown.rawValue {
return
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.last!
latitude = currentLocation.coordinate.latitude
longitude = currentLocation.coordinate.longitude
location = CLLocationCoordinate2DMake(latitude, longitude)
print("didUpdateLocations \(currentLocation.coordinate)")
if currentLocation.timestamp.timeIntervalSinceNow < -10 {
return
}
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location, regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: false)
}
#IBAction func requestPickUpButton(sender: AnyObject) {
ref = FIRDatabase.database().reference()
let userID: String = FIRAuth.auth()!.currentUser!.uid
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let orderRef = self.ref.child("users").child(userID).child("orders")
let origin = self.pickUpAddress.text!
let destination = self.deliveryAddress.text!
let orderID = orderRef.childByAutoId()
let formatter = NSDateFormatter();
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ";
let defaultTimeZoneStr = formatter.stringFromDate(self.date)
let order = ["date": defaultTimeZoneStr, "origin": origin, "destination": destination]
orderID.setValue(order)
self.currentOrderIDKey = orderID.key as String
print(self.currentOrderIDKey) ///This works!
self.performSegueWithIdentifier("ServiceConfirmation", sender: self)
self.locationManager.stopUpdatingLocation()
})
print(currentOrderIDKey) //this doesnt
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ServiceConfirmation" {
let destinationConroller = segue.destinationViewController as! UINavigationController
let targetController = destinationConroller.topViewController as! ServiceConfirmationViewController
targetController.currentOrderIDKey = "this works"
//this does not : targetController.currentOrderIDKey = currentOrderIDKey
}
}
#IBAction func signOutButton(sender: AnyObject) {
try! FIRAuth.auth()!.signOut()
if let storyboard = self.storyboard {
let viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.presentViewController(viewController, animated: false, completion: nil)
}
}
}
As a bonus question, I get this warning in the console every time I run the app:
<UILayoutContainerView: ...; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: ....>; layer = <CALayer: ....>>'s window is not equal to <UINavigationController: ....>'s view's window!
Thanks in advance!
This is another classic case of Asynchrounous calls, you are accessing the value of currentOrderIDKey in print(currentOrderIDKey) even before it has been assigned.
Your print(currentOrderIDKey) line gets called even before
self.currentOrderIDKey = orderID.key as String
print(self.currentOrderIDKey) ///This works!
gets called.
ref.child("users").child(userID).observeSingleEventOfType(.Value,.. sends a Asynchronous call to your backend which takes some time to retrieve the data, but even before you data could be retrieved your print(currentOrderIDKey) gets called resulting in null
Instead of segueing through performSegue.... use instantiation:-
let secondScene = self.navigationController?.storyboard?.instantiateViewControllerWithIdentifier("ServiceConfirmationViewControllerVC_ID") as! ServiceConfirmationViewController
secondScene.valueTranfered = self. currentOrderIDKey
self.navigationController?.pushViewController(secondScene, animated: true)
Where ServiceConfirmationViewControllerVC_ID is your secondScene storyBoardID
Might i suggest reading this:- Wikipedia : Asynchronous calls & https://stackoverflow.com/a/748189/6297658

Memory leak in Swift app due to ContiguousArrayStorage

I'm designing an app which has the a feature to let user add their own pin. Recently, I found my app has a memory leak with the title of "ContiguousArrayStorage". I debugged a little and figured out it was a problem with my for loop in my viewdidload, but I am not sure how to fix the leak.
Here is my code:
#IBOutlet weak var AppleMap: MKMapView!
#IBOutlet weak var LogoutOutlet: UIButton!
#IBOutlet weak var OutletforAddPokemon: UIButton!
#IBOutlet weak var FindLocationOutlet: UIButton!
#IBOutlet weak var FilterOutlet: UIButton!
let locationManager = CLLocationManager()
let calendar: NSCalendar! = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
var increment:Int = 0
var currentLoc:CLLocationCoordinate2D?
var centerMap = true
let StoringPin = FIRDatabase.database().reference().child("locations")
var Date = NSDate()
override func viewDidLoad() {
super.viewDidLoad()
AppleMap.delegate = self
StoringPin.observeEventType(.Value, withBlock: {
snapshot in
if snapshot.value is NSNull {
return
}
let val = snapshot.value as! [String : [String : AnyObject]]
for key in val.keys {
let latitudedata = val[key]!["latitude"] as! Double
let longitudedata = val[key]!["longitude"] as! Double
let namedata = val[key]!["name"] as! String
let Username = val[key]!["Username"]
as! String
let DATE = val[key]!["Date"]
as! String
let NumberOflikesforuser = val[key]!["Likes"] as! Int
let NumberOfDislikesforuser = val[key]!["Dislikes"] as! Int
let coord = CLLocationCoordinate2D(latitude: latitudedata, longitude: longitudedata)
let artwork = Capital(title: "\(namedata)", coordinate: coord, info: "HI", username: Username, NumofLikes: NumberOflikesforuser,NumofDisLikes: NumberOfDislikesforuser, UIDSTring: UID, date: DATE, color: MKPinAnnotationColor.Green)
artwork.subtitle = DATE
print("k")
let permastringforemail:String = Username
print(++self.increment)
print(UID)
print(permastringforemail)
stringforemail = permastringforemail
Arrayforpins.append(artwork)
self.AppleMap.addAnnotation(Arrayforpins[Arrayforpins.count - 1])
for Capital in Arrayforpins {
self.AppleMap.addAnnotation(Capital)
}
}
})
print(LogoutOutlet)
LogoutOutlet.layer.cornerRadius = 4
FindLocationOutlet.layer.cornerRadius = 4
OutletforAddPokemon.layer.cornerRadius = 4
//FilterOutlet.layer.cornerRadius = 4
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
let annotationView = MKAnnotationView()
let detailButton: UIButton = UIButton.init(type: .DetailDisclosure) as UIButton
annotationView.rightCalloutAccessoryView = detailButton
}
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
AppleMap.setRegion(coordinateRegion, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
AppleMap.showsUserLocation = true
} else {
locationManager.requestWhenInUseAuthorization()
}
print("update")
currentLoc = locValue
if centerMap {
centerMap = false
centerMapOnLocation(CLLocation(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude))
}
}
#IBAction func SendtoSelector(sender: AnyObject) {
self.performSegueWithIdentifier("SeguetoSelector", sender: self)
}
#IBAction func FilterFunc(sender: AnyObject) {
self.performSegueWithIdentifier("SeguetoFilter", sender: self)
}
#IBAction func FindLocation(sender: AnyObject) {
centerMapOnLocation(CLLocation(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude))
}
#IBAction func Logout(sender: AnyObject) {
LO = true
self.performSegueWithIdentifier("BacktoLoginScreen", sender: self)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
locationManager
if IndexBoolean == true{
//let annotationView = MKPinAnnotationView()
print("Hi")
let artwork = Capital(title: "\(pickerDataSource[chosenindex])", coordinate: CLLocationCoordinate2D(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude), info: "HEY", username: stringforemail, NumofLikes: NumberOfLikes, NumofDisLikes: NumberOfDislike, UIDSTring: UID, date: stringfordate2, color: MKPinAnnotationColor.Green)
//print(now)
print(chosenindex)
artwork.title = "\(pickerDataSource[chosenindex])"
//artwork.subtitle = stringfordate
AppleMap.addAnnotation(artwork)
//annotationView.pinColor = artwork.Green
Arrayforpins.append(artwork)
print(stringforemail)
AppleMap.addAnnotation(Arrayforpins[Arrayforpins.count - 1])
for Capital in Arrayforpins{
AppleMap.addAnnotation(Capital)
}
IndexBoolean = false
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "Central Time")
var utcTimeZoneSTR = formatter.stringFromDate(Date)
stringfordate = "\(utcTimeZoneSTR)"
let uid = NSUUID().UUIDString
UID = uid
StoringPin.child(uid).setValue(["name" : pickerDataSource[chosenindex],
"latitude" : currentLoc!.latitude,
"longitude" : currentLoc!.longitude,"Array Position" : chosenindex,"Username": stringforemail, "Likes": NumberOfLikes2, "Dislikes":
NumberOfDislike, "UID": UID, "Date": stringfordate])
if FilterBoolean == true {
print("b")
if FilterDataSource[Intforfilter] != stringforname {
print("k")
//self.AppleMap.viewForAnnotation(artwork)?.hidden = true
FilterBoolean == false
}
//else {
// print("m")
// self.AppleMap.removeAnnotation(artwork)
//
// }
FilterBoolean == false
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "Capital"
print(++increment)
if annotation.isKindOfClass(Capital.self) {
print("CAPITAL")
if let annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) {
annotationView.annotation = annotation
return annotationView
} else {
let annotationView = MKPinAnnotationView(annotation:annotation, reuseIdentifier:identifier)
annotationView.enabled = true
annotationView.canShowCallout = true
//annotationView.pinColor = MKPinAnnotationColor.Green
let btn = UIButton(type: .DetailDisclosure)
annotationView.rightCalloutAccessoryView = btn
return annotationView
}
}
return nil
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let capital = view.annotation as! Capital
let placeName = capital.title
let placeInfo = capital.info
let UserName = capital.username
stringforname = view.annotation!.title!!
coords = view.annotation!.coordinate
stringforemail = capital.username
stringfordate2 = capital.date
NumberOfLikes2 = capital.NumofLikes
UID = capital.UIDSTring
print(stringforname)
print(UID)
self.performSegueWithIdentifier("SegueToInfo", sender: self)
}
I note you're not taking care to specify how to capture self in your closure. When in a disposable object context (a UIViewController) and an async closure you always have to worry about this. You need to capture it as weak or unowned. Are you familiar with closure capture lists?
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID56
The pattern I use is to capture self weakly and then bail if self has become nil, e.g. the UIViewController has been dismissed and harvested. If it is still around, I temporarily capture it strongly with a local variable in a guard...let statement.
withBlock: { [weak self] (snapshot) in
guard let s = self else {
print("Callback called after view unloaded")
return
}
// now use 's' instead of 'self'
...
}
Can't say this is the only problem because we don't have the declarations for your helper classes. For example we don't know what mischief Capital is up to.
By the way, just be clear that your for loop does not run at viewDidLoad(). You're merely registering a callback that only reacts if and when the .Value event occurs on the StoringPin object.

iOS Simulator and reverseGeocodeLocation

Is it normal for the iOS simulator to not work every so often with regards to grabbing the location? My project for this app keeps crashing 50% of the time whenever I run the simulator...but I can't seem to pinpoint the problem within the code itself. If the problem is indeed in the code itself, can anyone please help me find the problem? The error is "fatal error: unexpectedly found nil while unwrapping an Optional value" and it says it occurs in my refreshPost function on the line
eventsPostedQuery.whereKey("CityName", equalTo: self.usersLocation)
I am using Parse as part of this app. Also, I have a viewDidAppear to refresh the "Posts", is this a correct way to go about this? Greatly appreciated!
import UIKit
import Parse
class HomeTableViewController: UITableViewController, CLLocationManagerDelegate {
#IBOutlet weak var navigationBar: UINavigationItem!
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBAction func cancelPost(segue: UIStoryboardSegue) {
}
var users = [String: String]()
var usernames = [String]()
var eventInfo = [String]()
var imageFiles = [PFFile]()
var usersLocation: String!
var locationManager: CLLocationManager!
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
CLGeocoder().reverseGeocodeLocation(userLocation) { (placemarks, error) -> Void in
if error != nil {
print(error)
} else {
let p = placemarks?.first // ".first" returns the first element in the collection, or nil if its empty
// this code above will equal the first element in the placemarks array
let city = p?.locality != nil ? p?.locality : ""
let state = p?.administrativeArea != nil ? p?.administrativeArea : ""
self.navigationBar.title = ("\(city!), \(state!)")
self.usersLocation = ("\(city!), \(state!)")
self.locationManager.stopUpdatingLocation()
print(self.usersLocation)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
menuButton.target = self.revealViewController()
menuButton.action = Selector("revealToggle:")
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 250.0
}
override func viewDidAppear(animated: Bool) {
refreshPosts()
}
func refreshPosts() {
let query = PFUser.query()
query?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let users = objects {
self.users.removeAll(keepCapacity: true)
self.usernames.removeAll(keepCapacity: true)
self.eventInfo.removeAll(keepCapacity: true)
self.imageFiles.removeAll(keepCapacity: true)
for object in users {
if let user = object as? PFUser {
self.users[user.objectId!] = user.username!
}
}
}
let eventsPostedQuery = PFQuery(className: "PostEvent")
eventsPostedQuery.whereKey("CityName", equalTo: self.usersLocation)
eventsPostedQuery.orderByDescending("createdAt")
eventsPostedQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let events = objects {
for event in events {
self.imageFiles.append(event["imageFile"] as! PFFile)
self.eventInfo.append(event["eventInfo"] as! String)
self.usernames.append(self.users[event["userId"] as! String]!)
self.tableView.reloadData()
}
}
})
})
}
You should call
refreshPosts()
from inside the else block of this completion block:
CLGeocoder().reverseGeocodeLocation(userLocation) { (placemarks, error) -> Void in
if error != nil {
print(error)
} else {
let p = placemarks?.first // ".first" returns the first element in the collection, or nil if its empty
// this code above will equal the first element in the placemarks array
let city = p?.locality != nil ? p?.locality : ""
let state = p?.administrativeArea != nil ? p?.administrativeArea : ""
self.navigationBar.title = ("\(city!), \(state!)")
self.usersLocation = ("\(city!), \(state!)")
self.locationManager.stopUpdatingLocation()
print(self.usersLocation)
}
}
As in only update the post if the reverse geocoder is finished and didn't return an error.

Resources