Hi! I'm new to this community, and this is my first post :)
I am new to Swift and SwiftUI/SwiftData, but I have 25 years experience in programming coming from a PHP/TS/Go/Rust background.
I'm working on a SwiftUI form for adding new hotels, where each hotel belongs to a specific category (e.g., big hotel, medium hotel, small hotel). Each category defines a default number of rooms, and for each room, I need to capture a description. The hotel category also has a boolean which allows or not the user to override the category number of rooms of an individual hotel.
In my implementation, selecting a hotel category updates the form to include a text input field for the description of each room based on the category's default room count. This works well when the room count is directly tied to the hotel category.
However, the form also allows users to manually adjust the number of rooms using a stepper, overriding the default count specified by the hotel category. It's at this point I encounter a challenge: when the number of rooms is increased via the stepper, I get an 'Index out of range' error accessing the array that holds the room descriptions.
How can I dynamically resize this descriptions array to accommodate the manual adjustments to the number of rooms without encountering out-of-range errors? I am aware of the onChange
and onReceive
, but I was thinking that maybe the new @Observable
and @Bindable
can be an easier approach ?
Also, am I doing this the right "SwiftUI" way, or is there a better way to do it ?
Appreciate any guidance or solutions you might have!
Attached is the code:
import Foundation
import SwiftUI
import SwiftData
struct AddHotelView: View {
@Environment(\.modelContext) private var modelContext
@Environment(\.presentationMode) var presentationMode
@Query private var hotelCategories: [HotelCategoryEntity]
@State private var selectedHotelCategory: HotelCategoryEntity?
@State private var numberOfRooms: Int16 = 1 // Default value
// Input by the user, for each room
@State private var roomDescriptions: [String] = []
var body: some View {
Form {
Section(header: Text("Hotel Details")) {
// Your existing fields like reference, postal code, and GPS position...
NavigationLink(destination: SelectHotelCategory(onSelect: { selectedCategory in
self.selectedHotelCategory = selectedCategory
updateFieldsForSelectedCategory()
})) {
Text(selectedHotelCategory?.categoryName ?? "Select Hotel Category")
}
}
// Other sections like Location Details and Photo...
if let hotelCategory = selectedHotelCategory {
Section(header: Text("Hotel Customization")) {
if hotelCategory.allowCustomizingNumberOfRooms {
Stepper(value: $numberOfRooms, in: 1...10) {
Text("Number of Rooms: \(numberOfRooms)")
}
} else {
Text("Number of Rooms: \(hotelCategory.defaultNumberOfRooms)")
.background(Color.gray.opacity(0.2))
}
}
Section(header: Text("Room Descriptions")) {
ForEach(0..<Int(numberOfRooms), id: \.self) { roomIndex in
HStack {
Text("Room \(roomIndex + 1):")
.bold()
TextField("Enter description...", text: $roomDescriptions[roomIndex])
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding(.vertical, 2)
}
}
}
}
.navigationTitle("Add Hotel")
// Save action and other logic...
}
private func updateFieldsForSelectedCategory() {
guard let hotelCategory = selectedHotelCategory else { return }
numberOfRooms = hotelCategory.defaultNumberOfRooms
updateRoomDescriptions()
}
private func updateRoomDescriptions() {
self.roomDescriptions = Array(repeating: "", count: Int(self.numberOfRooms))
}
}