Getting initializer error on ViewController - ios

I get the error: "Class 'ViewController' has no initializer". I have narrowed down the problem to this function that is being called. Does anyone see the problem?
let locationManager1: CLLocationManager // your location manager
func addBoundry(loc: CLLocation)
{
if let loc: CLLocation! = locationManager1.location {
let center: CLLocationCoordinate2D = loc!.coordinate
let lat: CLLocationDegrees = center.latitude
let long: CLLocationDegrees = center.longitude
var points = [CLLocationCoordinate2DMake(lat,long),CLLocationCoordinate2DMake(lat,long),CLLocationCoordinate2DMake(lat,long),CLLocationCoordinate2DMake(lat,long)]
let polygon = MKPolygon(coordinates: &points, count: points.count)
mapView.addOverlay(polygon)
} else {
print("no location...")
}
}

when assign property use optional.
let locationManager1: CLLocationManager?

When you say let locationManager1: CLLocationManager, you're declaring a property that must not be nil, but not actually assigning anything to it.
So, you either need to declare an init that ensures locationManager1 is assigned exactly one value (it's a let constant, not a var, after all!) or initialize the property inline like:
let locationManager1 = CLLocationManager()

Related

Mapbox Navigation in iOS with in my mapView controller

I want to integrate Mapbox navigation in iOS, I can easily get the direction/route between two coordinate also to get the navigation path from mapbox we can use below code
let options = NavigationOptions(styles: nil)
let viewController = NavigationViewController(for: self.directionsRoute!)
viewController.delegate=self
self.present(viewController, animated: true, completion: nil)
But the problem is I want to display the navigation in my mapview which is a part of another view controller, I can do that by getting a direction/route and instruction but I can't find any method which will be called every second so that I can update route instruction, as well as route, in case of user change the path.
Let me know if I am missing anything or any changes needed.
-Thanks in advance
here is my approach:
first i did get only directions instructions from the MapBox api taking advantage of it's free API calls quota and draw the instructions on GMSMapView or MapKit taking advantage of their good performance and memory management.
podfile
pod 'MapboxDirections.swift'
import MapboxDirections
this is done through the below code
have the property for MapBox directions
#IBOutlet weak var googleMapView: GMSMapView!
let locationManager = CLLocationManager()
let mapBoxirections = Directions(accessToken: osmToken)
var path: GMSMutablePath?
then do the actual api call
private func drawRouteBetween(source: StopModel, destination: StopModel) {
guard let name = source.name, let lat = source.latitude, let lng = source.longitude else { return }
guard let nameDest = destination.name, let latDest = destination.latitude, let lngDest = destination.longitude else { return }
let waypoints = [
Waypoint(coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lng), name: name),
Waypoint(coordinate: CLLocationCoordinate2D(latitude: latDest, longitude: lngDest), name: nameDest),
]
let options = RouteOptions(waypoints: waypoints, profileIdentifier: .automobile)
options.includesSteps = true
options.distanceMeasurementSystem = .metric
mapBoxirections.calculate(options) { (waypoints, routes, error) in
guard error == nil else {
print("Error calculating directions: \(error!)")
return
}
if let route = routes?.first, let leg = route.legs.first {
for step in leg.steps {
if let coordinates = step.coordinates {
for (index, point) in coordinates.enumerated() {
let source = point
if index <= coordinates.count - 2 {
let destination = coordinates[index + 1]
self.drawPolyLine(source: source, destination: destination)
}
}
}
}
}
}
}
note that StopModel is my custom made CLLocation so feel free to replace it with your own as long it has the latitude and longitude
create the method that draws Polyline on your CLLocationManagerDelegate as below
private func drawPolyLine(source: CLLocationCoordinate2D, destination: CLLocationCoordinate2D){
path?.add(source)
path?.add(destination)
let polyLine = GMSPolyline(path: path)
polyLine.strokeWidth = 4 // width of your choice
polyLine.strokeColor = .red // color of your choice
polyLine.map = googleMapView
}
then take a look at the MapBoxDirections.Route model and explore it's properties you will find very useful info inside it
and then take advantage of the callback function from the GMS Delegate that notifies you with the location update instead having a timer and calling it every second this is more efficient way
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
/* do your business here */
}
do not forget to have the delegate of the location manager to self or the class of your choice
Maybe this helps a bit: you can easily add observer for route progress changes:
NotificationCenter.default.addObserver(self,
selector: #selector(progressDidChange(notification:)),
name: .routeControllerProgressDidChange,
object: navigationService.router)
You need a navigation service with your route by creating it like
let navigationService = MapboxNavigationService(route: route)
The function progressDidChange can do something like:
#objc func progressDidChange(notification: NSNotification) {
guard let routeProgress = notification.userInfo?[RouteControllerNotificationUserInfoKey.routeProgressKey] as? RouteProgress,
let location = notification.userInfo?[RouteControllerNotificationUserInfoKey.locationKey] as? CLLocation else {
return
}
// you have all information you probably need in routeProgress, f.E.
let secondsRemaining = routeProgress.currentLegProgress.currentStepProgress.durationRemaining
...
}

