I've recently began trying to put more error handling in to my code so I thought I'd post a use-case to check I'm on the right path. I feel like this is going to make my functions much larger and more indented/nested than before. Is this the right way to go about things or should I be utilising try?
instead? Where do people generally put these Error
enum
definitions?
fileprivate enum LocationError: Error {
case noAuthorization
case noBeaconSupport
case rangingUnavailable
}
class ViewController: UIViewController, CLLocationManagerDelegate {
@IBOutlet weak var distanceReading: UILabel!
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
view.backgroundColor = UIColor.gray
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
do {
guard status == .authorizedWhenInUse else {
throw LocationError.noAuthorization
}
guard CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) else {
throw LocationError.noBeaconSupport
}
guard CLLocationManager.isRangingAvailable() else {
throw LocationError.rangingUnavailable
}
startScanning()
}
catch LocationError.noAuthorization {
print("User has not authorized us to use location")
}
catch LocationError.noBeaconSupport {
print("User's device does not support Beacons")
}
catch LocationError.rangingUnavailable {
print("User's device does not support ranging")
}
catch {
fatalError()
}
}
1 Answer 1
Throwing Swift errors is a mechanism how a function/method can report a failure to its caller. Your code throws and catches the error within the same method, and I can see no advantage of using try/catch in that situation. Your code is equivalent to
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status == .authorizedWhenInUse else {
print("User has not authorized us to use location")
return
}
guard CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) else {
print("User has not authorized us to use location")
return
}
guard CLLocationManager.isRangingAvailable() else {
print("User's device does not support ranging")
return
}
startScanning()
}
which is
- shorter and easy to understand,
- makes the
enum LocationError
obsolete (which isfileprivate
, and therefore apparently not used anywhere else), - makes the catch-all with
fatalError()
obsolete.
I would even go a step further: guard
is useful in connection with
optional binding (to avoid the "optional binding pyramid of doom").
In your case, the same can be achieved with a simple if/else if/.../else
statement:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status != .authorizedWhenInUse {
print("User has not authorized us to use location")
} else if !CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {
print("User has not authorized us to use location")
} else if !CLLocationManager.isRangingAvailable() {
print("User's device does not support ranging")
} else {
startScanning()
}
}
-
\$\begingroup\$ Thanks. In what case would I want to use
Error
andtry catch
? When I do more than just print? \$\endgroup\$Declan McKenna– Declan McKenna2017年12月08日 14:15:43 +00:00Commented Dec 8, 2017 at 14:15 -
\$\begingroup\$ @Deco: A function throws an Error to report a failure to its caller. You use try/catch when calling functions that throw an Error. \$\endgroup\$Martin R– Martin R2017年12月08日 14:22:51 +00:00Commented Dec 8, 2017 at 14:22