@iOS17; #SwiftMacro; Still Repeating Yourself?
Introducing what SwiftMacro is and what it does, straight from WWDC2023!
WWDC2023... Aside from Apple's Vision Pro, I started watching the conference without high expectations...
⁉️😮 Contrary to my expectations, WWDC2023 announced a lot of incredibly interesting things related to iOS and Swift. Out of those, I'd like to briefly introduce some of the most useful ways to use SwiftMacro!
(Specifically, the handy ones from Expression Macros and Attached Macros!)
First of all, the word "Macro" is quite familiar to us. If we take a quick look at the definition...
We can summarize it as something that allows you to perform repetitive tasks faster!
As iOS engineers writing Swift code, we encounter a lot of boilerplate. (We hate repetition, but you can't escape syntax 🥲)
Using SwiftMacro, what kind of repetitive tasks can we make easier?
@Observable
When writing UI code with SwiftUI, if we implement Combine's ObservableObject, we end up writing code like this:
class Object: ObservableObject {
@Published var title: String
@Published var subtitle: String
@Published var count: Int
...
}Implementing the ObservableObject protocol and painstakingly attaching @Published to every single property is tedious and repetitive...
What if we use @Observable instead?
@Observable
class Object {
var title: String
var subtitle: String
var count: Int
...
}I hear it supports both ValueType and ReferenceType for property types!
@CaseDetection
To check the case type of an Enum, we probably write code like the following quite often... right? (Well, I haven't, but I figure Apple had a reason for making it!)
enum Path {
case relative(String)
case absolute(String)
}
let absPaths = paths.filter { $0.isAbsolute }
extension Path {
var isAbsolute: Bool {
if case .absolute = self { true }
else { false }
}
}
extension Path {
var isRelative: Bool {
if case relative = self { true }
else { false }
}The downside is that every time a single case is added to the Enum type above, source code like isAbsolute has to keep being added.
What if we use @CaseDetection?
@CaseDetection
enum Path {
case relative(String)
case absolute(String)
// 아래는 매크로에 의해 자동 생생된 코드(옵션을 통해 볼 수 있음)
var isAbsolute: Bool {
if case .absolute = self { true }
else { false }
}
var isRelative: Bool {
if case . relative = self { true }
else { false }
}
}By using macros, we no longer need to go through the hassle of writing new source code every time a case is added!
#Preview
Starting with Xcode 15, you'll notice that when you create a SwiftUI file, a file using #Preview is automatically generated by default!
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// Xcode15 이상에서 작성되는 SwiftUI Preview 코드
#Preview {
ContentView()
}#Preview isn't just for SwiftUI; it supports UIKit as well! You can use it as long as your minimum deployment target is iOS 13 or higher.
Expand Macro
When using SwiftMacro, the automatically generated code is hidden by default! To see it, you can use the option shown below.
So far, we've looked at how to use @Observable, @CaseDetection, #Preview, and more.
There are many other macros besides these!
Those used in SwiftData include:
@Model
@Attribute(.unique)
@Transient
#Predicate
Plus, custom macros are totally possible too!
Except for #Preview, all of them are only available on iOS 17 and above 😭
I expect many more macros will be added in the future 😁
For more details, please check out the swift-evolution documents below!
I've heard that as Apple added the macro feature, they also worked on a lot of optimizations, so performance has improved significantly.
For company projects, we'll probably have to wait about 2 to 3 years to actually use macros.
Until the day we can use them, hang in there... everyone!
Author: Saeroi — iOS Engineer DelightRoom