Exception Handling in Swift [duplicate] - 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?

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
}

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.)

Swift 4:
extension Collection where Indices.Iterator.Element == Index {
subscript (exist index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Usage:
var index :Int = 6 // or whatever number you need
if let _ = myArray[exist: index] {
// do stuff
}
or
var index :Int = 6 // or whatever number you need
guard let _ = myArray[exist: index] else { return }

Don't it's not considered a unusual state error, its a state of its completely screwed up, do something like
guard array.indicies.contains(index) else { return }

Swift 5
This answer in Swift 5 become:
extension Collection where Indices.Iterator.Element == Index {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Otherwise from Swift 4 there's the ability to have clauses on associated types and it can become:
extension Collection {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}

You can try a different approach to this. Will surely work!
if msgSections != nil {
for msg in msgSections[indexPath.section] {
if msgSections[indexPath.section].index(of: msg) == indexPath.row {
(Code)
}
}

Related

Can you both find a first instance of a type in a collection, returning that instance as that concrete type?

This code bothers me. Below, I'm trying to find the first instance of a specific type of ViewController in a NavigationController's stack. Simple. But when I've found it, I have to then cast it to the type I just looked for, which seems redundant to me.
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
guard let foundViewController = viewControllers.first(where: { $0 is T }) as? T else {
return nil
}
self.popToViewController(foundViewController, animated:animated)
return foundViewController
}
Only thing I can think of is this...
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
guard let foundViewController = viewControllers.flatMap({ $0 as? T }).first() else {
return nil
}
self.popToViewController(foundViewController, animated:animated)
return foundViewController
}
...but I've repeatedly found using flatMap like this tends to confuse people reading the code, and, as correctly pointed out in the comments below, iterates over the entire collection whereas first doesn't do that.
So is there another way to solve this issue?
You can use case patterns to select the viewControllers of the type you are interested in and pop and return the first one you find:
extension UINavigationController {
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
for case let vc as T in viewControllers {
self.popToViewController(vc, animated: animated)
return vc
}
return nil
}
}
Example:
Use a button in OrangeViewController to return to GreenViewController earlier in the stack:
#IBAction func popToGreen(_ sender: UIButton) {
let greenVC = self.navigationController?.popToFirstViewController(
ofType: GreenViewController.self,
animated: true
)
// Modify a property in GreenViewController that
// will be moved into a label in viewWillAppear
greenVC?.labelText = "Returned here from Orange"
}
popToLastViewController(ofType:animated:)
You might also want a function to pop to the most recent viewController of a type. That is easily achieved with a simple modification (adding .reversed()):
func popToLastViewController<T:UIViewController>(ofType type:T.Type, animated: Bool) -> T? {
for case let vc as T in viewControllers.reversed() {
self.popToViewController(vc, animated: animated)
return vc
}
return nil
}
I'm in favor of combining flatMap and lazy to get the behavior of conditionally casting to T, stripping out mismatches, and not enumerating the whole array:
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
guard let foundViewController = viewControllers.lazy.flatMap({ $0 as? T }).first {
return nil
}
self.popToViewController(foundViewController, animated:animated)
return foundViewController
}
As for "confusing people that read the code:" flatMap is fairly idiomatic Swift, and will be less ambiguous with the upcoming rename to compactMap. If readers in your environment really have trouble, you could always write a small helper (generic or not) that performs the same work under a clearer name.

Invalid update: invalid number of items in section 0.

