Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I catch "Index out of range" in Swift?

I would really like to use a more simple classic try catch block in my Swift code but I can't find anything that does it.

I just need:

try { // some code that causes a crash. } catch { // okay well that crashed, so lets ignore this block and move on. }   

Here's my dilema, when the TableView is reloaded with new data, some information is still siting in RAM which calls didEndDisplayingCell on a tableView with a freshly empty datasource to crash.

So I frequently thrown the exception Index out of range

I've tried this:

func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {      do {         let imageMessageBody = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody         let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell         cell.willEndDisplayingCell()     } catch {         print("Swift try catch is confusing...")     } } 

I've also tried this:

func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {     print(indexPath.section)     print(indexPath.row)      if msgSections.count != 0 {         if let msg = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody {             let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell             cell.willEndDisplayingCell()         }     } } 

This is a very low priority block of code, and I have wasted a lot of time with trial and error figuring out which error handler built into swift works for what seems to be extremely unique situations when I have tons of scenarios just like this one where the code can crash and it will not have any effect on the user experience.

In short, I don't need anything fancy but Swift seems to have extremely specific error handlers that differ based on whether I'm getting a value from a functions return value or getting a value from an array's index which may not exist.

Is there a simple try catch on Swift like every other popular programming language?

like image 662
Matt Andrzejczuk Avatar asked May 14 '16 05:05

Matt Andrzejczuk


People also ask

What is index out of range in Swift?

One common error every array user ever faced is “Fatal error: Index out of range”. this error was caused by accessing an index that array doesn't even have in the first place. This error is undetectable on build time and can only show up on runtime which will be fatal on production or on distributed code.

How do you get an index of an array in Swift?

To find the index of a specific element in an Array in Swift, call firstIndex() method and pass the specific element for of parameter. Array. firstIndex(of: Element) returns the index of the first match of specified element in the array. If specified element is not present in the array, then this method returns nil .

What is index out of range error?

You'll get the Indexerror: list index out of range error when you try and access an item using a value that is out of the index range of the list and does not exist. This is quite common when you try to access the last item of a list, or the first one if you're using negative indexing.

Do try catch Swift?

The try/catch syntax was added in Swift 2.0 to make exception handling clearer and safer. It's made up of three parts: do starts a block of code that might fail, catch is where execution gets transferred if any errors occur, and any function calls that might fail need to be called using try .


2 Answers

As suggested in comments and other answers it is better to avoid this kind of situations. However, in some cases you might want to check if an item exists in an array and if it does safely return it. For this you can use the below Array extension for safely returning an array item.

Swift 5

extension Collection where Indices.Iterator.Element == Index {     subscript (safe index: Index) -> Iterator.Element? {         return indices.contains(index) ? self[index] : nil     } } 

Swift 4

extension Collection where Indices.Iterator.Element == Index {     subscript (safe index: Index) -> Iterator.Element? {         return indices.contains(index) ? self[index] : nil     } } 

Swift 3

extension Collection where Indices.Iterator.Element == Index {     subscript (safe index: Index) -> Generator.Element? {         return indices.contains(index) ? self[index] : nil     } } 

Swift 2

extension Array {     subscript (safe index: Int) -> Element? {         return indices ~= index ? self[index] : nil     } } 
  • This way you'll never hit Index out of range
  • You'll have to check if the item is nil

refer this question for more


Trying the Swift3 code in a Playground in Xcode 8.3.2 still leads to a "crash" when I do let ar = [1,3,4], then let v = ar[5]. Why? – Thomas Tempelmann May 17 at 17:40

You have to use our customized subscript so instead of let v = ar[5], it wll be let v = ar[safe: 5].

Default getting value from array.

let boo = foo[index] 

Add use the customized subscript.

let boo = fee[safe: index]  // And we can warp the result using guard to keep the code going without throwing the exception. guard let boo = foo[safe: index] else {   return } 
like image 93
Penkey Suresh Avatar answered Sep 19 '22 11:09

Penkey Suresh


Swift's Error Handling (do/try/catch) is not the solution to runtime exceptions like "index out of range".

A runtime exception (you might also see these called trap, fatal error, assertion failure, etc.) is a sign of programmer error. Except in -Ounchecked builds, Swift usually guarantees that these will crash your program, rather than continuing to execute in a bad/undefined state. These sorts of crashes can arise from force-unwrapping with !, implicit unwrapping, misuse of unowned references, integer operations/conversions which overflow, fatalError()s and precondition()s and assert()s, etc. (And, unfortunately, Objective-C exceptions.)

The solution is to simply avoid these situations. In your case, check the bounds of the array:

if indexPath.section < msgSections.count && indexPath.row < msgSections[indexPath.section].msg.count {     let msg = msgSections[indexPath.section].msg[indexPath.row]     // ... } 

(Or, as rmaddy says in comments — investigate why this problem is occurring! It really shouldn't happen at all.)

like image 36
jtbandes Avatar answered Sep 18 '22 11:09

jtbandes