Cannot convert value of type 'String?' to expected argument type 'CLLocationDegrees' (aka 'Double')

I try to display the distance between a post creator and the current user within a cell.
My error now is
Cannot convert value of type 'String?' to expected argument type 'CLLocationDegrees' (aka 'Double')
in the following code:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geoCoder = CLGeocoder()
let myLocation = CLLocation(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
/*Here the error*/ let myBuddysLocation = CLLocation(latitude: job!.distance, longitude: job!.distance)
let distance = myLocation.distance(from: myBuddysLocation) / 1000
print(String(format: "The distance to my buddy is %.01fkm", distance))
}
}
This is my JobClass:
class Job {
var distance: String?
let ref: DatabaseReference!
init(distance: String? = nil) {
self.distance = distance
ref = Database.database().reference().child("jobs").childByAutoId()
}
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
distance = value["distance"] as? String
}
}
func save() {
let newPostKey = ref.key
let postDictionary = [
"distance" : self.distance!
] as [String : Any]
self.ref.setValue(postDictionary)
}
}
I hope someone knows how to solve it, if you wan't I can add more code if it helps // I'm new to coding
The error is self-explanatory: You are trying to set a String? instead of a CLLocationDegrees, but you can even use a Double type.
There are several ways to fix this:
1) You can change your class definition as it follows:
class Job {
// Change distance to Double and set a default value instead of optional
var distance: Double = 0.0
let ref: DatabaseReference!
// Change the init
init(distance: Double = 0.0) {
self.distance = distance
ref = Database.database().reference().child("jobs").childByAutoId()
}
//You have to change all your distance to Double
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
// So change this cast
distance = value["distance"] as Double
}
}
}
2) You can try a cast from String? to Double, but I don't recommend this.
Anyway, here it is how you can do it:
if let dist = Double(job!.distance){
let myBuddysLocation = CLLocation(latitude: dist, longitude: dist) -> Error Appears in this Line
let distance = myLocation.distance(from: myBuddysLocation) / 1000
print(String(format: "The distance to my buddy is %.01fkm", distance))
}
Actually in your function the only problem are there you passing the string value in place of double you need to change like this.
let myBuddysLocation = CLLocation(latitude: Double.init(job!.distance) ?? 0.0, longitude: Double.init(job!.distance) ?? 0.0)
You need to change your function like this
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geoCoder = CLGeocoder()
let myLocation = CLLocation(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
let myBuddysLocation = CLLocation(latitude: Double.init(job!.distance) ?? 0.0, longitude: Double.init(job!.distance) ?? 0.0)
let distance = myLocation.distance(from: myBuddysLocation) / 1000
print(String(format: "The distance to my buddy is %.01fkm", distance))
}
}
It's normal because distance should be Double value. Changing it to: var distance: Double? should correct it.
Or parse the value to Double let myBuddysLocation = CLLocation(latitude: Double(distance)!, longitude: Double(distance)!)
I recommend you to avoid using ! because it could generate a fatal error if the string isn't a number, you can guard the value with guard or if let like this example:
if let latitude = Double(distance) {
let myBuddysLocation = CLLocation(latitude: latitude, longitude: latitude)
}

