108 lines
3.4 KiB
Markdown
108 lines
3.4 KiB
Markdown
# General Guidelines
|
|
|
|
## Technology Stack
|
|
|
|
Technology stack is described in TECH.md file.
|
|
|
|
## Data Model
|
|
|
|
Data model is defined in MODEL.md file.
|
|
|
|
## Persistence
|
|
|
|
When implementing an edit form for a SwiftData model, ensure proper loading:
|
|
|
|
1. BEFORE PRESENTING THE FORM:
|
|
- Ensure the model is fully loaded with all its relationships
|
|
- Use a deep fetch pattern to eagerly load all relationships
|
|
- Access each property to force SwiftData to materialize lazy-loaded relationships
|
|
- Consider creating a deep copy of the model if relationships are complex
|
|
|
|
2. IN THE EDIT VIEW:
|
|
- Accept the model as a @State parameter (e.g., @State var modelObject: ModelType)
|
|
- Create separate @State variables for each editable field
|
|
- Initialize each state variable in the init() method using State(initialValue:)
|
|
- Use the modelContext for persisting changes back to the data store
|
|
|
|
3. SAVING CHANGES:
|
|
- When saving, update the model object properties from state variables
|
|
- Use modelContext.save() to persist changes
|
|
- Handle errors appropriately
|
|
|
|
|
|
## User Interface and User Experience
|
|
|
|
Each view struct should be placed in its own file under "Views" directory.
|
|
|
|
The user interface and user experience should follow Apple's Human Interface Guidelines (HIG) and best practices for iOS development.
|
|
|
|
Avoid custom UI components, instead rely on available SwiftUI views and modifiers.
|
|
|
|
When creating a add/edit functionality for a model, unless otherwise instructed use a single Add/Edit View for both add and edit functionality.
|
|
|
|
Unless otherwise instructed, use a sheet to present both add and edit views.
|
|
|
|
Whenever a list view has no entries, show a placeholder view with text "No [ListName] yet." and a button "Add [ListName]".
|
|
|
|
Before presenting an add/edit view, ensure the model is fully loaded with all its relationships. Make use of async/await mechanism to load the model. Show an overlay with a loading indicator while the model is being loaded.
|
|
|
|
## Logger
|
|
|
|
Use custom logger instead of print statements.
|
|
|
|
Make a custom logger as follows:
|
|
|
|
```swift
|
|
import OSLog
|
|
|
|
struct AppLogger {
|
|
private let logger: Logger
|
|
private let subsystem: String
|
|
private let category: String
|
|
|
|
init(subsystem: String, category: String) {
|
|
self.subsystem = subsystem
|
|
self.category = category
|
|
self.logger = Logger(subsystem: subsystem, category: category)
|
|
}
|
|
|
|
func timestamp () -> String {
|
|
Date.now.formatDateET(format: "yyyy-MM-dd HH:mm:ss")
|
|
}
|
|
|
|
func formattedMessage (_ message: String) -> String {
|
|
"\(timestamp()) [\(subsystem):\(category)] \(message)"
|
|
}
|
|
|
|
func debug(_ message: String) {
|
|
logger.debug("\(formattedMessage(message))")
|
|
}
|
|
|
|
func info(_ message: String) {
|
|
logger.info("\(formattedMessage(message))")
|
|
}
|
|
|
|
func warning(_ message: String) {
|
|
logger.warning("\(formattedMessage(message))")
|
|
}
|
|
|
|
func error(_ message: String) {
|
|
logger.error("\(formattedMessage(message))")
|
|
}
|
|
}
|
|
```
|
|
|
|
```swift
|
|
extension Date {
|
|
func formatDateET(format: String = "MMMM, d yyyy @ h:mm a z") -> String {
|
|
let formatter = DateFormatter()
|
|
formatter.timeZone = TimeZone(identifier: "America/New_York")
|
|
formatter.dateFormat = format
|
|
return formatter.string(from: self)
|
|
}
|
|
|
|
static var ISO8601: String {
|
|
"yyyy-MM-dd'T'HH:mm:ssZ"
|
|
}
|
|
}
|
|
``` |