42 Replies
      Latest reply: Aug 27, 2015 9:08 AM by SevenTenEleven RSS
      john.estropia Level 1 Level 1 (10 points)

        I'm impressed by the structure imposed by the new do-catch construct.

        I also appreciate that the Swift team gave us try! to handle cases where we are sure that errors won't occur.

        Now one case that keeps bugging me when refactoring to Swift 2.0 code is I have to write something like this:

        let fileManager = NSFileManager.defaultManager()
        do {
           try fileManager.removeFileAtPath(cachePath1)
        } catch _ { }
        
        do {
           try fileManager.removeFileAtPath(cachePath2)
        } catch _ { }
        
        do {
           try fileManager.removeFileAtPath(cachePath3)
        } catch _ { }
        
        
        
        
        

         

        I just want to delete those paths if possible, but I don't care if they fail; they may have not existed in the first place and if they did, the system will clean the Caches sooner or later.

        If I write this as

        do {
           try fileManager.removeFileAtPath(cachePath1)
           try fileManager.removeFileAtPath(cachePath2)
           try fileManager.removeFileAtPath(cachePath3)
        } catch _ { }
        
        
        
        

        then if cachePath1 fails, 2 and 3 will never be executed. And while we don't care if the deletes fail, not trying at all just feels wrong. What if each method I call have a side effect that I need to trigger?

         

        On the other hand,

        try! fileManager.removeFileAtPath(cachePath1)
        try! fileManager.removeFileAtPath(cachePath2)
        try! fileManager.removeFileAtPath(cachePath3)
        
        
        
        

        will crash my app if any of the deletes fail. Definitely not what I want.

         

        Now I don't know if someone else had the same idea before, but what if we have a try? as well to counterpart the try!? We can write

        try? fileManager.removeFileAtPath(cachePath1)
        
        
        
        

        which ignores any thrown errors, and more importantly, does not trap on error.

         

        For methods that return an object, Swift 2.0 already bridges Objective-C methods of the pattern

        - (SomeObject *)doSomethingAndReturnError:(NSError **)error;
        
        
        
        

         

        to

        func doSomething() throws -> SomeObject
        
        
        
        

         

        such that doing

        let object: SomeObject = try! doSomething()
        
        
        
        

        returns a non-optional SomeObject instance.

         

        We can then make "try?" return an optional:

        let object = try? doSomething() // object will be bound to SomeObject?
        
        
        
        

         

        For void methods, returning a Void? is fine, or we can borrow the bridging rules from Objective-C and just return a Bool instead.

        if try? fileManager.removeFileAtPath(cachePath1) {
           println("file deleted!")
        }
        
        
        
        

         

        Is this idea too crazy or am I missing something that will break this kind of syntax?

         

         

        Update:

        I hope the discussion does not digress into a debate wether to handle file-system errors or not; I just used removeFileAtPath as ONE of the many use cases we encountered this problem. CharlesS pointed out another good use case: methods that were primarily used to check for a state, such as NSURL.checkResourceIsReachable()

        • Re: Can we get a "try?" as a more lenient "try!"?
          ahltorp Level 3 Level 3 (285 points)

          You can define your own control structure:

           

          func ignorethrow(@noescape block: () throws -> Void) {
              do {
                  try block()
              } catch {
              }
          }
          
          

           

          which you can then use like this:

           

          ignorethrow { try fileManager.removeFileAtPath(cachePath1) }
          ignorethrow { try fileManager.removeFileAtPath(cachePath2) }
          ignorethrow { try fileManager.removeFileAtPath(cachePath3) }
          
          

           

          Converting the error to an optional is probably not ideal, since the reason for throwing an error (or in the Objective-C case, returning an error) is to be more detailed in the error reporting.

           

          EDIT: added @noescape to make it more convenient if the code references self implictly.

            • Re: Can we get a "try?" as a more lenient "try!"?
              john.estropia Level 1 Level 1 (10 points)

              I understand that we can do workarounds for now, but I believe Swift will benefit having it built into the language.

               

              By the way, you can also extend and overload your ignorethrow function to revert the return types as bridged from Objective-C:

              func ignorethrow(@noescape block: () throws -> Void) -> Bool {
                  do {
                      try block()
                      return true
                  } catch {
                      return false
                  }
              }
              
              func ignorethrow(@noescape block: () throws -> T) -> T? {
                  do {
                      return try block()
                  } catch {
                      return nil
                  }
              }
              
            • Re: Can we get a "try?" as a more lenient "try!"?
              fluidsonic Level 1 Level 1 (10 points)

              We also have all these annoying catch {} everywhere when using NSFileManager.

               

              What about this solution?

              extension NSFileManager {
                   func tryRemoveItemAtPath(path: String) -> Bool {
                        do {
                             try removeItemAtPath(path)
                             return true
                        }
                        catch {
                             return false
                        }
                   }
              }
              

               

              And then:

              fileManager.tryRemoveItemAtPath(cachePath1)
              fileManager.tryRemoveItemAtPath(cachePath2)
              fileManager.tryRemoveItemAtPath(cachePath3)
              
              • Re: Can we get a "try?" as a more lenient "try!"?
                GSnyder Level 2 Level 2 (50 points)

                +1

                 

                The suggested work-arounds are fine, but this is such a common use case that it should be streamlined.

                • Re: Can we get a "try?" as a more lenient "try!"?
                  Wallacy Level 1 Level 1 (20 points)

                  Yes, I have also thought about the need for an operator "try?". Just did not comment

                   

                  It's not crazy to me, it is to even obvious: Not all errors need to be addressed in all contexts.

                   

                  And also if "try?" is be implemented, it would be possible to disable empty catch ("catch {}") for safety and readability.

                  • Re: Can we get a "try?" as a more lenient "try!"?
                    RLKingSoftware Level 3 Level 3 (475 points)

                    The point is, you SHOULD care. You should be striving to write error free code

                    not ignoring errors. Anything less is just sloppy at best, lazy at worse.

                      • Re: Can we get a "try?" as a more lenient "try!"?
                        Wallacy Level 1 Level 1 (20 points)

                        In theory: In reality not all errors are important.

                        And the "try?" syntax is better than a void catch {} like most people do.

                        Just look at some codes in my desk, using a simple search, i can see 80 error points, and 8 with catch {}. And why? Because an optional result in this point is everything i want in this particular code.

                         

                        For example: Some API may throw an error parsing a JSON object or just return a void result and no error be throw. The throw version is good right? But sometimes does matter. On Swift for example, Int(String) returns a optional Int, but if we want to know why the string parser fail? We cant! And most of the time, in this case, does matter!

                        You can't really force the dev make something on error, and the try? will only remove the inevitable catch {} in some cases.

                          • Re: Can we get a "try?" as a more lenient "try!"?
                            CharlesS Level 2 Level 2 (40 points)

                            In practice, not all errors are even errors.

                             

                            Consider this use case: You have a directory that may or may not exist, that you want to put some files in. If it doesn't exist, you want to create it first. So, you do this:

                             

                            NSURL *cacheDirURL = ...
                            
                            if (![cacheDirURL checkResourceIsReachableAndReturnError:NULL]) {
                              [[NSFileManager defaultManager] createDirectoryAtURL:cacheDirURL withBlah:blah:blah:];
                            }
                            
                            // now put some stuff in the directory
                            
                            
                            
                            

                             

                            If the directory doesn't exist, that's not an errorin this case. It's just informational. In Swift 2, you'll have to create a Boolean variable, set it to true in the do block, set it to false in the catch block, and then branch on it (you could, of course, just create the directory in the catch block, but that just feels wrong; creating the directory is *not* error-handling code).

                              • Re: Can we get a "try?" as a more lenient "try!"?
                                ahltorp Level 3 Level 3 (285 points)

                                So what you probably should do is to try and create the directory first, without even checking if it exists first. If it is important for some reason to check first, do that, but remember that the directory might appear between your call to check for existence and you actually trying to create it.

                                 

                                So what is my point? The point is that you have to do error checking on the creation part, but only selectively. One of the errors that directory creation might return (directory already exists) should be ignored in this case. However, any other error has to be handled, because the error might be that you have insufficient privileges, the disk might be full, the disk might have disappeared, the directory above might have disappeared/not exist in the first place, and so on.

                                 

                                Since I have a background in file systems, I'm particularly aware of programmers ignoring errors in that realm, especially the return value from closing a file.

                                  • Re: Can we get a "try?" as a more lenient "try!"?
                                    QuinceyMorris Level 5 Level 5 (1,920 points)

                                    1. I like that Swift makes it hard for you to ignore an error.

                                     

                                    2. I definitely agree that there needs to be a way to bridge from errors to optionals, in a way that doesn't require arbitratily pushing variables into higher scopes. Whether this is 'try?' or 'guard try' or 'try … else' or something else doesn't seem to matter too much, but there should be a direct way of doing it.

                                     

                                    I kinda like the idea of "try…else", where you can basically catch an error from a specific try without having to enclose it in a scope, and you can do recovery that knows where you are in that scope. That could be in addition to a "catch" block at the end, which does more general cleanup. It's the scoping that's the real problem with what we've got now.

                                     

                                    (But this is just vague hand-waving. I haven't tried to think it through to see if something like this is really sensible.)

                                     

                                    3. It could be argued that the API for removeFileAtPath is not well designed for Swift-style error handling, because it doesn't distinguish between the cases "deleted it" vs "didn't need to delete" vs "couldn't actually delete". Similarly, as you say, best practices say that you should try to create the directory and see what happens, since checking first for its existence leaves a race condition. So, that API really needs "created it" vs "didn't need to create it" vs "couldn't actually create it".

                                     

                                    In Obj-C, the NSError** parameter mechanism makes it easier to work around this lack, so I think my suggestion would be an extension to NSFileManager that provides Swift-error-handling-compatible API that distinguishes between the three cases.

                                    • Re: Can we get a "try?" as a more lenient "try!"?
                                      CharlesS Level 2 Level 2 (40 points)

                                      So what you probably should do is to try and create the directory first, without even checking if it exists first.

                                       

                                      Well, no. If the directory already exists, and I try to create it, I'm going to get an error which once again, is not an error. I just want the folder to be there, so if it already is, then great! Not a problem. Now, I could just ignore the error on folder creation, but then the problem is that in the case that the folder didn't already exist, now suddenly I do care about the error. I could do a checkURLIsReachable() after trying to create the folder, but then I'll get the wrong error — I don't want to know that the folder doesn't exist, I want to know why I couldn't create it.

                                       

                                      Anyway, either way this is going to require the ignoring of some error somewhere.

                                       

                                      If it is important for some reason to check first, do that, but remember that the directory might appear between your call to check for existence and you actually trying to create it.

                                       

                                      If we're talking about something in ~/Library/Caches/<my app bundle id>, that's pretty unlikely. Even more so if the app is sandboxed.

                                       

                                      So what is my point? The point is that you have to do error checking on the creation part, but only selectively. One of the errors that directory creation might return (directory already exists) should be ignored in this case. However, any other error has to be handled, because the error might be that you have insufficient privileges, the disk might be full, the disk might have disappeared, the directory above might have disappeared/not exist in the first place, and so on.

                                       

                                      Yes, of course, that's the other option, to try to create the directory, and then check the error object to see what the error was. So, just check for NSCocoaErrorDomain and NSFileWriteFileExistsError. Aaaaand also check for NSPOSIXErrorDomain and EEXIST. And also check for NSOSStatusErrorDomain and the five or six error codes in there that look like "already exists" errors. And also invent a time machine, travel to the future to see what new ErrorType Apple's going to eventually replace NSError with, and check for that too, because this isn't future-proof at all. Think I'm being paranoid about this? Well, if you look at the docs for NSFileWriteFileExistsError, you'll see that it was introduced in 10.7. So code that was written for Snow Leopard would have been checking against EEXIST, and it would have completely silently failed once Lion rolled around and started giving a new error that the developer couldn't possibly have known about. Actually, code for Snow Leopard wouldn't have been checking against EEXIST, because I just checked, and what that OS version actually did was to return NSFileWriteUnknownError, with a second error object containing EEXIST being put in its NSUnderlyingErrorKey. So there's another thing you have to check for. Uggggg

                                       

                                      Checking NSError codes is not reliable at all unless the API docs explicitly say what codes they will return, which not only isn't true for Apple's APIs, but they don't even stay consistent from one OS release to the next. This makes checking NSError codes tantamount to using private APIs. They're clearly for display only.

                                        • Re: Can we get a "try?" as a more lenient "try!"?
                                          ahltorp Level 3 Level 3 (285 points)

                                          So, if what you are saying about the APIs is correct (and I have no reason to believe otherwise), the discussion from a file management point of view (and maybe other points of view as well) is moot, since the APIs that actually return errors with NSError do so in a way that makes it very hard to handle them in a correct way. However, this kind of error handling (did something/didn't have to/error) has to be handled in some way. Ignoring specific errors is fine, but except for cases such as the original question (removing cache files that would be cleaned later anyway), ignoring all errors is normally a bad idea.

                                           

                                          I'm not going to comment on the specific file system practices, since the original poster doesn't want me to, and that would be a long thread in itself.

                                            • Re: Can we get a "try?" as a more lenient "try!"?
                                              CharlesS Level 2 Level 2 (40 points)

                                              Ignoring specific errors is fine, but except for cases such as the original question (removing cache files that would be cleaned later anyway), ignoring all errors is normally a bad idea.

                                               

                                              A syntax for ignoring specific errors is all that's being asked for.

                                                • Re: Can we get a "try?" as a more lenient "try!"?
                                                  ahltorp Level 3 Level 3 (285 points)

                                                  No, the "try?" suggestion is ignoring all errors for a call.

                                                    • Re: Can we get a "try?" as a more lenient "try!"?
                                                      CharlesS Level 2 Level 2 (40 points)

                                                      For a specific call. There are lots of specific cases where you don't need the error.

                                                        • Re: Can we get a "try?" as a more lenient "try!"?
                                                          ahltorp Level 3 Level 3 (285 points)

                                                          I'll say it once more: Ignoring specific errors is fine, but except for cases such as the original question (removing cache files that would be cleaned later anyway), ignoring all errors is normally a bad idea.

                                                           

                                                          There are very few cases where ignoring all types of errors for a call is a good idea. You can ignore errors that you know are not going to affect the behaviour of your program, but by definition unknown errors are not in that category. You might think you don't need the error, though.

                                                            • Re: Can we get a "try?" as a more lenient "try!"?
                                                              CharlesS Level 2 Level 2 (40 points)

                                                              Do you see the irony in the fact that even in your absolute statement, you acknowledge a case where it makes perfect sense to ignore the error?

                                                                • Re: Can we get a "try?" as a more lenient "try!"?
                                                                  ahltorp Level 3 Level 3 (285 points)

                                                                  I have not argued against ignoring errors completely in some cases, so I don't know where you get that from. I have argued that ignoring the type of error completely normally means that you assume that it is a certain type of error, and when the real error comes along, your code will behave incorrectly.

                                                                    • Re: Can we get a "try?" as a more lenient "try!"?
                                                                      CharlesS Level 2 Level 2 (40 points)

                                                                      There are a lot of cases where you don't carewhat the error is, and what's important is whether the file is reachable or whether you can perform some operation or not.

                                                                       

                                                                      Here's a couple more, in addition to the ones already mentioned:

                                                                       

                                                                      - Setting the color of a view object representing a file in the project, dependent on whether the file is reachable or not. For something like Xcode's left-hand sidebar, does it matter if the file is unreachable because it's missing, or because you don't have read permission to its parent folder? Not really, you just want to color it red to show that you can't open it.

                                                                       

                                                                      - Closing and/or releasing some kind of resource, whether it's a file descriptor, a network socket, or whatever. Like the temp files, it'll be closed when your app exits anyway, and its failure to close won't affect your processing of whatever data you just read from it without errors.

                                                                       

                                                                      - Implementing a delegate method, or overriding a method, or doing something with a method whose signature looks something like -isSomeCertainKindOfFileAtURL:, with no error parameter. If you can't open the file handle to the file, you're just returning that no, this is not the file you were looking for, as it's all that really matters (and, really, all you can do).

                                                                       

                                                                      - Compiling a list of files that may be in any of several standard directories, where not all of the directories are required to exist.

                                                                       

                                                                      There are cases where it's appropriate to ignore the error, and a try? operator would facilitate that.

                                                    • Re: Can we get a "try?" as a more lenient "try!"?
                                                      john.estropia Level 1 Level 1 (10 points)

                                                      NSURL.checkResourceIsReachable() is a VERY good use case for this, thanks. I should have used it in my sample code to prevent the discussion digressing into a file systems best practice debate..

                                                       

                                                      A lot of API in Foundation were designed to return Bool values and provide NSError as an OPTIONAL source for additional information. It just feels wrong that if I only need the Bool result as before I now also have to work around the do-catch syntax.

                                                  • Re: Can we get a "try?" as a more lenient "try!"?
                                                    john.estropia Level 1 Level 1 (10 points)

                                                    I do not condone ignoring errors, at worse we dump errors to our logs. But some errors are much expected that logging them are useless, such as in my example code where calling removeFileAtPath() on a non-existent file is guaranteed to fail but such failure is ZERO relevance to my app.

                                                     

                                                    Wether the language enforces error-handling or not, programmers who have a bad practice of ignoring important errors will find a way to do them (in Swiift's case, they'll write catch {}).

                                                     

                                                    The point is, which errors to handle is the programmer's decision and the language should be able to let us express either. They already gave us try! which lets us ignore errors, but to the extent that the whole application just gives up if an error does occur.

                                                      • Re: Can we get a "try?" as a more lenient "try!"?
                                                        ahltorp Level 3 Level 3 (285 points)

                                                        Yes, in your case, ignoring the error is correct, since you don't care if it succeds or not. However, regarding error handling with Bool, I pointed to a case(mkdir) where the error/no error return value seems to give you the information you need, but actually doesn't. This needs to be carefully designed so that we avoid these problems in our code.

                                                         

                                                        I don't want to make this about file systems, but it already was, and I'm not comfortable pointing out subtle error handling problems in, for example, UIKit, although I'm sure they exist.

                                                        • Re: Can we get a "try?" as a more lenient "try!"?
                                                          RLKingSoftware Level 3 Level 3 (475 points)

                                                          You do not know, at run time wether that file exists or not so, NO, it is NOT guaranteed to fail. If

                                                          the file exists, your call will remove it. If it does not, you know it does not unless, you ignore the

                                                          error. This is so horribly basic, I fail to understand how so many here don't see it.

                                                            • Re: Can we get a "try?" as a more lenient "try!"?
                                                              john.estropia Level 1 Level 1 (10 points)

                                                              I don't see the point in your argument because as I have mentioned in this particular case, neither result is relevant to my code. The discussion here isn't wether we should handle errors or not, its how to to write code elegantly when we are already decided we do not need to handle them.

                                                               

                                                              Please don't take this sentimentally. Just because we're requesting this try? feature doesn't mean we want to ignore all errors and use it everywhere; just in particular places where it doesn't make sense to do otherwise. As Wallacy has explained well below, Apple introduced the new do-catch syntax in the recent WWDC by first acknowledging different levels of error-handling. We just want to be able to demote certain errors as "Trivial errors".

                                                                • Re: Can we get a "try?" as a more lenient "try!"?
                                                                  CharlesS Level 2 Level 2 (40 points)

                                                                  Indeed. The other thing is that since it's now so much less painful to report errors than it used to be, it's much more tempting to use throws in just about any call that can fail, where you used to just return an optional (I've recently converted a whole bunch of methods in my own code). This much more liberal use of error reporting, however, can cause headaches for the caller without a try? syntax to opt into the older behavior.

                                                            • Re: Can we get a "try?" as a more lenient "try!"?
                                                              Magicman Level 1 Level 1 (10 points)

                                                              Thats far too general a statement to make. What constitutes an "error" depends on the API designer, but in general an error occurs when a method fails to perform all of its intended actions. Whether or not you can/must/should handle an error depends on the specific failing method and how and why you are calling it.

                                                               

                                                              Swift has a sensible default behaviour - that methods should always succeed, and if not then you should handle that explicitly. They accept that sometimes there is no way to reasonably handle a failure, so we have try!. The same logic goes in reverse - there are times where there is no need to handle a failure, so it follows we should have a try? keyword.

                                                               

                                                              If they don't add it, people will just hack around it with shims like the above examples. It's better that they bring it in to the language - it could present optimisation opportunities and there may be sensible limitations they can impose for safety.

                                                                • Re: Can we get a "try?" as a more lenient "try!"?
                                                                  ahltorp Level 3 Level 3 (285 points)

                                                                  The following is just an argument about the optimization opportunities.

                                                                   

                                                                  The Swift compiler (and I include LLVM in that) has already managed to make complicated deductions from code that is not obviously optimizable. Before byteswapping was built into the language, an expression like this:

                                                                   

                                                                  ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF)
                                                                  

                                                                   

                                                                  was optimized into one single x86 instruction (bswap). From what I understand, the most important thing for the optimizer is that the knowledge about the code's use is as complete as possible. The compiler could inline the closure, and then optimize it just as well as a built in statement. It doesn't now, however. But when I tried my ignorethrow code, the compiler already called the closure statically, so I suppose it has enough information for inlining, and therefore further optimizations.

                                                                  • Re: Can we get a "try?" as a more lenient "try!"?
                                                                    RLKingSoftware Level 3 Level 3 (475 points)

                                                                    No, it is not. Wether you act on an error or not, is up to you. How you handle the error, is not.

                                                                    Try! is a lame lazy way of abdicating responsibility. You ask for an action, you need to deal

                                                                    with the consequences of that request. If it fails and you really don't care, fine, ignore it or better

                                                                    yet, set a flag so some other method knows what and why something did or did not function. Windows(tm)

                                                                    has billions of errors in its code. Most of them, are ignored. Do you really want to write that kind of code?

                                                                    Think of a neural net. Errors in such a construct could prove fatal, not just to software but to the users

                                                                    of that code. Consider the SSL and like supposedly secure entities. By all means, write lazy code if it

                                                                    gets you through the day. Personally, I'd rather never write a single line of code if I could not be sure

                                                                    it would produce the same result, every time it executed.

                                                                • Re: Can we get a "try?" as a more lenient "try!"?
                                                                  Monicker Level 1 Level 1 (10 points)

                                                                  I would posit that Swift is correct as it is, but the FileManager API is what needs to be changed.

                                                                   

                                                                  It sounds like we need `NSFileManager { func ifExistsDeleteFile(file:string) -> Void` or something

                                                                  • Re: Can we get a "try?" as a more lenient "try!"?
                                                                    Wallacy Level 1 Level 1 (20 points)

                                                                    As shown at WWDC, Apple divides errors into 3 categories:

                                                                     

                                                                    1. Trivial errors
                                                                      • Int(someString), etc.
                                                                    2. Detailed, recoverable errors
                                                                      • File not found
                                                                      • Network failure
                                                                      • User cancellation, etc.
                                                                    3. Logic errors
                                                                      • Assertions, overflows, NSException, etc.

                                                                     

                                                                    The presenter says explicitly: "Int(someString) can fail, but an optional return type is fine", and in swift ErrorHandler is about the category 2.


                                                                    The Optionals will help trivial errors, and logic errors should crash you app, and you must fix this.

                                                                     

                                                                    I'm ok with that! Except that lack the operator "try?"! Let me explain:

                                                                     

                                                                    Who said i did not need to know in more detail why Int(someString) failed?

                                                                     

                                                                    That was a developer arbitrary decision based on own experience: In most cases, you just need to know that the parser failed and deal with it. Optionals are more than enough in this case.

                                                                     

                                                                    And I always can "promote" an error of category 1 to category 2 or even 3.

                                                                    // category 1
                                                                    if let a = Int("1") {
                                                                    
                                                                    }
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                     

                                                                    // category 2
                                                                    enum DataError : ErrorType {
                                                                        case ParserError
                                                                    }
                                                                    extension String {
                                                                        func toInt() throws -> Int {
                                                                            guard let a = Int("1") else {
                                                                                throw DataError.ParserError;
                                                                            }
                                                                            return a;
                                                                        }
                                                                    }
                                                                    do {
                                                                    let a = try "1".toInt()
                                                                    //...
                                                                    } catch {
                                                                    //...
                                                                    }
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                    // category 3
                                                                    let a = Int("a")!
                                                                    print("This string number is: \(a)")
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                    At the same time, we have the ability to do the reverse:

                                                                     

                                                                    //1
                                                                    extension Array {
                                                                        subscript (s index: Int) -> T? {
                                                                            return self.indices ~= index ? self[index] : nil
                                                                        }
                                                                    }
                                                                    //2
                                                                    enum DataError : ErrorType {
                                                                        case OutOfBounds
                                                                    }
                                                                    extension Array {
                                                                        func atIndex(index: Int) throws -> T {
                                                                            guard let response = self[s: index] else {
                                                                                throw DataError.OutOfBounds
                                                                            }
                                                                            return response
                                                                        }
                                                                    }
                                                                    let arr = [1,2,3]
                                                                    let c = arr[s: 5]; // possible error category 1
                                                                    let b = try arr.atIndex(6); // possible error category 2
                                                                    let a = arr[7]; // possible error category 3
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                    The ability to change how important an error is has always been on the developer's responsibility, is an explicit agreement between him and the compiler because it formally established the functions for such.

                                                                     

                                                                    We already have the try! operator to promote some code made on "category 2" to 3 code. But we dont have to make the category 2 to 1. Its possible make some hacks, but is better make this built in on the language.

                                                                     

                                                                    If we look some samples codes from Apple, scattered throughout developer library we will see that in several places when they pass nil as the argument to a call NSError function, and this is not anything crazy: I repeat, some errors messages are irrelevant in some applications.

                                                                     

                                                                    And in several cases we have to deal with a developer arbitrary decision to provide a more detailed response to a problem:

                                                                    // Swift 1.2
                                                                    if let unarchiveJSON = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil) as? [String:NSObject] {
                                                                       // This is all i need, no detail is important to me in this app, in another app maybe will.
                                                                    }
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                    But now:

                                                                     

                                                                    var unarchiveJSON:[String:NSObject]?
                                                                    do {
                                                                        unarchiveJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String:NSObject]
                                                                    } catch { }
                                                                    if let unarchiveJSON = unarchiveJSON {
                                                                    // ...
                                                                    }
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                    Using "try?"

                                                                    if let unarchiveJSON = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String:NSObject] {
                                                                       // This is all i need in this particular case example.
                                                                    }
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                    The try? operator is valid in this context as any other explicit treatment. Is me, developer saying: In this context, that error is not of category 2 but category 1.

                                                                    This new proposed operator, will simply normalize the inevitable fact that in many cases some errors messages are not important, using a unified, clear syntax for everyone. And it will also, maybe, help to handle with the case "catch {}" making this a compilation error (error-prone). Optionals result, is, in that way, some kind of error handler! I'm not avoid the error, just making another treatment, with less boilerplate.

                                                                     

                                                                    And most importantly: Will encourage more people to detail their errors using "throws" without fear of creating problems for those who use your code.

                                                                     

                                                                    With try? operator, Int(someString) could throws some error and this:

                                                                     

                                                                    if let a = try? Int("1") {
                                                                    //...
                                                                    }
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    

                                                                     

                                                                    Will be used for people who dont care why this Int parser fail, like i don't care why NSJSONSerialization fail above.

                                                                      • Re: Can we get a "try?" as a more lenient "try!"?
                                                                        owensd Level 1 Level 1 (10 points)

                                                                        A try? operator would be excellent.

                                                                         

                                                                        In practice, I've started moving away from failable initializers for throwing initializers for exactly the reason you mentioned, I can actually provide the caller the reason it failed.

                                                                         

                                                                        The other great thing about try?, it's easily auditable.

                                                                         

                                                                        Have you logged a bug for this?

                                                                        • Re: Can we get a "try?" as a more lenient "try!"?
                                                                          Magicman Level 1 Level 1 (10 points)

                                                                          This was such a good expression of why a try? keyword would be useful, it really deserves a reply from an Apple engineer (unless they're all on holiday).


                                                                          If they don't do it, people will create their own little hacks. We're talking about error code, people - sometimes reality will tell us that we misjudged the importance of some error, or in general handled it incorrectly. By bringing all of these constructs in to the language, we will at least have consistency when we come to debug this stuff, including being able to turn on logging or trapping for all hitherto-ignored errors.

                                                                        • Re: Can we get a "try?" as a more lenient "try!"?
                                                                          CharlesS Level 2 Level 2 (40 points)

                                                                          Well, it looks like they have dealt with this simply by converting checkResourceIsReachable back into a non-throwing method; now it's once again checkResourceIsReachableAndReturnError. Still no fix for use cases like the OP's, unfortunately.

                                                                          • Re: Can we get a "try?" as a more lenient "try!"?
                                                                            dwaite Level 1 Level 1 (0 points)

                                                                            The one issue with 'try?' that I see is that the '!' family indicates code may be making unsafe assumptions in the case of code review. A try? may actually be an unsafe assumption - that we can ignore the error value returned from a method; that the error won't cascade further down the line making debugging hellish. It's just not an unsafe assumption that immediately results in the app asserting/crashing.

                                                                             

                                                                            There are cases where you should be able to safely ignore an error, but I fear 'try?' makes it way too easy to ignore errors that the developer should actually be acting on. Some sort of standard library 'ignorethrow' @noescape function may be more appropriate

                                                                              • Re: Can we get a "try?" as a more lenient "try!"?
                                                                                john.estropia Level 1 Level 1 (10 points)

                                                                                "Unsafe assumptions" seem to be the theme for the "?" marker already. Take as? for example. You may assume that it evaluates to `nil` if it fails downcasts, but it may actually be upcast failures or that the original value is `nil`.


                                                                                The thing is, most methods in the iOS SDK that report errors have a way to show success or failure without having to look at the errors themselves. Either they return true/false, or an object/nil. try? just let us "ignore errors" in such a way that we don't have to create a do-catch scope when all we need is to know the true/false or object/nil result. In other words, yes you are explicitly ignoring errors but you can still handle success and failure.

                                                                                • Re: Can we get a "try?" as a more lenient "try!"?
                                                                                  john.estropia Level 1 Level 1 (10 points)

                                                                                  I'm glad they added this; certainly saves a few lines of code and scope nesting.

                                                                                  One thing that beta 6 added that goes well with "try?" is the warning of unused return values. for example, writing

                                                                                  try? fileManager.removeFileAtPath(cachePath1)
                                                                                  

                                                                                   

                                                                                  will result in the warning "Result of 'try?' is unused".  This prevents accidental bugs and you can still explicitly indicate if you did this on purpose:

                                                                                  _ = try? fileManager.removeFileAtPath(cachePath1)