-1

I found out from this post we can create fixed size array as such var array = [String?](repeating: nil, count: 10)

How can I do the same, when the parameter count is passed in from different view.

I have a primary view where user selects a start date and end date. I pass the dates to secondary view.

Using information from this post I figured out how to get all days between two dates.

I want to create a state variable as such @State private var names: [String?](repeating: nil, count: DayCount) Where DayCount is total days.

struct SecondaryView: View {
    // loop start and end date are passed from primary view
    var loopStartDate = Date()
    var loopEndDate = Date()
    @State private var dateArray = Date.dates(from: loopStartDate, to: loopEndDate)
    // throws cannot use instance member 'loopEndDate' within property initializer; 
        //property initializers run before 'self' is available

    @State private var names = [String?](repeating: nil, count: dateArray.count)
   // this throws error `Cannot use instance member 'dateArray' within property
      //initializer; property initializers run before 'self' is available`
HangarRash
  • 7,314
  • 5
  • 5
  • 32
  • 1
    "we can create fixed size array" No, it isn't fixed. – matt Dec 29 '22 at 00:19
  • See [How to initialize properties that depend on each other](https://stackoverflow.com/questions/25854300/how-to-initialize-properties-that-depend-on-each-other) – HangarRash Dec 29 '22 at 00:24

1 Answers1

0

Swift doesn't allow using the self instance before it's completely initialized, so using variables that depend on other variables isn't allowed (unless they're lazy, which requires using them after initialization).

Imagine two variables whose initialization depends on each other. Without having a provided initializer, which one should be initialized first? How would you resolve the conflict? How would you make a generic approach to solving this in more complex cases?

// Imaginary scenario
struct Test {
    var first: Int = second + 1
    var second: Int = first - 2
}

let test = Test()
print(test.first) // What would this print?

Swift just forbids it. It's up to you to come up with an initialization strategy inside your initializer.

Therefore, a solution could be just adding an initializer that takes care of that:

struct SecondaryView: View {
    var loopStartDate = Date()
    var loopEndDate = Date()
    @State private var dateArray: [Date]
    @State private var names: [String?]

    init() {
        dateArray = Date.dates(from: loopStartDate, to: loopEndDate)
        names = Array<String?>(repeating: nil, count: dateArray.count)
    }
}

Note that manually adding an initializer will prevent the automatically generated one from being created, so you'll have to manually add future attributes to it.

Renzo Tissoni
  • 642
  • 9
  • 21
  • Extra note, `[String?](repeating: nil, count: 10)` doesn't create a fixed-size array, rather, it's just an array filled with ten `nil` values. Declaring it as `var` and adding elements to it will correctly add another value to the array. – Renzo Tissoni Dec 29 '22 at 00:29
  • Now there's another error being thrown in `PrimaryView` when calling `SecondaryView` due to`init() { dateArray = Date.dates(from: loopStartDate, to: loopEndDate) names = Array(repeating: nil, count: dateArray.count) }` which says `arguement passed to call that takes no argument` @renzo – ribash sharma Dec 30 '22 at 03:04
  • You haven't shown what `PrimaryView` looks like, but the implementation I wrote before takes no arguments, so `SecondaryView()` should be enough to initialize it. However, it's not plug-and-play. There are some missing conformances, like the body of the view that I left out in my response, so maybe take that into account as well. I also don't know what `Date.dates(from: loopStartDate, to: loopEndDate)` is, but you seem to have it in your codebase. – Renzo Tissoni Jan 04 '23 at 15:34