How to get single dataBase reference from Firebase

I'm sharing and retrieving coordinates with Firebase, but when I print them in my console..I get same coordinates 3-4 time.
Which creates an odd effect on my custom marker image file.
How can I get the coordinates from Firebase only once?
Here is my code:
var posts=[postStruct]()
var mapView : GMSMapView? = nil
var friendLocator : [Locator] = [Locator]()
struct Locator {
let name: String
let long: CLLocationDegrees
let lat: CLLocationDegrees
}
var latPass: Double!
var longPass: Double!
var fetchLat: Double!
var fetchLong: Double!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
var location=locations[0]
let span:MKCoordinateSpan=MKCoordinateSpanMake(0.01, 0.01)
var myLocation:CLLocationCoordinate2D=CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion=MKCoordinateRegionMake(myLocation, span)
latPass=28.3217378
longPass=75.6895935
post()
self.configureMapView()
let dataBaseRef=FIRDatabase.database().reference()
dataBaseRef.child("Raunak Trikha").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {(snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
var fetchLat = postDict["lat"] as! Double
var fetchLong = postDict["long"] as! Double
let locator = Locator(name: "Raunak Trikha", long: fetchLong, lat: fetchLat)
self.friendLocator.append(locator)
self.locateFriend()
print(fetchLat)
print(fetchLong)
})
manager.stopUpdatingLocation()
self.view = mapView
}
func locateFriend() {
for friend in friendLocator{
let friendMarker = GMSMarker()
friendMarker.position=CLLocationCoordinate2D(latitude: friend.lat, longitude: friend.long)
friendMarker.title=friend.name
friendMarker.map=mapView
mapView?.selectedMarker=friendMarker
if friend.name=="Virat Singh"{
friendMarker.icon=UIImage(named: "ViratPin.png")
}
else if friend.name=="Raunak Trikha"{
friendMarker.icon=UIImage(named: "currentLocation.png")
}
}
do {
mapView?.mapStyle = try GMSMapStyle(jsonString: kMapStyle)
} catch {
NSLog("One or more of the map styles failed to load. \(error)")
}
}
func configureMapView(){
let camera = GMSCameraPosition.camera(withLatitude: latPass, longitude: longPass, zoom: 10)
self.mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
view = mapView
mapView?.settings.scrollGestures = true
mapView?.settings.zoomGestures = true
mapView?.settings.myLocationButton = true
//mapView?.addSubview(searchBar)
//mapView?.addSubview(searchSupporter)
//mapView?.bringSubview(toFront: searchBar)
for gesture in (mapView?.gestureRecognizers!)! {
mapView?.removeGestureRecognizer(gesture)
}
}
when I print fetchLat & fetchLong I get the same coordinates 4 time, which overlaps my custom marker image that creates the weird effect.
Since your code that adds a particular Locator struct is called multiple times, check your array to make sure it doesn't already contain the exact same struct before adding it to the array locally.
This will evaluate your array of structs and determine if there is no value for it. But it also assumes that name property of the struct is a unique identifier for each struct, which may not be your case. You can alternatively compare any value within the filter closure that you want to make sure isn't duplictated, i. e. lat and long.
let locator = Locator(name: "Raunak Trikha", long: fetchLong, lat: fetchLat)
if self.friendLocator.filter({ $0.name == locator.name }).count == 0 {
self.friendLocator.append(locator)
}
self.locateFriend()
This function func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) will get called whenever your location changes/updates or until the GPS settles (Warms up) on your location.
I notice you are using the firebase single event obeserver function for database updates using .observeSingleEvent() which is correct however since you have defined the call in the above didUpdateLocations function it will be called multiple times.
Either move the call to Firebase out of the function or supply some conditional to call firebase only once. I.e only update if the location has changed more than X range/distance etc.

Why is my MKCoordinateRegion nil?

