3

I want to take say a stock price and then generate values within .05% in a 2D array.

I am passing the stock information such as stock name and stock price.

But when I try and run a method that creates the 2D array of values I am met with a "Cannot use instance member '' within property initializer; property initializers run before 'self' is available"

I was looking how to get around this.

Say Amazon stock is 2400. This data passes into the new view. What I want to do is create a 2D array of values near 2400, perhaps 2405 and 2395. I want a 2D array of these values generated in the new view. But I am getting an error that I cannot generate the values until the view is loaded, but I need them to generate the view.

I think I have to use init method but I am not sure how.

This is the line that is throwing the error above.

var new_prices = generateValues(price: position.stock_price)

Here is the method

func generateValues(price: Double) -> [[Double]] {
    var values = [[Double]]()
     for x in 1...5 {
        values[0][x-1] = (price + price * 0.005 * Double(x))
    }
    return values
}

This is what is being passed in the stock postion

struct Position : Identifiable {
    var id = UUID()
    var stock_name: String
    var stock_ticker: String
    var stock_price: Double
    var price_paid: Double

}

Arguments passed throwing an error for no argument init().

struct PositionDetail_Previews: PreviewProvider {
    static var previews: some View {
        PositionDetail(position: Position(stock_name: "Amazon", 
stock_ticker: "AMZN", stock_price : 2400, price_paid : 2300))
    }
}
5
  • 2
    please provide a stackoverflow.com/help/minimal-reproducible-example so we can easily help you Commented May 27, 2020 at 2:55
  • I removed the code. It's not really necessary. Commented May 27, 2020 at 3:02
  • 2
    unfortunately you misunderstood me. a reproducible and runnable example is a big help for all who want to help you. if you want to make it easy for helpers, provide this example. this is not a "we write code for u for free" platform, but a "we help you with your provided code"- platform Commented May 27, 2020 at 3:22
  • My question isn't code specific. It's a general question of how data is created/passed in SwiftUI. I could easily do this in UIKit or any other language. I just don't know how SwiftUI handles variable initialization from passed values. Commented May 27, 2020 at 3:33
  • I threw in the code that I wrote. I think the mistake is I need to create an init method but I'm confused on how to do this. Commented May 27, 2020 at 3:47

4 Answers 4

2

You could do

struct ContentView: View{
@State var new_prices = [[Double()]]
        init () {
            new_prices = generateValues(price: position.stock_price)
        }
    var body: some View{
           your view...
     }
}
Sign up to request clarification or add additional context in comments.

11 Comments

I get this error on the for each loop in my vstack "Cannot use mutating getter on immutable value: 'self' is immutable"
Ill need it every time the view loads.
if you are using swiftUI, then instead of using lazy, use the State property wrapper so that it will be @State var new_prices = generateValues(price: position.stock_price)
Then I get the original error "Cannot use instance member 'position' within property initializer; property initializers run before 'self' is available" little bit of chicken and egg with these errors. I think it has to be in an init method?
so this is about to where I got although yours has better structure. But then I get the issue (check the last edit. bottom of the post) where I need to include the Position data in the init because of argument errors. Initializing the init is what I can't figure out how to do.
|
0

If I correctly understood & replicated your code initially you have got this

demo

so here is a solution (tested with Xcode 11.4 / iOS 13.4)

func generateValues(price: Double) -> [[Double]] {
    var values = [[Double]]()
    for x in 1...5 {
        values[0][x-1] = (price + price * 0.005 * Double(x))
    }
    return values
}

struct PositionDetail: View {
    var position: Position
    var new_prices: [[Double]]

    init(position: Position) {
        self.position = position
        self.new_prices = generateValues(price: position.stock_price)
    }

    var body: some View {
        VStack { // << just for demo
            Text("Position: \(self.position.stock_name)")
            Text("First price: \(self.new_prices[0][0])")
        }
    }
}

other code with no changes.

11 Comments