Recently I got the following error:
Fatal Exception: NSInternalInconsistencyException
Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (13) must
be equal to the number of items contained in that section before the
update (12), plus or minus the number of items inserted or deleted
from that section (0 inserted, 0 deleted) and plus or minus the number
of items moved into or out of that section (0 moved in, 0 moved out).
The error occurs in the following code in my tvOS client:
let removedIndexPaths = removedIndexes.map({ IndexPath(row: $0, section: 0) })
let addedIndexPaths = addedIndexes.map({ IndexPath(row: $0, section: 0) })
let updatedIndexPaths = updatedIndexes.map({ IndexPath(row: $0, section: 0) })
self.collectionView?.performBatchUpdates({
self.collectionView?.deleteItems(at: removedIndexPaths)
self.collectionView?.insertItems(at: addedIndexPaths)
}, completion: { _ in
guard let collectionView = self.collectionView else {
return
}
for indexPath in updatedIndexPaths {
if let myCell = collectionView.cellForItem(at: indexPath) as? MyCollectionViewCell {
let item = self.dataManager.items[indexPath.row]
myCell.updateUI(item)
}
}
let collectionViewLayout = self.collectionViewLayoutForNumberOfItems(self.dataManager.items.count)
if collectionViewLayout.itemSize != self.collectionFlowLayout.itemSize {
collectionView.setCollectionViewLayout(collectionViewLayout, animated: false)
}
})
I am only using one section in my collection view:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
I have checked out couple of posts on the same topic, but they have not solved my problem, my guess is that the problem is in the following two lines, but I am not sure:
self.collectionView?.deleteItems(at: removedIndexPaths)
self.collectionView?.insertItems(at: addedIndexPaths)
Please help.
The call to insertItems(at:) and deleteItems(at:) must be accompanied with change in the datasource as well.
So, before calling these APIs, you would want to change your datasource, i.e. add objects into it before calling insertItems and remove objects from it before calling deleteItems
Found a very nice article about UICollectionView invalid number of items crash problem - https://fangpenlin.com/posts/2016/04/29/uicollectionview-invalid-number-of-items-crash-issue/
The item count returned by collectionView(_:numberOfItemsInSection:) should be sync with the updates made inside the closure. With this idea in mind, it’s easy to solve, just add a property as the item count and update it inside performBatchUpdates closure
func updateItems(updates: [ItemUpdate]) {
collectionView.performBatchUpdates({
for update in updates {
switch update {
case .Add(let index):
collectionView.insertItemsAtIndexPaths([NSIndexPath(forItem: index, inSection: 0)])
itemCount += 1
case .Delete(let index):
collectionView.deleteItemsAtIndexPaths([NSIndexPath(forItem: index, inSection: 0)])
itemCount -= 1
}
}
}, completion: nil)
}
and for the collectionView(_:numberOfItemsInSection:), instead of returning items.count, we return the property which is manually maintained by performBatchUpdates closure.
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemCount
}

When to use Never [duplicate]

I read the code block enclosed in curly braces after the keyword else in the context of a guard-else flow, must call a function marked with the noreturn attribute or transfer control using return, break, continue or throw.
The last part is quite clear, while I don't understand well the first.
First of all, any function returns something (an empty tuple at least) even if you don't declare any return type. Secondly, when can we use a noreturn function? Are the docs suggesting some core, built-in methods are marked with noreturn?
The else clause of a guard statement is required, and must either call
a function marked with the noreturn attribute or transfer program
control outside the guard statement’s enclosing scope using one of the
following statements:
return
break
continue
throw
Here is the source.
First of all, any function returns something (an empty tuple at least) even if you don't declare any return type.
(#noreturn is obsolete; see Swift 3 Update below.)
No, there are functions which terminate the process immediately
and do not return to the caller. These are marked in Swift
with #noreturn, such as
#noreturn public func fatalError(#autoclosure message: () -> String = default, file: StaticString = #file, line: UInt = #line)
#noreturn public func preconditionFailure(#autoclosure message: () -> String = default, file: StaticString = #file, line: UInt = #line)
#noreturn public func abort()
#noreturn public func exit(_: Int32)
and there may be more.
(Remark: Similar annotations exist in other programming languages
or compilers, such as [[noreturn]] in C++11, __attribute__((noreturn)) as a GCC extension, or _Noreturn for the
Clang compiler.)
You can mark your own function with #noreturn if it also terminates
the process unconditionally, e.g. by calling one of the built-in functions, such as
#noreturn func myFatalError() {
// Do something else and then ...
fatalError("Something went wrong!")
}
Now you can use your function in the else clause of a guard statement:
guard let n = Int("1234") else { myFatalError() }
#noreturn functions can also be used to mark cases that "should not
occur" and indicate a programming error. A simple example
(an extract from Missing return UITableViewCell):
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: MyTableViewCell
switch (indexPath.row) {
case 0:
cell = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! MyTableViewCell
cell.backgroundColor = UIColor.greenColor()
case 1:
cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! MyTableViewCell
cell.backgroundColor = UIColor.redColor()
default:
myFatalError()
}
// Setup other cell properties ...
return cell
}
Without myFatalError() marked as #noreturn, the compiler would
complain about a missing return in the default case.
Update: In Swift 3 (Xcode 8 beta 6) the #noreturn attribute
has been replaced by a Never return type, so the above example
would now be written as
func myFatalError() -> Never {
// Do something else and then ...
fatalError("Something went wrong!")
}
simple playground to see how it works ...
//: Playground - noun: a place where people can play
import Foundation
#noreturn func foo() {
print("foo")
exit(1)
}
var i: Int?
guard let i = i else {
foo()
}
print("after foo") // this line will never executed
//prints foo and finish
For instance, consider an assync operation that return either a value or error in result type. We usually write this as follows.
enum Result<Value, Error> {
case success(Value)
case failure(Error)
}
func fetch(_ request: URLRequest,
completion: (Result<(URLResponse, Data), Error>) -> Void) {
// ...
}
Now, if you know a function that always return value, never an error, then we could write :
func alwaysSucceeds(_ completion: (Result<String, Never>) -> Void) {
completion(.success("yes!"))
}
When compiler see Never, it won't force you to write all the switch cases to be exhaustive, so you can skip .failure as follows.
alwaysSucceeds { (result) in
switch result {
case .success(let string):
print(string)
}
}
Ref : https://nshipster.com/never/

