This is my code
import UIKit
import GooglePlaces
import GoogleMaps
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
GMSPlacesClient.provideAPIKey("************************")
GMSServices.provideAPIKey("************************")
return true
}
but I'm still getting the exception
Terminating app due to uncaught exception 'GMSServicesException',
reason: 'Google Maps SDK for iOS must be initialized via [GMSServices
provideAPIKey:...] prior to use'
Is there any other cause, help me to fix it.
ViewController class is calling before appDelegate. So, the APIKey was not initiated. After finding this I initiate the viewController in appDelegate.
You must add API Key in App delegate and implement GoogleMap delegate in ViewController
import UIKit
import GoogleMaps
let googleApiKey = "<YOUR API Key HERE>"
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GMSServices.provideAPIKey(googleApiKey)
return true
}
//other methods
}
and your ViewController should be like
import UIKit
import GoogleMaps
class ViewController: UIViewController, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status == .authorizedWhenInUse else {
return
}
locationManager.startUpdatingLocation()
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
return
}
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
}
}
PS: Make sure you don't forget these important steps.
Go to Google Developers Console and create app and API Key
Enable APIs for Google Maps SDK for iOS
Add privacy permissions in .Plist file
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Application requires user’s location information while the app is running in the foreground.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Will you allow this app to always know your location?</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Do you allow this app to know your current location?</string>
Related
I tried get the coordinates when user launched app.
I setted up the locationManager code in a independent file:
UserLocation.swift:
import Foundation
import CoreLocation
class UserLocation: NSObject, CLLocationManagerDelegate {
var userCurrentLocation: CLLocationCoordinate2D?
let locationManager = CLLocationManager()
func locationSetup() {
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedWhenInUse {
print("authorization error")
return
}
locationManager.distanceFilter = 300
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.startUpdatingLocation()
print("startUpdatingLocation")
if CLLocationManager.locationServicesEnabled() {
print("locationServicesEnabled")
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager getting start")
if let location = locations.last {
self.userCurrentLocation = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
print("print location in locationManager: \(String(describing: userCurrentLocation?.latitude))")
locationManager.stopUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didFinishDeferredUpdatesWithError error: Error?) {
print(error?.localizedDescription as Any)
return
}
}
then, I call the func in the AppDelegate.swift's application(_::didFinishLaunchingWithOptions:) like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let getUserLocation = UserLocation()
getUserLocation.locationSetup()
}
but , the only calls locationSetup() function successfully , never called the related function locationManager(_::didUpdateLocations:). the print("locationManager getting start") I putted the first line in the locationManager(_::didUpdateLocations:), never printed out
BTW, in the info.plist , I already set Privacy - Location When In Use Usage Description .
can anyone help me out ?
Your getUserLocation is a local variable. It goes out of existence 1 millisecond after you create it. So it never has time to do anything (e.g. location updating). Promote it to an instance variable so that it lives longer:
var getUserLocation : UserLocation?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.getUserLocation = UserLocation()
self.getUserLocation?.locationSetup()
return true
}
(Also please use better variable names. An object reference should not begin with a verb.)
Please cross-check simulator location: Simulator -> Debug -> Locations that shouldn't be None in your case...
Hope this will help you... Thanks :)
I want my iOS app to update it's location when closed and make post requests but location manager didUpdateLocations doesn't get called from the background despite it working fine in normal app use. Below is my SendLocation class. Calling determineCurrentLocation() get it going.
My problem is that if I call determineCurrentLocation() from another class while the app is running - it works perfectly. But when I call it from the App Delegate didUpdateLocations never gets called.
class SendLocation: NSObject, CLLocationManagerDelegate{
var locationManager:CLLocationManager = CLLocationManager()
struct LocationStruct{
var latitude:CLLocationDegrees?, longitude:CLLocationDegrees?
}
var locationStruct = LocationStruct()
var userLocation: CLLocation? = nil
func sendLocationPost(){
// This function makes the POST
}
func determineCurrentLocation(){
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates=true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled(){
DispatchQueue.main.async {
print("test1") // This prints fine from background
self.locationManager.startUpdatingLocation()
}
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
print("test2") // This never gets printed from backgrounf
locationManager.stopUpdatingLocation()
userLocation = locations[0] as CLLocation
locationStruct.latitude=userLocation?.coordinate.latitude
locationStruct.longitude=userLocation?.coordinate.longitude
sendLocationPost()
}
Below are the two functions I edited in my App Delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
let sendLocation = SendLocation()
sendLocation.determineCurrentLocation()
}
My Info.plist has been changed to be correct, as far as I know.
Here is a picture of my Background Modes
And my privacy settings
Just in case I'm missing something there.
If anyone has any ideas what I've done wrong then I'd be very appreciative of some help.
I have an app that uses the user's location throughout its various view conrollers, and I've learned that in order to get that location you need to create a CLLocationManger instance and conform to the CLLocationManagerDelegate protocol.
Is it possible to make one CLLocationManager instance and use it's properties like coordinates in my different view controllers or should I make a CLLocationManager in each view controller class?
A quick and somewhat robust hack could be:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.startUpdatingLocation()
return true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
lastLocation = locations.last!
}
In this same source file, at global scope, add:
private var lastLocation: CLLocation? {
didSet {
locationCallback?(lastLocation!)
locationCallback = nil
}
}
private var locationCallback: ((CLLocation) -> Void)?
func getLastLocation(callback: (CLLocationManager) -> Void) {
guard let location = lastLocation else {
locationCallback = callback
return
}
locationCallback(location)
}
Finally, elsewhere in your app, you can get your last known location using just:
getLastLocation { location in
print(location)
}
I have enabled location services under capabilities for background mode, everything works fine if I click the home button, I get the updates. I even get updates when using safari until I go on facebook or something, it just stops. The app hasn't crashed or anything and It doesn't consume much memory nor does it drain alot of power. My app has an NSTimer which only requires the location after every 30 seconds.
App Delegate:
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var manager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
manager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
manager.delegate = self
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
return true
}
Main View Controller:
import UIKit
import CoreLocation
class MainViewController: UIViewController, SettingsViewDelegate {
#IBOutlet weak var pageMenuView: UIView!
var audioPlayer = AVAudioPlayer()
var lastTimestamp: NSDate!
var timer : NSTimer?
lazy var locationManager: CLLocationManager = {
var _locationManager = CLLocationManager()
_locationManager.delegate = self
_locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
// Movement threshold for new events
return _locationManager
}()
override func viewDidLoad() {
super.viewDidLoad()
self.timer = NSTimer.scheduledTimerWithTimeInterval(30, target: self, selector: #selector(MainViewController.startLocationUpdates), userInfo: nil, repeats: true)
}
func startLocationUpdates() {
// Here, the location manager will be lazily instantiated
locationManager.startUpdatingLocation()
}
}
// MARK: - CLLocationManagerDelegate
extension MainViewController: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("location \(reminderTime)")
self.locationManager.stopUpdatingLocation()
}
}
Update:
This issue happens on the physical device, iphone 5c
I just followed http://www.raywenderlich.com/92428/background-modes-ios-swift-tutorial location update part.
But manager doesn't print location info in background mode.
Then manager print logs to Xcode's console when app enter foreground.
Is this code right?
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var manager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
return true
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
if UIApplication.sharedApplication().applicationState != .Active {
NSLog("App is backgrounded. New location is %#", newLocation)
}
}
.....
}
I got answer for myself.
If your app running on iOS 9.0 or above and want to run your location service app on background,
you have to allowsBackgroundLocationUpdates variable set to true.
so here is my code.
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var manager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
if #available(iOS 9.0, *){
manager.allowsBackgroundLocationUpdates = true
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
if UIApplication.sharedApplication().applicationState != .Active {
NSLog("App is backgrounded. New location is %#", newLocation)
}
}
.....
}