I am experienced programmer (primarily procedural languages - FORTRAN, C, Pascal, Tcl/TK, etc.), I have written some modules for a solo project that I've taken on as a retirement project. I'm currently trying to understand try-catch blocks. This is the section of code that I'm working on:
init(name: String, age: Int) {
let fileContents = try {
String(contentsOfFile: filePath, encoding: .utf8)
} catch {
// Handle the error if the file cannot be read
print("Error reading file: (error)")
return nil
}
This produces the error of: /Users/wayned1/.../main.swift:27:28 The 'do' keyword is used to specify a 'catch' region
and helpfully offers to fix this to:
init(name: String, age: Int) {
let fileContents = do { // /Users/wayned1/.../main.swift:27:27 Consecutive statements on a line must be separated by ';'
String(contentsOfFile: filePath, encoding: .utf8) // /Users/wayned1/.../main.swift:27:28 Expected initial value after '='
} catch {
// Handle the error if the file cannot be read
print("Error reading file: \(error)")
return nil // /Users/wayned1/.../main.swift:32:13 Only a failable initializer can return 'nil'
}
I have 4 books on Swift & IOS programming:
Swift cookbook, second edition (2019)
Mastering Swift5, Fifth edition (2021)
IOS programming, The Big Nerd Ranch guide, 6th edition (2016) - old
Swift Fundamentals (2014) -- this seems to be real old
I'm having problems groking Swift. Is there a website that will teach a fossil
about some of this stuff?
The book "Advanced Swift" has a chapter on error handling, I don't have the other ones handy to compare.
It's worth doing some web searching for blog/medium articles about the topic you are focused on. Swift playgrounds can be a useful tool for exploring something in isolation as well.
talk to an AI, it's very capable to help with resolving those questions.
About one third of Swift is what you already know from those mentioned languages, just with slightly different syntax. Another third is what you'd know from C++ or Java/Kotlin.
And don't panic – you don't have to know all of Swift at once, even if you know half of Swift you could do quite a lot.
The basic pattern is do-try-catch where the try is inside the first block:
do {
fileContents = try String(contentsOfFile: filePath, encoding: .utf8)
} catch {
// Handle the error if the file cannot be read
}
If you are doing this in an initializer, you can either have a failable initializer using init?:
class Sample1 {
let filePath: String = …
let fileContents: String
init?() {
do {
fileContents = try String(contentsOfFile: filePath, encoding: .utf8)
} catch {
print("Error reading file: \(error)") // note, the missing \ has been added
return nil
}
}
}
Or you can have a throwing initializer, that throws the error:
class Sample2 {
let filePath: String = …
let fileContents: String
init() throws {
do {
fileContents = try String(contentsOfFile: filePath, encoding: .utf8)
} catch {
print("Error reading file: \(error)")
throw error
}
}
}
In this latter example, the question is whether you really need this to print the error, giving that it is throwing the error and the caller can decide what to do. So you might simplify that to:
class Sample3 {
let filePath: String = …
let fileContents: String
init() throws {
fileContents = try String(contentsOfFile: filePath, encoding: .utf8)
}
}
Anyway, the benefit of the throws pattern is that the caller can catch (with its own do-try-catch block), itself, in case different handling is needed on the basis of the precise error message. E.g., you might show one error if the file is not found and another if the UTF8 encoding failed. If you just return nil from a failable initializer (init?), the caller does not know what sort of message to show to the user.
As a more “personal preference” issue, I might suggest that you might consider keeping init very lightweight, avoiding memory-intensive and/or blocking/synchronous API. You might move the fetching of the contents of the file into a separate method (and/or consider asynchronous and less memory intensive alternatives). But that’s beyond the scope of the question.
Also, you need to roll up your sleeves and practice by writing lots of small, toy programs.
For each toy program, you can use the following templates as starting points.
Template 1 (for synchronous world)
// MainDriver.swift
import Foundation
@main
enum MainDriver {
static func main () {
work ()
}
}
private func work () {
let log = {
print (#function, $0)
}
let clock = ContinuousClock ()
let t0 = clock.now
let value = Int.random (in: 0..<1024)
let d = clock.now - t0
log ("took \(d) to get \(value)")
}
Template 2 (for asynchronous world)
// MainDriver.swift
import Foundation
@main
enum MainDriver {
static func main () async {
await work ()
}
}
private func work () async {
let log = {
print (#function, $0)
}
log ("starting a task...")
let clock = ContinuousClock ()
let t0 = clock.now
let task = Task {
return Int.random (in: 0..<1024)
}
log ("waiting for task to finish...")
let value = await task.value
let d = clock.now - t0
log ("took \(d) to get \(value)")
}