Swift Error handling - Unforseen Errors [duplicate]

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?
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
}
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.)
Swift 4:
extension Collection where Indices.Iterator.Element == Index {
subscript (exist index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Usage:
var index :Int = 6 // or whatever number you need
if let _ = myArray[exist: index] {
// do stuff
}
or
var index :Int = 6 // or whatever number you need
guard let _ = myArray[exist: index] else { return }
Don't it's not considered a unusual state error, its a state of its completely screwed up, do something like
guard array.indicies.contains(index) else { return }
Swift 5
This answer in Swift 5 become:
extension Collection where Indices.Iterator.Element == Index {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Otherwise from Swift 4 there's the ability to have clauses on associated types and it can become:
extension Collection {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
You can try a different approach to this. Will surely work!
if msgSections != nil {
for msg in msgSections[indexPath.section] {
if msgSections[indexPath.section].index(of: msg) == indexPath.row {
(Code)
}
}

iOS swift tableview cell for parse query data

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
var query = PFQuery(className:"category")
let object = objects[indexPath.row] as String
query.whereKey("type", equalTo:"DRUM")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
NSLog("%#", object.objectId)
let abc = object["link"]
println("the web is \(abc)")
cell.textLabel!.text = "\(abc)"
}
} else {
NSLog("Error: %# %#", error, error.userInfo!)
}
}
return cell
}
after add the let object = objects[indexPath.row] as String can't load the view, delete the line show only one row successfully.
First I advise you to get your cell data outside cellForRowAtIndexPath. This function is not a good place to receive data from parse. Make another function and create a class variable and put handle getting data from there.
let object = objects[indexPath.row] as String
for object in objects
Try not to use same variable names for different stuff, as they will confuse you.
This line is not contributing to anything at the moment it seems. Try deleting it:
let object = objects[indexPath.row] as String
First lets have principles in mind. Don't ever update UI from a separate thread, its behavior is unexpected or undefined. It works or works weird.
Second, the problem you have is the when the VC gets loaded the tableView's datasource is called there and then on the main thread. Now you tried to add something on the cell by doing a Async call in separate thread which will take time and main thread is not waiting when the call to parse is being done. If you have difficulty in Async please take a look at the documentation its really important to get a good grasp of the few terms and the principles.
The thing is your main thread runs top to bottom without waiting each call to server thats async in the cell generation. So the result of that call will post later on and you are not posting on main thread too.
Moreover, i would suggest you don't do this approach for big projects or manageable code base. I generally do is:
when the view loads call the Parse with the needed information
Wait for that on a computed variable which i will observe to reload table views once I'm conformed i have the data.
Initially table view will have 0 rows and thats fine. Ill make a spinner dance during that time.
I hope i made some issues clear. Hope it helps you. Cheers!
//a computed var that is initialized to empty array of string or anything you like
//we are observing the value of datas. Observer Pattern.
var datas = [String](){
didSet{
dispatch_async(dispatch_get_main_queue(), {
//we might be called from the parse block which executes in seperate thread
tableView.reloadData()
})
}
}
func viewDidLoad(){
super.viewDidLoad()
//call the parse to fetch the data and store in the above variable
//when this succeeds then the table will be reloaded automatically
getDataFromParse()
}
//get the data: make it specific to your needs
func getDataFromParse(){
var query = PFQuery(className:"category")
//let object = objects[indexPath.row] as String //where do you use this in this block
var tempHolder = [String]()
query.whereKey("type", equalTo:"DRUM")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil && objects != nil {
for object in objects!{
//dont forget to cast it to PFObject
let abc = (object as! PFObject).objectForKey("link") as? String ?? "" //or as! String
println("the web is \(abc)")
tempHolder.append(abc)
}
} else {
print("error") //do some checks here
}
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = datas[indexPath.row]
return cell
}