Reputation: 36003
I am developing my first app in SwiftUI and my brain have not wrapped around certain things yet.
I have to display prices, titles and descriptions of inapp purchases on the interface.
I have a singleton model like this, that loads as the app starts.
class Packages:ObservableObject {
enum Package:String, CaseIterable {
typealias RawValue = String
case package1 = "com.example.package1"
case package2 = "com.example.package2"
case package3 = "com.example.package3"
}
struct PurchasedItem {
var productID:String?
var localizedTitle:String = ""
var localizedDescription:String = ""
var localizedPrice:String = ""
}
static let sharedInstance = Packages()
@Published var purchasedItems:[PurchasedItem] = []
func getItem(_ productID:String) -> PurchasedItem? {
return status.filter( {$0.productID == productID } ).first
}
func getItemsAnync() {
// this will fill `purchasedItems` asynchronously
}
purchasedItems
array will be filled with PurchasedItem
s, asynchronous, as the values of price, title and description come from the App Store.
Meanwhile, at another part of the interface, I am displaying buttons on a view, like this:
var buttonPackage1String:String {
let item = Packages.sharedInstance.getItem(Packages.Package.package1.rawValue)!
let string = """
\(umItem.localizedTitle) ( \(umItem.localizedPrice) ) \
\(umItem.localizedDescription) )
"""
return string
}
// inside var Body
Button(action: {
// purchase selected package
}) {
Text(buttonPackage1String)
.padding()
.background(Color.green)
.foregroundColor(.white)
.padding()
}
See my problem?
buttonPackage1String
is built with title, description and price from an array called purchasedItems
that is stored inside a singleton and that array is filled asynchronously, being initially empty.
So, at the time the view is displayed all values may be not retrieved yet but will, eventually.
Is there a way for the button is updated after the values are retrieved?
Upvotes: 1
Views: 87
Reputation: 4746
Below is a small pseudo code, I'm not sure it's suitable for your project but might give you some hint.
Packages.sharedInstance
.$purchasedItems //add $ to get the `Combine` Subject of variable
.compactMap { $0.firstWhere { $0.id == Packages.Package.package1.rawValue } } //Find the correct item and skip nil value
.map { "\($0.localizedTitle) ( \($0.localizedPrice) ) \ \($0.localizedDescription) )" }
.receive(on: RunLoop.main) // UI updates
.assign(to: \.buttonTitleString, on: self) //buttonTitleString is a @Published variable of your View model
.store(in: &cancellables) //Your cancellable collector
Combine
framework knowledge is a must to start with SwiftUI.
Upvotes: 1
Reputation: 258345
Calculable properties are not observed by SwiftUI. We have to introduce explicit state for this and update it once dependent dynamic data changed.
Something like,
@ObservedObject var packages: Packages
...
@State private var title: String = ""
...
Button(action: {
// purchase selected package
}) {
Text(title)
}
.onChange(of: packages.purchasedItems) { _ in
self.title = buttonPackage1String // << here !!
}
Upvotes: 1