If your self.init method fails by throwing an exception, the state of the class 
is essentially unknown. Trying to recover inside the convenience init method by 
calling another initializer seems problematic since you don't know what the 
state will be after the second init. Therefor, it's probably more correct to do 
the exception handling outside of this class like you did in the MakeTestClass 
function.

But just for the heck of it, I tried taking the second init outside the catch 
block, and put a flag that something failed. But Swift won't allow any uses of 
self after the first catch. For example (with errors on the lines the compiler 
reports):

convenience init( fromFile file:NSURL, fromBackupFile backupFile:NSURL ) throws
{
    var firstInitFailed = false

    do {
        try self.init( fromFile:file ) 
    } catch {
        do {
            firstInitFailed = true
                }
        }

    if  firstInitFailed == false {
        try self.init( fromFile:backupFile) // 'self' used inside 'catch' block 
reachable from self.init call
        }
} // 'self' used inside 'catch' block reachable from self.init call
This might be a bug in the compiler since the error message isn't correct; self 
isn't referenced in the catch block. Also the error message on the closing 
brace is puzzling.

In any case, this appears to imply that in Swift you can't do any error 
recovery that involves self inside of an init method, even if you get an 
exception from something other than calling self.init. This doesn't seem right 
if the throw was for something that doesn't affect the object state. For 
example, if you had a file system call that tried to create 'fromFile' and that 
call threw an exception. This shouldn't affect anything with class state and 
should be possible to handle it without affecting object initialization.

The third option is to move this file handling outside of the init method. For 
example:

convenience init( fromFile file:NSURL, fromBackupFile backupFile:NSURL ) throws
{
    try self.init(fromFile: file)

    self.openFile(fromFile: file, fromBackupFile: backupFile)
}

func openFile( fromFile file:NSURL, fromBackupFile backupFile:NSURL )
{
    do {
        // try to open the fromFile
    } catch {
        do {
            // try to open the backupFile
        }
    }
}

However, this changes the class' init behavior since the designated initializer 
can't do the file opening/handling. I'm guessing your best bet is going with 
the separate function to handle this. Or make it a type method.

Doug Hill



> On Jul 25, 2016, at 10:46 AM, J.E. Schotsman via swift-users 
> <swift-users@swift.org> wrote:
> 
> Hello,
> 
> I have a class that can be initialized from a file.
> I would like to add a convenience init that uses a backup file if necessary.
> Writing a function for this is easy, but I can’t manage to turn this into an 
> init function.
> 
> import Foundation
> 
> class TestClass
>       {
>       init( fromFile file:NSURL ) throws
>               {
>               
>               }
>       
>       convenience init( fromFile file:NSURL, fromBackupFile backupFile:NSURL 
> ) throws
>               {
>               do { try self.init( fromFile:file ) }
>               catch {
>                       do { try self.init( fromFile:backupFile ) } // error: 
> 'self' used inside 'catch' block reachable from self.init call
>                       }
>               }
>       }
> 
> func MakeTestClass( fromFile file:NSURL, fromBackupFile backupFile:NSURL ) 
> throws -> TestClass
>       {
>       do { return try TestClass( fromFile:file ) } 
>       catch { do { return try TestClass( fromFile:backupFile ) } } 
>       }
> 
> Any suggestions?

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to