So I've got the following function to set my map region for my app:
func mapRegion() -> MKCoordinateRegion {
databaseHandle = databaseRef.child("RunList").child(runName).observe(.value, with: { (snapshot) in
let runData = snapshot.value as? [String: AnyObject]
self.minLat = runData?["startLat"] as? Double
self.minLng = runData?["startLong"] as? Double
self.maxLat = runData?["endLat"] as? Double
self.maxLng = runData?["endLong"] as? Double
print("testing")
print(self.minLat!)
print(self.maxLng!)
self.coordinate = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: (self.minLat! + self.maxLat!)/2,
longitude: (self.minLng! + self.maxLng!)/2),
span: MKCoordinateSpan(latitudeDelta: (self.maxLat! - self.minLat!)*1.1,
longitudeDelta: (self.maxLng! - self.minLng!)*1.1))
})
return coordinate
}
I have initialised the following variables at the top of my class:
var minLat: Double!
var minLng: Double!
var maxLat: Double!
var maxLng: Double!
var coordinate: MKCoordinateRegion!
And am trying to set the map region as follows:
override func viewDidLoad() {
super.viewDidLoad()
configureView()
mapView.region = mapRegion()
}
The error I am getting is in the lines where I am setting my coordinate region under the block of code that contains my databaseHandle in mapRegion(). When the code is running, I get the following error under the line 'return coordinate':
fatal error: unexpectedly found nil while unwrapping an Optional value
To my understanding, this is occurring because 'coordinate' has no values and is an Optional. Why is it that coordinate has no values even though I am setting it up using a global variable? I feel like I am missing something so simple!
Thanks
Your code does not work because observe works asynchronously and it is impossible to return a value from a method containing a asynchronous task.
You need a completion handler
func mapRegion(completion: (MKCoordinateRegion)->()) {
databaseHandle = databaseRef.child("RunList").child(runName).observe(.value, with: { (snapshot) in
let runData = snapshot.value as? [String: AnyObject]
self.minLat = runData?["startLat"] as? Double
self.minLng = runData?["startLong"] as? Double
self.maxLat = runData?["endLat"] as? Double
self.maxLng = runData?["endLong"] as? Double
print("testing")
print(self.minLat!)
print(self.maxLng!)
let region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: (self.minLat! + self.maxLat!)/2,
longitude: (self.minLng! + self.maxLng!)/2),
span: MKCoordinateSpan(latitudeDelta: (self.maxLat! - self.minLat!)*1.1,
longitudeDelta: (self.maxLng! - self.minLng!)*1.1))
completion(region)
})
}
And use it
mapRegion() { region in
mapView.region = region
// do other things with the region
}
Side note:
There are too many question marks in the code. Use optional bindings to unwrap optionals safely.

Getting coordinates of user's location (OneShotLocationManager)

I am currently trying to make a weather app. It is using the http://developer.forecast.io API, which accepts longitude and latitude coordinates to find the weather. I have OneShotLocationManager implemented, but I can't quite get the coordinates from it then use them with the Forecast API.
Here's my code:
var manager: OneShotLocationManager?
let coordinate: (lat: Double, long: Double) = (0,0)
override func viewDidLoad() {
super.viewDidLoad()
manager = OneShotLocationManager()
manager!.fetchWithCompletion { location, error in
// fetch location or an error
if let loc = location {
print(location?.coordinate.latitude)
print(location?.coordinate.longitude)
let coordinate: (lat: Double, long: Double) = ((location?.coordinate.latitude)!,location!.coordinate.longitude)
} else if let err = error {
print(err.localizedDescription)
}
self.manager = nil
} }
I can get OneShotLocationManager to print the coordinates to the console, but I can't figure out how to actually use them with Forecast.io. Any help on this would be greatly appreciated.
You have no need to use location var anymore, for that reason you do the if let check.
Try this:
if let loc = location {
let coordinate = (lat: loc.coordinate.latitude, long: loc.coordinate.longitude)
// ...your web API request stuff using coordinate....
}

Resources