iOS Location updates stop after 15-30 minutes - swiftui

I've built an app in SwiftUI, and have followed all the instructions I can to register the CLocationManager, and allow it to background. However, after some time in the background, my app stops producing location updates. When I view the file containing the coordinates, I have data for 15-30 minutes, then nothing. Snippets of the location manager init:
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.activityType = .fitness
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.distanceFilter = 0;
locationManager.allowsBackgroundLocationUpdates = true
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = locations.last!
session.addPointToCurrentSegment(newLocation)
coreDataHelper.add(toCoreData: newWayPoint)
}
My app has full permissions granted to access location "Always allow"

You aren't handling the location update anywhere when location manager does update. It should update in the background if “Location updates” is set in capabilities.
class Location: NSObject, ObservableObject, CLLocationManagerDelegate {
#Published var location: CLLocation = CLLocation(latitude: 0, longitude: 0)
private var manager: CLLocationManager = CLLocationManager()
func requestAuthorization() {
manager.allowsBackgroundLocationUpdates = true
/*
request authorization here
*/
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
/*
handle authorization changes here
*/
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// get the most recent location
guard let location = locations.last else { return }
self.location = location
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
if let error = error as? CLError, error.code == .denied {
manager.stopUpdatingLocation()
return
}
}
}

I wrote your own code, but it worked for me. The only different things I did were:
locationManager.activityType = .other
Use all methods of the delegate
In one of the methods of the delegate (didPause) try to start again, setting the manager again (locationManager.pausesLocationUpdatesAutomatically = false and locationManager.activityType = .other)
4)start the notification of awakening for the change of position, again in the didPause method.
I did not use the locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation and locationManager.distanceFilter = 0
for point 4 I recommend this reading:
https://developer.apple.com/documentation/usernotifications/unlocationnotificationtrigger
I hope I was helpful

Related

SwiftUI iOS 15 LocationButton{...} make app crash when using Chinese Simplified Localization

