Home > Net >  why can't my SwiftUI struct be initialised outside the View?
why can't my SwiftUI struct be initialised outside the View?

Time:12-16

I couldn't get my the below struct to initialise until I moved it into the body of the View. Can someone explain that to me please? Thanks!

This didn't work:

struct ContentView: View {
    var sampleText = "This is sample text"
    var booksArray = ["Book 1", "Book 2", "Book 3"]
    let aBook = Book(passage: sampleText, books: booksArray)
    
    var body: some View {
        ScrollView(.vertical, showsIndicators: false, content: {
            VStack {
                Text(aBook.passage)
                    .frame(width: 350, height: .infinity, alignment: .center)
                    .kerning(1)
            }
        })
    }
}

But this did work:

struct ContentView: View {
    var sampleText = "This is sample text"
    var booksArray = ["Book 1", "Book 2", "Book 3"]
    
    var body: some View {
        let aBook = Book(passage: sampleText, books: booksArray)
        ScrollView(.vertical, showsIndicators: false, content: {
            VStack {
                Text(aBook.passage)
                    .frame(width: 350, height: .infinity, alignment: .center)
                    .kerning(1)
            }
        })
    }
}

CodePudding user response:

The error you got from this code reads:

cannot use instance member 'sampleText' within property initializer; property initializers run before 'self' is available

which in plain English means that when aBook is being assigned, self, which is the ContentView is not yet available, so you cannot use sampleText and booksArray to initialize aBook (because it requires all the properties to be initialized - to hold a value).

You might think, ok then I will use lazy to initialize aBook, which means that the assignment of the property will happen after ContentView has finished the initialization - at first access.

lazy var aBook = Book(passage: sampleText, books: booksArray)

but then a new error appears:

Text(aBook.passage) <= cannot use mutating getter on immutable value: 'self' is immutable

wait, what? But I'm not trying to mutate it you might say.

The reality though is that you do mutate ContentView by accessing aBook. Let's illustrate this:

  1. sampleText is assigned a value
  2. booksArray is assigned a value
  3. aBook is not assigned since is lazy. Waits for access in order to be assigned a value.
  4. ContentView is initalized
  5. You access aBook in the body
  6. aBook tries to assign a value, but because ContentView is a struct (value type - immutable) it needs to make a copy of the whole thing. Which is why you get the error

So, long story short you got a few options:

  1. let aBook = Book(passage: sampleText, books: booksArray) in the body (as you already mention)

  2. A computed property:

    var aBook: Book { Book(passage: sampleText, books: booksArray) }

or a custom initializer:

init(sampleText: String, booksArray: [String]) {
    self.sampleText = sampleText
    self.booksArray = booksArray
    self.aBook      = Book(passage: sampleText, books: booksArray)
}

or even:

let sampleText = "This is sample text"
let booksArray = ["Book 1", "Book 2", "Book 3"]
let aBook: Book

init() {
    self.aBook = Book(passage: sampleText, books: booksArray)
}

I hope that this makes sense.

  • Related