Showing posts with label crash. Show all posts
Showing posts with label crash. Show all posts

Tuesday, June 13, 2017

Objective-C/Swift 3 avoid crashes in iPad in an iPhone Application

Targeting iOS app only for iPhone devices and run it on iPad devices, App gets crashed for UIAlertController(actionSheet) and UIAlertController.

Ever faced?

Let’s see the reason for crash and the fix for it.

(I am going to use Swift 3.0 in this post)


UIAlertController(actionSheet)




        let optionsMenu = UIAlertController(title: nil, message: "", 
                                                                     preferredStyle: .actionSheet)

        let deleteAction = UIAlertAction(title: “Delete”), style: .destructive, 
                                                                                                  handler: {
            (alert: UIAlertAction!) -> Void in
            //Delete Action
        })

        let saveAction = UIAlertAction(title: “Edit”, style: .default, handler: {
            (alert: UIAlertAction!) -> Void in
             // Edit Action
        })

        let cancelAction = UIAlertAction(title: “Cancel”, style: .cancel, handler: {
            (alert: UIAlertAction!) -> Void in
             //Cancel Action
        })

        optionsMenu.addAction(saveAction)
        optionsMenu.addAction(deleteAction)
        optionsMenu.addAction(cancelAction)

       self.present(optionMenu, animated: true, completion: nil)



 The above code works fine on iPhone device, But crashes on iPad device.

 The reason is in iPad, The presentation is going to be like a popover. 

 So we need to give either sourceView or barButtonItem as a source to the 
 popover as shown below.




   if (UIDevice.current.userInterfaceIdiom == .pad) {

       if let presentation = optionMenu.popoverPresentationController {

          presentation.barButtonItem = ((self.navigationItem.rightBarButtonItems)!)[0]

       }

  }




UIActivityViewController


  let sharingItems = [“share me….”] as [Any]

  let activityViewController = UIActivityViewController(activityItems: sharingItems, 
                                                                                     applicationActivities: nil)

   activityViewController.completionWithItemsHandler = 
                                                               {activity, completed, items, error in    
        }             
  
   self.present(activityViewController, animated: true, completion: nil)

    Same thing happens with UIActivityViewController. It crashes in iPad.
    Here also we need to provide sourceView/barButtonItem for iPad.

    if let popOverVC = activityViewController.popoverPresentationController { 

         if let navItems = self.navigationItem.rightBarButtonItems {
                popOverVC.barButtonItem = navItems[0]
         }

         else {
             popOverVC.sourceView = self.view
        }


    }



Hope this post is useful. Feel free to comment incase of any queries.



Tuesday, April 4, 2017

Swift Don’t use forced unwrapping !

In this post, Let’s see what happens If we forced unwrap things and the crashes that it creates.

Let me take 3 simple classes.

  1. User
  2. UserAccount
  3. WishList
class User {
    var name: String?
    var account: UserAccount?
}

class WishList {
    var totalItems: Int = 0
}

class UserAccount {
    var status: String?
    var items: [WishList]?
}

Here I want to check the account of the user. Then only we need to check the wish list of the user and notify if items are available in the wish list.

I am creating user instance and passing it to a function which checks user’s account first and then wish list and then notifies the user on items availability.

let user: User = User()
user.name = "Karunakar Bandikatla"

checkForCartItemsAndNotifyItemsAvailability(user: user)

func checkForCartItemsAndNotifyItemsAvailability(user: User) {
        
}

First, I need to check user’s account optional value.
What If we forced unwrap and access user’s account object.

func checkForCartItemsAndNotifyItemsAvailability(user: User) {
    let account: UserAccount = user.account!
}

Application gets crashed with a fatal error.

fatal error: unexpectedly found nil while unwrapping an Optional value

This is because, we haven’t assigned UserAccount object for user.

I am assigning it now.

let userAccount: UserAccount = UserAccount()
user.account = userAccount

If we run the app, It will not get crashed. To avoid this crash, What we can do is, We can check with If let as shown below

func checkForCartItemsAndNotifyItemsAvailability(user: User) {

      if let account = user.account {

      }
}

Like this we are checking the optional user account object and proceeding only if the value is not nil.

For checking a single object is fine using If lets. What If we have chain of objects need to be checked like in our example.

  1. User’s account should be checked
  2. User’s wish list object should be checked 

In handling chain of optional objects, guard helps a lot in checking all the optional nil conditions in a single statement and returning the function execution If any of the optionals are nil.

Let me show you how.

func checkForCartItemsAndNotifyItemsAvailability(user: User) {

        guard let account = user.account,
                 let wishlist = account.items else{
                    print("something is missing!")
                return
        }

        if wishlist.count > 0 {
            print("notify user")
        }
}


Thus we are checking all the chain the optional objects in a single guard statement and If any of the condition doesn’t met returning in guard else block. That’s how guard avoids all the optional nil crashes.

Don’t go for Forced Unwrapping, It will crash the app one way or other in any unchecked scenario.

Go for IfLet for a single object check and go for guard for chain of objects check.


Hope this post is useful. Feel free to comment incase of any queries.