1111
1212_ Going fully declarative_ : SwiftUI Rules.
1313
14+ SwiftUI Rules is covered in the companion AlwaysRightInstitute
15+ blog post:
16+ [ Dynamic Environments ¶ SwiftUI Rules] ( http://www.alwaysrightinstitute.com/swiftuirules/ ) .
17+
1418## Requirements
1519
1620SwiftUI Rules requires an environment capable to run SwiftUI.
@@ -30,6 +34,214 @@ The package URL is:
3034[ https://github.com/AlwaysRightInstitute/SwiftUIRules.git
3135] ( https://github.com/AlwaysRightInstitute/SwiftUIRules.git ) .
3236
37+ ## Using SwiftUI Rules
38+
39+ SwiftUI Rules is covered in the companion AlwaysRightInstitute
40+ blog post:
41+ [ Dynamic Environments ¶ SwiftUI Rules] ( http://www.alwaysrightinstitute.com/swiftuirules/ ) .
42+
43+ ### Declaring Own Environment Keys
44+
45+ Let's say we want to add an own
46+ environment key called ` fancyColor ` .
47+
48+ First thing we need is an
49+ [ ` DynamicEnvironmentKey ` ] ( https://github.com/AlwaysRightInstitute/SwiftUIRules/blob/develop/Sources/SwiftUIRules/DynamicEnvironment/DynamicEnvironmentKey.swift#L17 )
50+ declaration:
51+ ``` swift
52+ struct FancyColorEnvironmentKey : DynamicEnvironmentKey {
53+ public static let defaultValue = Color.black
54+ }
55+ ```
56+ Most importantly this specifies the static Swift type of the environment key
57+ (` Color ` )
58+ and it provides a default value.
59+ That value is used when the environment key is queried,
60+ but no value has been explicitly set by the user.
61+
62+ Second we need to declare a property on the
63+ [ DynamicEnvironmentPathes] ( https://github.com/AlwaysRightInstitute/SwiftUIRules/blob/develop/Sources/SwiftUIRules/DynamicEnvironment/DynamicEnvironmentPathes.swift#L19 )
64+ struct:
65+ ``` swift
66+ extension DynamicEnvironmentPathes {
67+ var fancyColor : Color {
68+ set { self [dynamic : FancyColorEnvironmentKey.self ] = newValue }
69+ get { self [dynamic : FancyColorEnvironmentKey.self ] }
70+ }
71+ }
72+ ```
73+ That's it. We can start using our new key.
74+
75+ Some View accessing our splendid new ` fancyColor `
76+ using the
77+ [ @Environment ] ( https://developer.apple.com/documentation/swiftui/environment )
78+ property wrapper:
79+ ``` swift
80+ struct FancyText : View {
81+
82+ @Environment (\.fancyColor ) private var color
83+
84+ var label : String
85+
86+ var body: some View {
87+ Text (label)
88+ .foregroundColor (color) // boooring
89+ }
90+ }
91+ ```
92+ and a View providing it:
93+
94+ ``` swift
95+ struct MyPage : View {
96+
97+ var body: some View {
98+ VStack {
99+ Text (" Hello" )
100+ FancyText (" World" )
101+ }
102+ .environment (\.fancyColor , .red )
103+ }
104+ }
105+ ```
106+
107+ ### Setting Up a Ruling Environment
108+
109+ We recommend creating a ` RuleModel.swift ` Swift file. Put all your
110+ rules in that central location:
111+ ``` swift
112+ // RuleModel.swift
113+ import SwiftUIRules
114+
115+ let ruleModel : RuleModel = [
116+ \.priority == .low => \.fancyColor <= .gray ,
117+ \.priority == .high => \.fancyColor <= .red ,
118+ \.priority == .normal => \.fancyColor <= .black
119+ ]
120+ ```
121+
122+ You can hookup the rule system at any place in the SwiftUI View hierarchy,
123+ but we again recommend to do that at the very top.
124+ For example in a fresh application generated in Xcode, you could modify
125+ the generated ` ContentView ` like so:
126+ ``` swift
127+ struct ContentView : View {
128+ private let ruleContext = RuleContext (ruleModel : ruleModel)
129+
130+ var body: some View {
131+ Group {
132+ // your views
133+ }
134+ .environment (\.ruleContext , ruleContext)
135+ }
136+ }
137+ ```
138+
139+ Quite often some “root” properties need to be injected:
140+ ``` swift
141+ struct TodoList : View {
142+ let todos: [ Todo ]
143+
144+ var body: someView {
145+ VStack {
146+ Text (" Todos:" )
147+ ForEach (todos) { todo in
148+ TodoView ()
149+ // make todo available to the rule system
150+ .environment (\.todo , todo)
151+ }
152+ }
153+ }
154+ }
155+ ```
156+ ` TodoView ` and child views of that can now derive environment values of
157+ the ` todo ` key using the rule system.
158+
159+ ### Use Cases
160+
161+ Ha! Endless 🤓 It is quite different to “Think In Rules”™
162+ (a.k.a. declaratively),
163+ but they allow you to compose your application in a highly decoupled
164+ and actually “declarative” ways.
165+
166+ It can be used low level, kinda like CSS.
167+ Consider dynamic environment keys a little like CSS classes.
168+ E.g. you could switch settings based on the platform:
169+ ``` swift
170+ [
171+ \.platform == " watch" => \.details <= " minimal" ,
172+ \.platform == " phone" => \.details <= " regular" ,
173+ \.platform == " mac" || \.platform == " pad"
174+ => \.details <= " high"
175+ ]
176+ ```
177+
178+ But it can also be used at a very high level,
179+ for example in a workflow system:
180+ ``` swift
181+ [
182+ \.task .status == " done" => \.view <= TaskFinishedView (),
183+ \.task .status == " done" => \.actions <= [],
184+ \.task .status == " created" => \.view <= NewTaskView (),
185+ \.task .status == " created" => \.actions = [ .accept , .reject ]
186+ ]
187+
188+ struct TaskView : View {
189+ @Environment (\.view ) var body // body derived from rules
190+ }
191+ ```
192+
193+ Since SwiftUI Views are also just lightweight structs,
194+ you can build dynamic properties which carry them!
195+
196+ In any case: We are interested in any idea how to use it!
197+
198+
199+ ### Limitations
200+
201+ #### Only ` DynamicEnvironmentKey ` s
202+
203+ Currently rules can only evaluate ` DynamicEnvironmentKey ` s,
204+ it doesn't take regular environment keys into account.
205+ That is, you can't drive for example the builtin SwiftUI ` lineLimit `
206+ using the rulesystem.
207+ ``` swift
208+ [
209+ \.user .status == " VIP" => \.lineLimit <= 10 ,
210+ \.lineLimit <= 2
211+ ]
212+ ```
213+ ** Does not work** . This is currently made explicit by requiring keys which
214+ are used w/ the system to have the ` DynamicEnvironmentKey ` type.
215+ SO you can't accidentially run into this.
216+
217+ We may open it up to any ` EnvironmentKey ` , TBD.
218+
219+ #### No KeyPath'es in Assignments
220+
221+ Sometimes one might want this:
222+ ``` swift
223+ \.todos .count > 10 => \.person .status <= " VIP"
224+ ```
225+ I.e. assign a value to a multi component keypath (` \.person.status ` ).
226+ That ** does not work** .
227+
228+ #### SwiftUI Bugs
229+
230+ Sometimes SwiftUI “looses” its environment during navigation or in List's.
231+ watchOS and macOS seem to be particularily problematic, iOS less so.
232+ If that happens, one can pass on the ` ruleContext ` manually:
233+ ``` swift
234+ struct MyNavLink <Destination , Content >: View {
235+ @Environment (\.ruleContext ) var ruleContext
236+ ...
237+ var body: someView {
238+ NavLink (destination : destination
239+ // Explicitly pass along:
240+ .environment (\.ruleContext , ruleContext))
241+ ...
242+ }
243+ ```
244+
33245
34246## Who
35247
0 commit comments