Exploring Apple WWDC 2023: Major Feature Announcements for iOS Developers

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)
}
}
}
}