The position preview is throwing 3 errors. "Type 'PositionDetail_Previews' has no member 'generateValues'", "Type 'PositionDetail_Previews' has no member 'new_prices'", "Type 'PositionDetail_Previews' has no member 'position'". I did put new_prices in the List and it did not throw an error so I believe this will work but the preview is holding me back.
I believe the init and the previews need to align I just don't know how.
@aeipownu, I assume you changed something in your code, because in preview provided in your question only one init(position:) is used. Also as you can see I moved generateValues out of struct to standalone, because it does not depend on internals, so it is design incorrect to make it member.
pastebin.com/1vNWqLfe Here is the whole page of code. There is only one position being passed in the preview just as above.
When I comment out preview the code actually builds which is a big step forward. However I am only getting one single value out of new_prices. Not sure why as I have hard coded a test grid.
|
0

This is how you call dynamic functions to initialize the current version of SwiftUI View data. (Note: no .init() needed):

import SwiftUI

struct Position : Identifiable {
    var id = UUID()
    var stock_name: String
    var stock_ticker: String
    var stock_price: Double
    var price_paid: Double
}

struct PositionDetail: View {
    var position: Position

    func generateValues(price: Double) -> [[Double]] {
        var values: [[Double]] = Array(repeating: Array(repeating: 0, count: 5), count: 1)

        for x in 1...5 {
            values[0][x-1] = (price + price * 0.005 * Double(x))
        }

        return values
    }

    var body: some View {
        let values   = generateValues(price: position.stock_price)

        return HStack {
            ForEach(values, id:\.self) { row in
                HStack {
                    ForEach(row, id: \.self) { value in
                        Text("\(value)")
                    }
                }
            }
        }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}


struct PositionDetail_Previews: PreviewProvider {
    static var previews: some View {
        PositionDetail(position: Position(stock_name: "Amazon", stock_ticker: "AMZN", stock_price : 2400, price_paid : 2300))
    }
}

Note: The generateValues function, as specified, was broken...

1 Comment

As an FYI, modifying the .init() of a View struct is not in itself bad, and when simple let/var elements are used, there is no problem at all. But when your View struct uses the @State and/or @Binding property wrappers, the complexity of the .init() can go up quite fast.
0

Using your position model:

struct Position : Identifiable {
    var id = UUID()
    var stock_name: String
    var stock_ticker: String
    var stock_price: Double
    var price_paid: Double

}

You could generate the values when creating the view, given an existing Position:

struct StockPriceViewBeforehand: View {
    // MARK: Properties
    var position: Position
    var newPrices: [[Double]]

    // MARK: Body
    var body: some View {
        Text("Use prices here")
    }
}

struct StockPriceViewBeforehand_Previews: PreviewProvider {
    static var previews: some View {
        let amznPosition = Position(stock_name: "Amazon", stock_ticker: "AMZN", stock_price : 2400, price_paid : 2300)        
       return StockPriceViewBeforehand(position: amznPosition, newPrices: StocksValueGenerator.generateValues(price: amznPosition.stock_price))
    }
}


final class StocksValueGenerator {
    static func generateValues(price: Double) -> [[Double]] {
        var values = [[Double]]()
        for x in 1...5 {
            values[0][x-1] = (price + price * 0.005 * Double(x))
        }
        return values
    }
}

Note that I used the generateValues method as a static function of a StocksValueGenerator class above, but you could use it as you wish.

As an alternative, you could call the generate values method when the view appears:

struct StockPriceView: View {
    // MARK: Properties
    var position: Position
    @State var values = [[Double]]()

    // MARK: Body
    var body: some View {
        Text("Your view here")
            .onAppear {
                self.values = self.generateValues(price: self.position.stock_price)
            }
    }

    // MARK: Methods
    func generateValues(price: Double) -> [[Double]] {
        var values = [[Double]]()
        for x in 1...5 {
            values[0][x-1] = (price + price * 0.005 * Double(x))
        }
        return values
    }
}

struct StockPriceView_Previews: PreviewProvider {
    static var previews: some View {
        let amznPosition = Position(stock_name: "Amazon", stock_ticker: "AMZN", stock_price : 2400, price_paid : 2300)

        return StockPriceView(position: amznPosition)
    }
}

The difference between these two options is that in the first one you would create the View with the already generated newPrices, so the View could display them accordingly when appearing. On the second option, you would first have an empty array of prices, which would then be assigned the generated ones.

(I know the second option doesn't count as initializing values before the view is loaded, but I just thought I'd give you some options)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.