30 Days of SwiftUI - Day 29: Architecting, Accessing, and Interacting: MVVM, Accessibility, Notifications, and More!
Hello Swift architects! Today, we're diving into some advanced SwiftUI topics, including MVVM architecture, accessibility, notifications, gestures, and layout. Let's get started!
Architecting for Success: Introducing MVVM into Your SwiftUI Project
MVVM (Model-View-ViewModel) is a popular architectural pattern that promotes separation of concerns, making your code more maintainable and testable.
Detailed Explanation:
- Model:
- Represents the data and business logic of your application.
- It's responsible for fetching, storing, and manipulating data.
- In SwiftUI, models are often plain Swift structs or classes conforming to
Codable
.
- View:
- Represents the user interface.
- It's responsible for displaying data and handling user interactions.
- In SwiftUI, views are structs conforming to the
View
protocol. - Views should be as "dumb" as possible, relying on the ViewModel for data and logic.
- ViewModel:
- Acts as a bridge between the Model and the View.
- It transforms data from the Model into a format suitable for the View.
- It handles user input from the View and updates the Model.
- In SwiftUI, ViewModels are often
@Observable
classes, using@Published
properties to notify the View of changes. - The ViewModel should contain all of the logic required to display and manipulate the data.
Example: MVVM with a Simple Counter
import SwiftUI
// Model
struct CounterModel {
var count = 0
}
// ViewModel
@Observable
class CounterViewModel {
private var model = CounterModel()
var count: Int {
model.count
}
func increment() {
model.count += 1
}
}
// View
struct CounterView: View {
@State private var viewModel = CounterViewModel()
var body: some View {
VStack {
Text("Count: \(viewModel.count)")
.font(.largeTitle)
Button("Increment") {
viewModel.increment()
}
}
}
}
struct CounterView_Previews: PreviewProvider {
static var previews: some View {
CounterView()
}
}
Visual Representation:
- A text view displaying the count, and a button to increment it.
Inclusive Design: Accessibility Introduction
Accessibility in SwiftUI: Making Apps for Everyone
Accessibility is about designing and developing applications that are usable by people with a wide range of abilities, including those with visual, auditory, motor, and cognitive impairments. In the context of SwiftUI, this means providing alternative ways for users to interact with and understand your app's content.
SwiftUI Code Examples:
Accessibility Labels:
- Provide descriptive labels for images, buttons, and other UI elements.
import SwiftUI
struct AccessibilityLabelExample: View {
var body: some View {
Image(systemName: "speaker.wave.3.fill")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50)
.accessibilityLabel("Volume Speaker")
Button(action: {
// Action
}) {
Text("Play Audio")
}
.accessibilityLabel("Play Audio Button")
}
}
struct AccessibilityLabelExample_Previews: PreviewProvider {
static var previews: some View {
AccessibilityLabelExample()
}
}
- In this example, the image and button have descriptive labels that VoiceOver will read aloud.
Clear Labels: Identifying Views with Useful Labels
We'll learn how to add labels to views for accessibility.
Example: Accessibility Label
import SwiftUI
struct AccessibilityLabel: View {
var body: some View {
Image(systemName: "gear")
.accessibilityLabel("Settings")
}
}
struct AccessibilityLabel_Previews: PreviewProvider {
static var previews: some View {
AccessibilityLabel()
}
}
Accessibility Control: Hiding and Grouping Accessibility Data
We'll learn how to hide and group accessibility data.
Example: Accessibility Hidden and Grouped
import SwiftUI
struct AccessibilityControl: View {
var body: some View {
VStack {
Text("Hidden Text")
.accessibilityHidden(true)
HStack {
Text("Grouped")
Text("Elements")
}
.accessibilityElement(children: .combine)
.accessibilityLabel("Grouped Elements")
}
}
}
Voice Over Data: Reading the Value of Controls
We'll learn how to provide custom values for VoiceOver.
Example: Accessibility Value
import SwiftUI
struct AccessibilityValue: View {
@State private var volume = 50.0
var body: some View {
Slider(value: $volume, in: 0...100)
.accessibilityValue("\(Int(volume)) percent")
}
}
Custom Actions: Adding Custom Row Swipe Actions to a List
We'll learn how to add custom swipe actions to list rows.
Example: List Swipe Actions
import SwiftUI
struct ListSwipeActions: View {
@State private var items = ["Item 1", "Item 2", "Item 3"]
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text(item)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
items.removeAll { $0 == item }
} label: {
Label("Delete", systemImage: "trash")
}
}
}
}
}
}
struct ListSwipeActions_Previews: PreviewProvider {
static var previews: some View {
ListSwipeActions()
}
}
Local Alerts: Scheduling Local Notifications
Local notifications allow your app to display alerts, play sounds, and badge your app's icon, even when the app is in the background or closed. This is particularly useful for reminders, alerts, and other time-sensitive information.
Steps to Schedule Local Notifications:
-
Import the UserNotifications Framework:
- This framework provides the necessary classes and methods for working with notifications.
-
Request Authorization:
- Before scheduling notifications, your app must request permission from the user.
-
Create a Notification Content Object:
- This object contains the notification's title, body, sound, and other properties.
-
Create a Notification Trigger Object:
- This object specifies when the notification should be delivered (e.g., after a certain time interval, at a specific date, or based on a location).
-
Create a Notification Request Object:
- This object combines the content and trigger objects, and it also includes a unique identifier for the notification.
-
Schedule the Notification:
- Use the
UNUserNotificationCenter
to schedule the notification request.
- Use the
SwiftUI Code Example:
import SwiftUI
import UserNotifications
struct LocalNotificationsView: View {
var body: some View {
VStack {
Button("Schedule Notification") {
scheduleNotification()
}
}
.onAppear(perform: requestAuthorization)
}
func requestAuthorization() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
print("Authorization granted!")
} else if let error = error {
print(error.localizedDescription)
}
}
}
func scheduleNotification() {
let content = UNMutableNotificationContent()
content.title = "Reminder"
content.body = "Don't forget to complete your task!"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false) // Deliver in 5 seconds
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling notification: \(error.localizedDescription)")
} else {
print("notification scheduled")
}
}
}
}
struct LocalNotificationsView_Previews: PreviewProvider {
static var previews: some View {
LocalNotificationsView()
}
}
9. Lock Screen Notifications: Posting Notifications to the Lock Screen
We'll learn how to post notifications to the lock screen.
import SwiftUI
import UserNotifications
struct LockScreenNotificationsView: View {
var body: some View {
VStack {
Button("Schedule Lock Screen Notification") {
scheduleLockScreenNotification()
}
}
.onAppear(perform: requestAuthorization)
}
func requestAuthorization() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
print("Authorization granted!")
} else if let error = error {
print(error.localizedDescription)
}
}
}
func scheduleLockScreenNotification() {
let content = UNMutableNotificationContent()
content.title = "Important Reminder"
content.body = "This notification will appear on the lock screen."
content.sound = UNNotificationSound.default
// Configure the notification to show on the lock screen
// By default, notifications will appear on the lock screen if allowed by the user.
// You can further customize this with interruption level and relevance score (iOS 15+)
if #available(iOS 15.0, *) {
content.interruptionLevel = .active // Or .passive, .timeSensitive, .critical
//content.relevanceScore = 1.0 // Higher score = more important (0.0 to 1.0)
}
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false) // Deliver in 5 seconds
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling notification: \(error.localizedDescription)")
} else {
print("Lock screen notification scheduled")
}
}
}
}
struct LockScreenNotificationsView_Previews: PreviewProvider {
static var previews: some View {
LockScreenNotificationsView()
}
}
User Interaction: How to Use Gestures in SwiftUI
We'll learn how to use gestures in SwiftUI.
Example: Tap Gesture
import SwiftUI
struct TapGestureDemo: View {
@State private var tapCount = 0
var body: some View {
Text("Tap Count: \(tapCount)")
.onTapGesture {
tapCount += 1
}
}
}
struct TapGestureDemo_Previews: PreviewProvider {
static var previews: some View {
TapGestureDemo()
}
}
Precise Placement: Absolute Positioning for SwiftUI Views
We'll learn how to use absolute positioning for views.
Example: Absolute Positioning
import SwiftUI
struct AbsolutePositioning: View {
var body: some View {
ZStack(alignment: .topLeading) {
Color.blue.frame(width: 200, height: 200)
Text("Top Left")
.position(x: 50, y: 50)
}
}
}
struct AbsolutePositioning_Previews: PreviewProvider {
static var previews: some View {
AbsolutePositioning()
}
}
🔥 Conclusion
Day 29 has been a deep dive into advanced SwiftUI topics. We've explored MVVM architecture, accessibility, notifications, gestures, and layout. These skills will empower you to build more robust and user-friendly apps. Keep experimenting and building!
Follow me on Linkedin: igatitech 🚀🚀🚀
Comments
Post a Comment