Mallow's Blog

Property wrappers in SwiftUI 2.0

Property wrappers enables us to attach logic directly to our properties themselves  which often opens up new opportunities for code reuse and generalisation. Let’s see what are the different types of property wrappers? Which type of property wrapper should be used for different scenarios? and when those property wrappers can be used?

Different type of property wrappers:

  • @State
  • @StateObject
  • @ObservedObject
  • @EnvironmentObject

@State

@State is used inside view’s when the changes occurs in the state view will be reloaded. We need to do the below this when we are working with @State.

  1. Declare @State property as private
  2. Must store only value type
  3. Property need to be initialised when it is declared

struct CounterView:View {
@State private var counter: Int = 0

var body: some View {
VStack(spacing: 20) {
Text("\(counter)")
.font(.title)
HStack(spacing: 20) {
Button(action: {
counter += 1
}, label: {
Text("Increase")
})
Button(action: {
counter -= 1
}, label: {
Text("Decrease")
})
}
}
}
}

When to use @State property wrapper

  • When data is store and used only inside that view
  • When data is a value type

@StateObject

@StateObject is same as like @State but it will be store only the objects that confirms to ObservableObject protocol. We need to do the below this when we are working with @StateObject.

  1. Declare @State property as private
  2. Must store only reference type(class) that confirms to ObservableObject protocol
  3. Property need to be initialised when it is declared

struct CounterView: View {
@StateObject private var counter: Counter = Counter(initialValue: 0)

var body: some View {
VStack(spacing: 20) {
Text("\(counter.value)")
.font(.title)
HStack(spacing: 20) {
Button(action: {
counter.value += 1
}, label: {
Text("Increase")
})
Button(action: {
counter.value -= 1
}, label: {
Text("Decrease")
})
}
}
}
}
class Counter: ObservableObject {

@Published var value: Int
init(initialValue: Int) {
value = initialValue
}
}

When to use @StateObject property wrapper

  • When data is store and used only inside that view
  • When data is a reference type

@ObservedObject

We need use the @ObservedObject when data is not created or owned by that view.  We need to do the below this when we are working with @ObservedObject.

  1. Must store only reference type(class) that confirms to ObservableObject protocol
  2. Property need to passed from outside view

struct HomeView: View {
var body: some View {
CounterView(counter: Counter(initialValue: 0))
}
}
struct CounterView: View {
@ObservedObject var counter: Counter
var body: some View {
VStack(spacing: 20) {
Text("\(counter.value)")
.font(.title)
HStack(spacing: 20) {
Button(action: {
counter.value += 1
}, label: {
Text("Increase")
})
Button(action: {
counter.value -= 1
}, label: {
Text("Decrease")
})
}
}
}
}
class Counter: ObservableObject {

@Published var value: Int
init(initialValue: Int) {
value = initialValue
}
}

When to use @ObservedObject property wrapper

  • When we needed data from outside of the view
  • When data is a reference type

@EnvironmentObject

When same data is needed in various places in subviews in that cases @EnvironmentObject will help us. We need to do the below this when we are working with @EnvironmentObject.

  1. Must store only reference type(class) that confirms to ObservableObject protocol
  2. Need to pass the value to different subview’s

struct HomeView: View {
var body: some View {
CounterView()
.environmentObject(Counter(initialValue: 0))
}
}
struct CounterView: View {
@EnvironmentObject var counter: Counter
var body: some View {
VStack(spacing: 20) {
Text("\(counter.value)")
.font(.title)
CounterActionView()
}
}
}
struct CounterActionView: View {

@EnvironmentObject var counter: Counter
var body: some View {
HStack(spacing: 20) {
Button(action: {
counter.value += 1
}, label: {
Text("Increase")
})
Button(action: {
counter.value -= 1
}, label: {
Text("Decrease")
})
}
}
}
class Counter: ObservableObject {

@Published var value: Int
init(initialValue: Int){
value = initialValue
}
}

When to use @EnvironmentObject property wrapper

  • When data is needed in subview’s
  • When data is a reference type

Property wrappers is definitely one of the most exciting new features as it opens up a lot of doors for code reuse and customisability, and enables powerful new ways to implement property-level functionality. Even outside of declarative frameworks like SwiftUI, there’s a ton of potential use cases for property wrappers, many of which won’t require us to make any big changes to our overall code as property wrappers mostly operate completely transparently.

Kavinkumar Veerakumar,
iOS Team,
Mallow Technologies.

Leave a Reply

%d bloggers like this: