Skip to content

Instantly share code, notes, and snippets.

@bocato
Last active February 15, 2022 13:02
Show Gist options
  • Save bocato/dd8929873d18bddaf3fe90acc6341ea6 to your computer and use it in GitHub Desktop.
Save bocato/dd8929873d18bddaf3fe90acc6341ea6 to your computer and use it in GitHub Desktop.
Implements @Environment, but for UIKit stuff.
// Implementation
public protocol UIKitEnvironmentKey {
associatedtype Value
static var defaultValue: Value { get }
}
public struct UIKitEnvironmentValues: CustomStringConvertible {
private class Wrapper {
init() {}
var value: Any?
}
public var description: String { .init(describing: wrapper.value) }
private var wrapper: Wrapper = .init()
public init() {} // Should not me intialized outside of an @UIKitEnvironment
public subscript<K>(key: K.Type) -> K.Value where K: UIKitEnvironmentKey {
get { wrapper.value as? K.Value ?? K.defaultValue }
set { wrapper.value = newValue }
}
mutating func update<V>(_ keypath: WritableKeyPath<UIKitEnvironmentValues, V>, _ newValue: V) { // fileprivate?
self[keyPath: keypath] = newValue
}
}
@frozen @propertyWrapper public struct UIKitEnvironment<Value> {
public var values: UIKitEnvironmentValues = .init()
public let keyPath: KeyPath<UIKitEnvironmentValues, Value>
@inlinable public init(_ keyPath: KeyPath<UIKitEnvironmentValues, Value>) {
self.keyPath = keyPath
}
@inlinable public var wrappedValue: Value { values[keyPath: keyPath] }
}
public protocol UKitEnviromentSetter {}
extension UKitEnviromentSetter {
public func uikitEnvironment<V>(_ keypath: WritableKeyPath<UIKitEnvironmentValues, V>, _ newValue: V) -> Self {
let mirror = Mirror(reflecting: self)
var uiKitEnvironment = mirror
.children
.first(where: { $0.value is UIKitEnvironment<V> })?
.value as? UIKitEnvironment<V>
uiKitEnvironment?.values.update(keypath, newValue)
return self
}
}
extension UIViewController: UKitEnviromentSetter {}
extension UIView: UKitEnviromentSetter {}
// Usage
private struct UIKitStringContainerEnvironmentKey: UIKitEnvironmentKey {
static let defaultValue: String = "defaultValue"
}
public extension UIKitEnvironmentValues {
var stringValue: String {
get { self[UIKitStringContainerEnvironmentKey.self] }
set { self[UIKitStringContainerEnvironmentKey.self] = newValue }
}
}
import UIKit
import SwiftUI
class ViewController2: UIViewController {
@UIKitEnvironment(\.stringValue) var stringValue: String
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(configuration: .gray(), primaryAction: .init(handler: { action in
print(self.stringValue)
}))
button.frame = .init(x: view.center.x, y: view.center.y, width: 50, height: 50)
self.view.addSubview(button)
self.view.backgroundColor = .blue
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(configuration: .filled(), primaryAction: .init(handler: { action in
let newController = ViewController2()
.uikitEnvironment(\.stringValue, "Not Default!")
self.present(newController, animated: true, completion: nil)
}))
button.frame = .init(x: view.center.x, y: view.center.y, width: 100, height: 100)
self.view.addSubview(button)
self.view.backgroundColor = .red
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment