Apple's Worldwide Developers Conference (WWDC) is an eagerly anticipated annual event where the company unveils its latest software updates and development tools. In 2023, WWDC introduced several exciting features for developers, aimed at enhancing the app development experience and expanding the reach of apps across various Apple devices.
Let's dive into the major feature releases for developers announced at Apple WWDC 2023.
Swift Macro
Version 5.9 introduced the concept of macros to Swift. Macros can be categorized into multiple smaller types.
ExpressionMacro to generate expression.
AccessorMacro to add getters and setters.
ConformanceMacro makes a type conform to a protocol.
Let's take a look at a basic macro to see how they function. Macros have the advantage of being executed during compile time.
Defining the AuthorMacro
One useful macro can be created to generate the file author name.
In MyMacrosPlugin.swift:
import Foundation
import SwiftSyntax
import SwiftSyntaxMacros
public struct AuthorMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
let argument = node.argumentList.first?.expression
let segments = argument.as(StringLiteralExprSyntax.self)?.segments
return "Autor: \(segments.first.content.text)"
}
}
This code defines a Swift macro named AuthorMacro that prints the author name from the string literal passed to it.
The AuthorMacro struct implements the ExpressionMacro protocol, allowing it to expand macros involving expressions.
The expansion function takes in a macro invocation and context and performs the following checks:
It ensures that the macro is invoked with a single argument that is a static string literal.
It appends the greeting message to the string.
The function returns an expression representing the constructed greeting message.
Declare Macro in Main Project
@freestanding(expression)
public macro author(_ stringLiteral: String) -> String =
#externalMacro(module: "MyMacrosPlugin", type: "AuthorMacro")
Adding a string parameter and declaring the macro in our app target is a straightforward process. By incorporating the string parameter, we can enhance the macro's functionality and customize its behavior based on the specific needs of our application. This flexibility allows us to pass dynamic string values to the macro, enabling more versatile and adaptable macro expansions.
Calling the Macro
print(#author("Mark")) //prints "Author: Mark"
In order to use this macro simply call #author and pass the String as parameter. The macro will print the Author name.
Macros can be a powerful tool for improving the readability, performance, and functionality of your Swift code. However, it is important to use them carefully, as they can also make your code more difficult to understand and maintain.
Here are some tips for using macros:
Keep your macros short and simple.
Use descriptive names for your macros.
Document your macros thoroughly.
Test your macros thoroughly.
Use macros sparingly.
By following these tips, you can use macros to write more concise, efficient, and powerful Swift code.
SwiftData
One of the highlights of Apple WWDC 2023 was the introduction of SwiftData. This new framework enables developers to seamlessly connect their data models to the user interface in SwiftUI.
Creating a Model
To enable saving instances of a model class using SwiftData, import the framework and annotate the class with the Model macro. This macro modifies the class to conform to the PersistentModel protocol, which SwiftData utilizes to analyze the class and generate an internal schema.
By default, SwiftData includes all noncomputed properties of a class, provided they use compatible types. The framework supports primitive types like Bool, Int, and String, as well as more complex value types such as structures, enumerations, and other types that conform to the Codable protocol.
import SwiftData
// Annotate with the @Model macro.
@Model
class Task {
var name: String
var role: String
var startDate: Date
var endDate: Date
var owner: Owner?
}
Leveraging Swift's macro system, developers can enjoy a streamlined API for modeling data using the familiar Codable protocol.
Persisting a Model
To persist a model instance by SwiftData, insert the instance into the context using the insert function.
var task = Task(name: name,
role: role,
startDate: startDate,
endDate: endDate)
context.insert(task)
After performing the insert, you have two options for saving the changes. The first option is to explicitly call the save() method on the context immediately. This will persist the changes to the underlying data store.
Alternatively, you can rely on the context's implicit save behavior. Contexts automatically track changes made to their known model instances, and these changes will be included in subsequent saves without requiring explicit invocation of the save() method. The context will take care of persisting the changes to the data store as needed.
Fetching a Model
To fetch instances of a model and optionally apply search criteria and a preferred sort order in your SwiftUI view, you can use the @Query property wrapper. Additionally, by using the @Model macro, you can add Observable conformance to your model classes.
This enables SwiftUI to automatically refresh the containing view whenever changes occur to any of the fetched instances.
import SwiftUI
import SwiftData
struct ContentView: View {
@Query(sort: \.endDate, order: .reverse) var allTasks: [Task]
var body: some View {
List {
ForEach(allTasks) { task in
TaskView(for: task)
}
}
}
}
WidgetKit
This major feature release empowers developers to extend their app's content beyond the app itself. With WidgetKit, developers can create glanceable, up-to-date experiences in the form of widgets, Live Activities, and watch complications.
@main
struct WeatherStatusWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "",
provider: WeatherStatusProvider()
) { entry in
WeatherStatusView(entry.weatherStatus)
}
.configurationDisplayName("Weather Status")
.description("Shows an overview of your weather status")
.supportedFamilies([.systemSmall])
}
}
The technology and design similarities among widgets, Live Activities, and watch complications facilitate seamless feature development and usage across different contexts.
ActivityKit
ActivityKit offers developers the ability to create Live Activities that provide live updates and interactions directly from their apps. Live Activities can appear in prominent positions such as the Lock Screen, Dynamic Island, and as banners on the Home Screen. Users can view real-time information, launch the app, and perform specific functionalities through buttons and toggles, without fully opening the app.
import SwiftUI
import WidgetKit
@main
struct FoodOrderActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FoodOrderAttributes.self) { context in
} dynamicIsland: { context in
}
}
}
By leveraging SwiftUI and WidgetKit, developers can share code between widgets and Live Activities, making it easier to build engaging experiences.
Observable
The Observable protocol simplifies the implementation of data change notifications. By attaching the Observable macro to custom types, developers indicate conformance to the Observable protocol. This protocol enables types to emit notifications to observers whenever the underlying data changes.
@Observable final class Animal {
var name: String = ""
var sleeping: Bool = false
init(name: String, sleeping: Bool = false) {
self.name = name
self.sleeping = sleeping
}
}
To enable change tracking, use the withObservationTracking(_:onChange:) function. In the provided code example, this function is used to call the onChange closure when the name property of a car changes. However, it does not trigger the closure when the sleeping flag of the car changes. This behavior occurs because the function only tracks properties that are read within its apply closure, and in this case, the sleeping property is not read within that closure.
func render() {
withObservationTracking {
for animal in animals { //apply closure
print(animal.name)
}
} onChange: { //onChange closure
print("Call UI updation.")
}
}
The Observable protocol provides a convenient way to handle data updates and build reactive interfaces, enhancing the overall user experience of the app.
WorkoutKit
This powerful framework offers models and utilities for creating and previewing workout compositions in iOS and watchOS apps. Developers can design various types of workouts, including CustomWorkoutComposition, GoalWorkoutComposition, and others catering to different fitness activities. The framework provides methods for validating, exporting, and previewing workouts, allowing users to save compositions to the Workout app. Furthermore,
WorkoutKit enables developers to create and manage workout schedules, sync scheduled compositions to Apple Watch, and query completed workouts.
PayLaterView
Showcasing Apple Pay Later Feature Apple Pay Later, a new financial service, received special attention at WWDC 2023. To enhance its visibility, Apple introduced the PayLaterView, a dedicated view for displaying the Apple Pay Later visual merchandising widget.
VisionOS
One of the key features of VisionOS is the ability to create multiple windows within the app. These windows, built using SwiftUI, provide familiar views and controls while enabling developers to add depth by incorporating stunning 3D content. With VisionOS, it is possible to further enhance the app's depth by incorporating 3D volumes.
These volumes, powered by RealityKit or Unity, allows to showcase captivating 3D content that can be viewed from any angle within the Shared Space or an app's Full Space. The flexibility of volumes helps to craft engaging experiences that captivate and delight app users.
By default, apps in VisionOS launch into the Shared Space, where they coexist side-by-side, akin to multiple apps on a Mac desktop. Utilizing windows and volumes, apps can display their content within this shared environment, giving users the ability to freely reposition and interact with these elements. For a truly immersive experience, apps can open a dedicated Full Space, where only their content is visible. Within a Full Space, apps can leverage windows and volumes, create unbounded 3D content, open portals to different worlds, or provide users with a fully immersive environment.
Conclusion
Apple WWDC 2023 brought significant enhancements for developers, offering tools and frameworks to streamline data modeling, extend app content through widgets and Live Activities, simplify data change notifications, optimize workout compositions, and showcase new financial features.
These advancements empower developers to create more immersive and feature-rich applications across Apple's ecosystem of devices.
Comments