I am using the iOS 15 new View(LocationButton {...}), and my app crashed immediately. After I tested for 2 days, reduce code one file by one file, I found the Language Localization make it crashed. Just create 1 project using Xcode 13.1, create a file(LocationView) as below code showed, create a file like InfoPlist or other property list file, and configure the project file for the Localization, add some language package like Spanish/Danish/Japanese/Chinese Traditional/Chinese Simplified, then make the InfoPlist file localized using Chinese simplified, the run app, crashed. But if using other language localized like Chinese Traditional/Japanese/Spanish, it is ok. When app crashed, it will show an alert (as below)
Thread 1: "Invalid parameter not satisfying: width && height".
2021-11-19 15:23:29.247424+0800 MyNewProject2[98414:3116225] *** Assertion failure in CGImageRef _Nonnull UISCreateImageFromDrawing(__strong id _Nonnull, CGFloat, UISDisplayRange)(), UISDrawing.m:19
2021-11-19 15:23:29.259872+0800 MyNewProject2[98414:3116225] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: width && height'
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: width && height'
terminating with uncaught exception of type NSException
CoreSimulator 776.4 - Device: iPhone 13 Pro Max (FC597160-C5F6-4268-91F5-F38AB31AED4F) - Runtime: iOS 15.0 (19A339) - DeviceType: iPhone 13 Pro Max
Would you please also have a try using below code to test it.
Important: device language should use Chinese Simplified to launch, then App InfoPlist.strings localized using Chinese Simplified.
import SwiftUI
import CoreLocationUI
import CoreLocation
import MapKit
struct LocationView: View {
#StateObject var locationWorker = LocationWorker()
var body: some View {
NavigationView {
LocationButton(.currentLocation) {
locationWorker.startUpdatingLocation()
}
.foregroundColor(Color.white)
.cornerRadius(27)
.frame(width: 210, height: 54)
.padding(.bottom, 30)
.navigationTitle("Search Location")
}
.navigationViewStyle(.stack)
}
}
class LocationWorker: NSObject, ObservableObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
#Published var locationStatus: CLAuthorizationStatus?
#Published var lastLocation: CLLocation?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func startUpdatingLocation() {
locationManager.startUpdatingLocation()
}
func stopUpdatingLocation() {
locationManager.stopUpdatingLocation()
}
var statusString: String {
guard let status = locationStatus else {
return "unknown"
}
switch status {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationStatus = status
print(#function, statusString)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
print(#function, location)
stopUpdatingLocation()
}
}

Updated Reverse Geolocation

There are a number of examples showing how to do reverse geolocation, but nothing recent on implementation in SwiftUI. My current code uses the iPhone GPS to generate coordinates that are used with maps to show the location. I would also like to display the street address since a map without text indicating the location isn't very helpful.
My Questions:
Do I have all the relevant code to implement reverse geolocation?
I have seen examples using storyboards and print statements to display the location, but how do I return the location to a Swiftui view with an #escaping closure?
import Foundation
import CoreLocation
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
#Published var currentAddress: String = ""
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.distanceFilter = 10 // distance before update (meters)
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.startUpdatingLocation()
}
func startLocationServices() {
if locationManager.authorizationStatus == .authorizedAlways || locationManager.authorizationStatus == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
}
func getLocationCoordinates() -> (Double, Double) {
let coordinate = self.locationManager.location != nil ? self.locationManager.location!.coordinate : CLLocationCoordinate2D()
print("location = \(coordinate.latitude), \(coordinate.longitude)")
return (Double(coordinate.latitude), Double(coordinate.longitude))
}
// Using closure
func getAddress(handler: #escaping (String) -> Void)
{
self.currentAddress = ""
let coordinate = self.locationManager.location != nil ? self.locationManager.location!.coordinate : CLLocationCoordinate2D()
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark?
placeMark = placemarks?[0]
guard let placemark = placemarks?.first else { return }
if let streetNumber = placemark.subThoroughfare,
let street = placemark.subThoroughfare,
let city = placemark.locality,
let state = placemark.administrativeArea {
DispatchQueue.main.async {
self.currentAddress = "\(streetNumber) \(street) \(city) \(state)"
}
} else if let city = placemark.locality, let state = placemark.administrativeArea {
DispatchQueue.main.async {
self.currentAddress = "\(city) \(state)"
}
} else {
DispatchQueue.main.async {
self.currentAddress = "Address Unknown"
}
}
}
)
print( self.currentAddress)
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if locationManager.authorizationStatus == .authorizedAlways || locationManager.authorizationStatus == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
}
}
// Get Placemark
func getPlace(for location: CLLocation,
completion: #escaping (CLPlacemark?) -> Void) {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
guard error == nil else {
print("*** Error in \(#function): \(error!.localizedDescription)")
completion(nil)
return
}
guard let placemark = placemarks?[0] else {
print("*** Error in \(#function): placemark is nil")
completion(nil)
return
}
completion(placemark)
}
}
}
If I add the follow code say in ContentView:
#State private var entryLat: Double = 0.0
#State private var entryLong: Double = 0.0
let result = lm.getLocationCoordinates()
entryLat = result.0
entryLong = result.1
How would I call getPlace?
To use the following code you need to setup the appropriate entitlements and authorizations.
Here is a working example of using geolocation in swiftui, from code I got from
a number of sources on the net years ago.
This should give you a base to do reverse geolocation in swiftui:
import Foundation
import CoreLocation
import SwiftUI
import Combine
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
let locationProvider = LocationProvider()
#State var currentAddress = ""
var body: some View {
Text(currentAddress)
.onAppear {
getAddress()
}
}
func getAddress() {
// for testing Tokyo
let location = CLLocation(latitude: 35.684602, longitude: 139.751992)
locationProvider.getPlace(for: location) { plsmark in
guard let placemark = plsmark else { return }
if let streetNumber = placemark.subThoroughfare,
let street = placemark.subThoroughfare,
let city = placemark.locality,
let state = placemark.administrativeArea {
self.currentAddress = "\(streetNumber) \(street) \(city) \(state)"
} else if let city = placemark.locality, let state = placemark.administrativeArea {
self.currentAddress = "\(city) \(state)"
} else {
self.currentAddress = "Address Unknown"
}
}
}
}
/**
A Combine-based CoreLocation provider.
On every update of the device location from a wrapped `CLLocationManager`,
it provides the latest location as a published `CLLocation` object and
via a `PassthroughSubject<CLLocation, Never>` called `locationWillChange`.
*/
public class LocationProvider: NSObject, ObservableObject {
private let lm = CLLocationManager()
/// Is emitted when the `location` property changes.
public let locationWillChange = PassthroughSubject<CLLocation, Never>()
/**
The latest location provided by the `CLLocationManager`.
Updates of its value trigger both the `objectWillChange` and the `locationWillChange` PassthroughSubjects.
*/
#Published public private(set) var location: CLLocation? {
willSet {
locationWillChange.send(newValue ?? CLLocation())
}
}
/// The authorization status for CoreLocation.
#Published public var authorizationStatus: CLAuthorizationStatus?
/// A function that is executed when the `CLAuthorizationStatus` changes to `Denied`.
public var onAuthorizationStatusDenied : ()->Void = {presentLocationSettingsAlert()}
/// The LocationProvider intializer.
///
/// Creates a CLLocationManager delegate and sets the CLLocationManager properties.
public override init() {
super.init()
self.lm.delegate = self
self.lm.desiredAccuracy = kCLLocationAccuracyBest
self.lm.activityType = .fitness
self.lm.distanceFilter = 10
self.lm.allowsBackgroundLocationUpdates = true
self.lm.pausesLocationUpdatesAutomatically = false
self.lm.showsBackgroundLocationIndicator = true
}
/**
Request location access from user.
In case, the access has already been denied, execute the `onAuthorizationDenied` closure.
The default behavior is to present an alert that suggests going to the settings page.
*/
public func requestAuthorization() -> Void {
if self.authorizationStatus == CLAuthorizationStatus.denied {
onAuthorizationStatusDenied()
}
else {
self.lm.requestWhenInUseAuthorization()
}
}
/// Start the Location Provider.
public func start() throws -> Void {
self.requestAuthorization()
if let status = self.authorizationStatus {
guard status == .authorizedWhenInUse || status == .authorizedAlways else {
throw LocationProviderError.noAuthorization
}
}
else {
/// no authorization set by delegate yet
#if DEBUG
print(#function, "No location authorization status set by delegate yet. Try to start updates anyhow.")
#endif
/// In principle, this should throw an error.
/// However, this would prevent start() from running directly after the LocationProvider is initialized.
/// This is because the delegate method `didChangeAuthorization`,
/// setting `authorizationStatus` runs only after a brief delay after initialization.
//throw LocationProviderError.noAuthorization
}
self.lm.startUpdatingLocation()
}
/// Stop the Location Provider.
public func stop() -> Void {
self.lm.stopUpdatingLocation()
}
// todo deal with errors
public func getPlace(for location: CLLocation, completion: #escaping (CLPlacemark?) -> Void) {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
guard error == nil else {
print("=====> Error \(error!.localizedDescription)")
completion(nil)
return
}
guard let placemark = placemarks?.first else {
print("=====> Error placemark is nil")
completion(nil)
return
}
completion(placemark)
}
}
}
/// Present an alert that suggests to go to the app settings screen.
public func presentLocationSettingsAlert(alertText : String? = nil) -> Void {
let alertController = UIAlertController (title: "Enable Location Access", message: alertText ?? "The location access for this app is set to 'never'. Enable location access in the application settings. Go to Settings now?", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string:UIApplication.openSettingsURLString) else {
return
}
UIApplication.shared.open(settingsUrl)
}
alertController.addAction(settingsAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
UIApplication.shared.windows[0].rootViewController?.present(alertController, animated: true, completion: nil)
}
/// Error which is thrown for lacking localization authorization.
public enum LocationProviderError: Error {
case noAuthorization
}
extension LocationProvider: CLLocationManagerDelegate {
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.authorizationStatus = status
#if DEBUG
print(#function, status.name)
#endif
//print()
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
self.location = location
}
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
if let clErr = error as? CLError {
switch clErr {
case CLError.denied : do {
print(#function, "Location access denied by user.")
self.stop()
self.requestAuthorization()
}
case CLError.locationUnknown : print(#function, "Location manager is unable to retrieve a location.")
default: print(#function, "Location manager failed with unknown CoreLocation error.")
}
}
else {
print(#function, "Location manager failed with unknown error", error.localizedDescription)
}
}
}
extension CLAuthorizationStatus {
/// String representation of the CLAuthorizationStatus
var name: String {
switch self {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
}

CurrentLocation is nil after the user accepts location prompt

I have an app that displays nearby performances of shows. A List on the View is populated via an API call when the app loads.
When I call my model to load my performances, I surround it with a location manager call like this:
LocationManager.sharedInstance.runLocationBlock {
var currentLocation: CLLocation!
if(CLLocationManager.authorizationStatus() == .authorizedWhenInUse){
currentLocation = locManager.location
//call the API
SessionManager.manager.request(url).responseJSON { (responseData) -> Void in
//load objects then call the callback which ultimately populates the View...
callback()
}}
}
}
The issue is that the app blows up when it hits currentLocation - saying it's null. It breaks even if I surround it with a CLLocationManager.authorizationStatus() == .authorizedWhenInUse if statement. Note the NEXT time I load the app everything works great and it knows the current location.
My thinking is that the authorization has been given, but the device hasn't had the time it needs to load the location(?)
Cruising around the internet, it says the safest place to stick a "callback" is in the location manager class' (_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) func.
I get that, but how do I make a call back to my API call in that func? It's in a different model class. Or is there an altogether better way to do this. I even tried adding a sleep function to give the device some time.
Ultimately, I want the app to:
Prompt the user for location permissions
Use those permissions to get a lat/lng
Make the async API call using the coordinates and get the performances
Return the performances to the user
...right now it dies when the app loads b/c the current location is nil.
Here's my location manager for reference...
import Foundation
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate {
static let sharedInstance = LocationManager()
private var locationManager = CLLocationManager()
private let operationQueue = OperationQueue()
override init(){
super.init()
operationQueue.isSuspended = true
locationManager.delegate = self
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if(status == .authorizedAlways || status == .authorizedWhenInUse){
self.operationQueue.isSuspended = false
}else if(status == .denied){
self.operationQueue.cancelAllOperations()
}
}
func runLocationBlock(callback: #escaping () -> ()){
let authState = CLLocationManager.authorizationStatus()
if(authState == .authorizedAlways || authState == .authorizedWhenInUse){
self.operationQueue.isSuspended = false
}else{
//Request permission
locationManager.requestWhenInUseAuthorization()
}
let block = { callback() }
self.operationQueue.addOperation(block)
}
}

How to use user location out of locationmanager function swift 3

i am kinda new to coding and have come up with this problem. I im making af trashcanfinder app based on MapKit and I would like to show the distance from the user location to my pins on the map. I have already made my function to convert a CLLocationCoordinate2D to a CLLocation, and have a function to show distance from one point to another, but I can't seem to use the user location out of the locationmanager function.
This is my code:
#IBOutlet weak var mapView: MKMapView!
let manager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
}
func distancePointToPoint(location1: CLLocationCoordinate2D, location2: CLLocationCoordinate2D) -> String {
let cllocation1 = converter(location: location1)
let cllocation2 = converter(location: location2)
let distance = cllocation1.distance(from: cllocation2)
let shortDistance = distance - distance.truncatingRemainder(dividingBy: 0.1) //makes sure there is only one number behind comma
if shortDistance < 0 {
return "The distance is \(shortDistance) meters"
}
else {
return "The distance is \(shortDistance - (shortDistance / 1000).truncatingRemainder(dividingBy: 0.1)) kilometers"
}
}
So I need to use the user location as location1 in the distancePointToPoint function.
I know that with any other function I could just return the value, but I don't have any values to give the locationmanager function as it will get it from the device itself.
I already have done research and the only other way I have found is to put myLocation variable outside of the function and only change the value inside the function, but the xcode starts to complain that the class doesn't have an initializer and I don't know what to do.
Sorry if I made any stupid mistakes as I have just started to learn code.
Any help is appreciated!
Here is the LocationManager didUpdate Methods :
//MARK: LocationManager Delegates Methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//Get Current Location
let location = locations.last! as CLLocation
let userLocation:CLLocation = locations[0] as CLLocation
//Save current lat long
UserDefaults.standard.set(userLocation.coordinate.latitude, forKey: "LAT")
UserDefaults.standard.set(userLocation.coordinate.longitude, forKey: "LON")
UserDefaults().synchronize()
}
And then create a function and which gets called from viewWillAppear Method :
func createPin(){
//Access user Location LAT & LON from User Defaults
let coordinate = CLLocationCoordinate2D(latitude: UserDefaults.standard.value(forKey: "LAT") as! CLLocationDegrees, longitude: UserDefaults.standard.value(forKey: "LON") as! CLLocationDegrees)
var region = MKCoordinateRegion(center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
region.center = coordinate
self.map.setRegion(region, animated: true)
//Also now you have coordintes know for USER Location you can add annotation too
//Rest do your stuff
}
Feel free to comment if any further issue. Thanks

Swift 3 and NSURLSession issue

Thanks to Apple my iOS 9 Project 'Swift 2.3' is completely unusable with iOS 10's 'Swift 3'...
I fixed almost everything except that I am having issue with using NSURLSession, Xcode is telling me that it has been renamed to URLSession, if I rename it Xcode will tell me:
use of undeclared type URLSession
Foundation is imported.
What is the issue?!
For example I am using it this way...
lazy var defaultSession: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "reCoded.BGDownload")
configuration.sessionSendsLaunchEvents = true
configuration.isDiscretionary = true
let session = URLSession(configuration: configuration, delegate: self, delegateQueue, queue: nil)
return session
}()
and even with the delegate methods the same issue.
Try using Foundation.URLSession where ever you use URLSession.
/Got it to work/ In some cases try to copy your code somewhere else then remove everything in your class that uses URLSession then type the session methods again and put back your copied code you should be fine.
Update your URLSessin functions with;
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.data.append(data as Data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
I can explain how but by playing around with the code I got this to work in SWIFT 3 after two days of frustration. I guess SWIFT 3 removed a lot of unnecessary words.
let task = Foundation.URLSession.shared.dataTask(with: <#T##URL#>, completionHandler: <#T##(Data?, URLResponse?, Error?) -> Void#>)
Here's where I am right now. It's not perfect but works maybe half of the time.
First, in the class where my URLsession is defined:
import Foundation
class Central: NSObject, URLSessionDataDelegate, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDownloadDelegate {
I don't think all of that is necessary, but there it is. Then here is the function that is called by my background fetch:
func getWebData() {
var defaults: UserDefaults = UserDefaults.standard
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myBGconfig")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
urlString = "https://www.powersmartpricing.org/psp/servlet?type=dayslider"
if let url = URL(string: urlString) {
let rateTask = backgroundSession.downloadTask(with: URL(string: urlString)!)
rateTask.taskDescription = "rate"
rateTask.resume()
}
When the task comes back:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL ) {
if downloadTask.taskDescription == "rate" { // I run 2 web tasks during the session
if let data = NSData(contentsOf: location) {
var return1 = String(data: data as! Data, encoding: String.Encoding.utf8)!
DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 0.2){
var defaults: UserDefaults = UserDefaults.standard
defaults.set(myNumber, forKey: "electricRate") // myNumber is an extract of the text in returned web data
defaults.set(Date(), forKey: "rateUpdate")
defaults.synchronize()
self.calcSetting() //Calls another function defined in the same class. That function sends the user a notification.
let notificationName = Notification.Name("GotWebData")
NotificationCenter.default.post(name: notificationName, object: nil)
} // Closes the Dispatch
}
if session.configuration.identifier == "myBGconfig" {
print("about to invalidate the session")
session.invalidateAndCancel()
}
}
I haven't figured out yet how to kill the session when BOTH tasks have completed, so right now I kill it when either one is complete, with invalidateAndCancel as above.
And finally, to catch errors:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didCompleteWithError: Error?) {
if downloadTask.taskDescription == "rate" {
print("rate download failed with error \(didCompleteWithError)")
}
if downloadTask.taskDescription == "other" {
print("other download failed with error \(didCompleteWithError)")
}
downloadTask.resume() // I'm hoping this retries if a task fails?
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
if let error = error as? NSError {
print("invalidate, error %# / %d", error.domain, error.code)
} else {
print("invalidate, no error")
}
}