Можно ли выкинуть из класса init() в Swift с постоянной строкой, загруженной из файла?

Я хотел бы, чтобы мой класс init() в Swift выкинул ошибку, если что-то пошло не так, как при загрузке файла в строку внутри класса. Как только файл будет загружен, строка не будет изменена, поэтому я бы предпочел использовать let. Это работает:

class FileClass {    
    var text: NSString = ""   
    init() throws {   
        do {
            text = try NSString( contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) }
        catch let error as NSError {
            text = ""
            throw error
        }      
    }
}

но когда я заменяю

var text: NSString = ""

с

let text: NSString

Я получаю . Все хранимые свойства экземпляра класса должны быть инициализированы перед тем, как выбраться из ошибки инициализации.

Я пробовал различные подходы, например, чтобы сделать текст необязательным

let text: NSString?

но не нашли никакой работы. Возможно ли, чтобы текст был загружен из файла, неизменный, и для init(), чтобы выбросить ошибку? Могу ли я получить торт и съесть его три?

Большое спасибо заранее!

Ответ 1

[Обновить] Swift Version >= 2.2

Так как Swift 2.2 вы можете нарушить выполнение инициализатора класса без необходимости заполнения всех сохраненных свойств

class FileStruct {
    let text: String

    init() throws {
        do {
            text = try String(contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) }
        catch let error as NSError {
            throw error
        }
    }
}

Swift Version <= 2.1

В настоящее время в Swift class вы не можете сломать выполнение инициализатора перед инициализацией каждого сохраненного свойства.

С другой стороны, у вас нет этого ограничения с structs, поэтому

struct FileStruct {
    var text: String

    init() throws {
        do {
            text = try String(contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) }
        catch let error as NSError {
            throw error
        }
    }
}

Вы также можете избежать блокировки do/catch

struct FileStruct {
    var text: String

    init() throws {
        text = try String(contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding)
    }
}

Наконец, я заменил NSString на String, так как мы используем Swift, а не Objective-C; -)

Ответ 2

Вместо этого вы можете использовать отказоустойчивый инициализатор, как это более подходит для такого рода сценариев.

class FileClass {
    let text: String
    init?() {
        guard let fileContents = try? NSString( contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) else {
            text = ""
            return nil
        }
        text = fileContents as String
    }
}

или, если вы хотите напечатать ошибку:

class FileClass {
    let text: String
    init?() {
        do {
            text = try String( contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding )
        } catch let error as NSError {
            print("Error while reading: \(error)")
            text = ""
            return nil
        }
    }
}

Использование проще, чем с инициализатором throwing, поскольку вы можете использовать if-let или guard-let:

if let file = FileClass() {
}

или

guard let file = FileClass() else {
    return
}

против

let file: FileClass
do {
    file = FileClass()
} catch {
}