Here's the definition of EnvironmentKey:
public protocol EnvironmentKey {
associatedtype Value
static var defaultValue: Self.Value { get }
}
I'm confused as to the purpose of the defaultValue property. Or perhaps I'm confused about the intended use of #Environment. Let me explain...
Based on my testing, I know that the defaultValue is being accessed. If you, for example, define the defaultValue as a getter:
protocol MyInjector: EnvironmentKey {
static var defaultValue: Foo {
print("get default value!")
return Foo()
}
}
you'll find that SwiftUI frequently calls this property (because of this, in practice I define my defaultValue as static let).
When you add a breakpoint on the print statement and move down the stack it stops on the arrow below:
extension EnvironmentValues {
var myInjector: Foo {
get { self[MyInjector.self] } <-----------------
set { self[MyInjector.self] = newValue }
}
}
Further down in the stack is the line in my component's init where I access the following variable:
#Environment(\.myInjector) var foo: Foo
That is, it seems to access defaultValue any time a SwiftUI view with an #Environment variable is re-rendered.
I think that the internal implementation of #Environment needs to set this default value before it determines the actual value as a way to avoid making the variable declaration optional.
Finally, I also experimented with adding #Environment to an ObservableObject in the hopes that it would have access to the same "environment" as the SwiftUI view tree.
class Bar: ObservableObject {
#Environmenet(\.myInjector var foo: Foo
}
Unfortunately, that was not the case and the instance that Bar received was different than the instance I had injected via View.environment(\.myInjector, Foo()).
I also found, btw that I could use #Environment to inject a global singleton by using the shared variable pattern:
protocol MyInjector: EnvironmentKey {
static let defaultValue = Foo()
}
and the same instance was available to SwiftUI views and any other class, but I'm not sure how that could be useful in any way.
So what is the purpose of defaultValue? Is it simply, as I suspect, so that the internal implementation can assign an arbitrary value to the variable before the true value is determined, or is there something else going on?
IMO there are two reasons to have defaultValue in this pattern (and I assume this was the intention):
1) to specify corresponding value type
2) to make environment value always valid (in usage of #Environment)
In SwiftUI: Set Status Bar Color For a Specific View post you can find example of usage of this defaultValue feature to easily connect via environment different parts of application.
Related
So, basic question probably but I would like to understand it clearly.
I don't understand the dot something syntax with no prefix. For example the navigationViewStyle below, I can guess that it is a method of the NavigationView which is a struct.
We didn't declare a Navigation View Struct because it's SwiftUI and that is style in this programmatic UI?
In that case when we write dot something how does it know which object to relate to? am I referencing the struct definition or a this instance of the struct?
If I write Self. or self. none of the methods show up.
Also for the .stack, I just picked .stack. When I checked the navigationViewStyle documentation, I did not find the three options (stack, automatic, columns) which appear when coding. What is the origin of the three options? Is it inherited from a higher class or a combintation of multiple protocols? In that case the logic scales up and down, how do you plan out what to do?
My problem is that when I read an already written code it seems organic but my issue is if I want to write code from scratch then unless I memorize the pattern I have no idea how to reach the same decision on what to write.
Also is this just for SwiftUI or in Swift in general?
import SwiftUI
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
SymbolGrid()
}
.navigationViewStyle(.stack)
}
}
}
To complete #hallo answer, when you have a method that have a parameter of some type, then you can use any static var that are define in the type without needing to specify the type.
struct MyType {
var myVar: Int
static var one = MyType(myVar:1)
static var two = MyType(myVar:2)
}
// suppose you have somewhere a func defined as :
func doSomething(with aVar: MyType {
// do something
…
}
// you can call the function :
doSomethin(with: MyType.one)
// or
doSomething(with: .one)
In the second call the compiler know that the type of data must be MyType so it will deduce that .one is MyType.one, so you do not have to specify MyType. Just use « .one ». This is some syntaxic sugar from Swift.
This also work for enum where you do not have to specify the type in such case.
.navigationViewStyle() takes anything conforming to NavigationViewStyle and the available styles are also defined as static computed variables for convenience. You can also write .navigationViewStyle(StackNavigationViewStyle()) for the same result, but that seems much less readable in my opinion and therefore you just write .navigationViewStyle(.stack)
You can always just ⌘ (command) + click on anything and then "Jump to Definition" to see (or ⌃⌘ + click) to see the underlying definition
#available(iOS 13.0, tvOS 13.0, watchOS 7.0, *)
#available(macOS, unavailable)
extension NavigationViewStyle where Self == StackNavigationViewStyle {
/// A navigation view style represented by a view stack that only shows a
/// single top view at a time.
public static var stack: StackNavigationViewStyle { get }
}
As you see NavigationViewStyle.stack will return a StackNavigationViewStyle
I'm working with Property wrappers in a project that is divided into few Swift package modules dependent on each others. I have been using my own property wrappers in the code, but I want to switch to Combine, as it contains a lot of similar functionalities to my own property wrappers. In the process of converting I often occur the compiler issue:
Abort trap: 6 with error message: Global is external, but doesn't have external or weak linkage!.
As the first step I have decided to get rid of this error while still using my own property wrappers, because it can occur even there, but I managed to get rid of it. But in not a clean way, and I would like to get more knowledge on what is going on, so I could proceed with Combine later - with the same errors.
Ok, so to get rid of the Abort trap: 6 with my old property wrappers I needed to switch the way I used this property in some of modules. Instead of writing and reading it directly, I access it with $property.wrappedValue. Then it works, but this is very ugly in code, and kinda denies the puropse of using a property wrapper. Could someone explain me why this error occurs? In some of the modules Im able to use it directly with no problem. I have no idea what is going on, and what I can do to resolve this. I tried to convert a lot of similar properties with Combine, and I just get more of this errors, and actually I was not even able to resolve them like this.
Please tell me what is this error about, why it happens and what I can do to resolve it.
If his helps, this is how this property wrapper is defined:
#propertyWrapper
public class ObservableChangedValue<Value: Equatable>: Observable<Value> {
public var projectedValue: ObservableChangedValue { self }
public override var wrappedValue: Value {
get { super.wrappedValue }
set { super.wrappedValue = newValue }
}
override func shouldExecuteNotifications(oldValue: Value, newValue: Value) -> Bool {
oldValue != newValue
}
}
public class Observable<Value> {
public init(wrappedValue: Value) {
self.wrappedValue = wrappedValue
}
internal var observations = [Observation<Value>]() { didSet { isBeingObserved = !observations.isEmpty } }
public var wrappedValue: Value {
didSet {
guard shouldExecuteNotifications(oldValue: oldValue, newValue: wrappedValue) else { return }
observations.forEach {
$0.executeNotification(oldValue: oldValue, newValue: wrappedValue)
}
}
}
public weak var delegate: ObservableDelegate?
public var isBeingObserved = false {
didSet {
guard isBeingObserved != oldValue else { return }
delegate?.isBeingObservedChanged(isBeingObserved)
}
}
internal func shouldExecuteNotifications(oldValue: Value, newValue: Value) -> Bool { true }
}
One big thing - I have noticed that the error only occurs when I access this variable from other file than where it was defined. I added getters and setters for now, but still not a nice solution. This seems to be a compiler error, but Im not sure.
Using property wrappers in extensions from different file seems to be
causing this
see what they claims in documentation
Property wrapper types
A property wrapper type is a type that can be used as a property wrapper. There are two basic requirements for a property wrapper type:
The property wrapper type must be defined with the attribute
#propertyWrapper. The attribute indicates that the type is meant to
be used as a property wrapper type, and provides a point at which
the compiler can verify any other consistency rules.
The property wrapper type must have a property named wrappedValue,
whose access level is the same as that of the type itself. This is
the property used by the compiler to access the underlying value on
the wrapper instance.
I have a class with enum property state. This property's value (by value I mean ReactiveSwift.Property's value) needs to be accessed and observed by other classes, but value change should be private. Currently it is implemented in a such way:
enum State {
case stopped, running, paused
}
var state: Property<State> {
return Property(mutableState)
}
fileprivate let mutableState = MutableProperty<State>(.stopped)
This pattern allows me to modify mutableState property within class file. At the same time outside the class state is available only for reading and observing.
The question is whether there is a way to implement a similar thing using single property? Also probably someone can suggest a better pattern for the same solution?
I can't think of any way to do this with a single property. The only adjustment I would make to your code is to make state a stored property rather than a computed property that gets newly created each time it is accessed:
class Foo {
let state: Property<State>
fileprivate let mutableState = MutableProperty<State>(.stopped)
init() {
state = Property(mutableState)
}
}
Depending where you want to mutate the state, you can try doing either of:
private(set) var state: Property<State>
or if you modify it from an extension but still the same file
fileprivate(set) var state: Property<State>
class AppScene: SCNScene {
static var targetNode: SCNLookAtConstraint
override init() {
super.init()
//other code not shown...
}
}
So this is my current AppScene class and I am trying to be able to make this variable public to be able to use it in other classes. Specifically to call a button press in my SKOverlay Scene and make it do something in my SCNScene. However the error shows
'static var' declaration requires an initializer expression or getter/setter specifier
My guess is that I must have a method the return the variable that is called returnTargetNode.
Make this var an optional
static var targetNode: SCNLookAtConstraint?
Or give it a value (a default value/ a value at the init..)
While migrating some projects from Objective-C to Swift, I find myself replacing the #property/#synthesize syntax with a lot more boiler-plate code.
For example, for every #property I am implementing, I currently use this pattern:
var foo: Foo? {
get {
return _foo
}
set {
_foo = newValue
}
}
var _foo: Foo?
It ends up adding quickly. Compare this with the 2 lines in Objective-C. That doesn't feel right.
Should I just replace the code using _foo in my ported classes with foo and skip the abstraction of each public var behind an interface entirely?
As #Amit89 pointed out, you can simply use _foo.
In your example, adding foo does exactly nothing, as you simply recreated the get and set functionality already supplied with _foo.
If you want to control how _foo is set you can use property observers:
var _foo: Foo? {
didSet { }
willSet { }
}
If you want to control how _foo is retrieved, then it would indeed make sense to create a private version of _foo, and then accessing it via a computed property:
private var _foo: Foo?
var foo: Foo? {
get {
// return `_foo` in some special way
}
// add a setter if you want to set `_foo` in some special way
}