(_ key: K.Type) -> Bool
16 |
17 | // hacky, to support adding generic rules into the OID cache of the model
18 | var candidateKeyType: ObjectIdentifier { get }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Assignments/RuleKeyAssignment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleKeyAssignment.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 23.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | /**
10 | * RuleKeyAssignment
11 | *
12 | * This RuleAction object evaluates the action value as a lookup against the
13 | * _context_. Which then can trigger recursive rule evaluation (if the queried
14 | * key is itself a rule based value).
15 | *
16 | * In a model it looks like:
17 | * user.role = 'Manager' => bannerColor = defaultColor
18 | *
19 | * The bannerColor = defaultColor
represents the RuleKeyAssignment.
20 | * When executed, it will query the RuleContext for the 'defaultColor' and
21 | * will return that in fireInContext().
22 | *
23 | * Note that this object does *not* perform a
24 | * takeValueForKey(value, 'bannerColor'). It simply returns the value in
25 | * fireInContext() for further processing at upper layers.
26 | *
27 | * @see RuleAction
28 | * @see RuleAssignment
29 | */
30 | public struct RuleKeyAssignment
32 | : RuleCandidate, RuleAction
33 | {
34 | // FIXME: drop this one, not that useful anymore
35 |
36 | public let key : K.Type
37 | public let value : Value.Type
38 |
39 | public init(_ key: K.Type, _ value: Value.Type) {
40 | assert(key != value, "assignment recursion!")
41 | // there seems to be no "where K != Value" in Swift generics?
42 | self.key = key
43 | self.value = value
44 | }
45 |
46 | public var candidateKeyType: ObjectIdentifier {
47 | return ObjectIdentifier(key)
48 | }
49 | public func isCandidateForKey(_ key: K.Type)
50 | -> Bool
51 | {
52 | return self.key == key
53 | }
54 |
55 | public func fireInContext(_ context: RuleContext) -> Any? {
56 | return context[value]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Assignments/RuleTypeIDAssignment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleTypeIDAssignment.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | /**
10 | * RuleTypeIDAssignment
11 | *
12 | * This class just returns its value if its fired.
13 | *
14 | * In a model it looks like:
15 | *
16 | * \user.role = 'Manager' => \.bannerColor = "red"
17 | *
18 | */
19 | public struct RuleTypeIDAssignment: RuleCandidate, RuleAction {
20 |
21 | public let typeID : ObjectIdentifier
22 | public let constant : Value
23 | #if DEBUG
24 | let debugInfo : String
25 | #endif
26 |
27 | public init(_ typeID: ObjectIdentifier, _ constant: Value,
28 | debugInfo: String = "")
29 | {
30 | self.typeID = typeID
31 | self.constant = constant
32 | #if DEBUG
33 | self.debugInfo = debugInfo
34 | #endif
35 | }
36 |
37 | public var candidateKeyType: ObjectIdentifier {
38 | return typeID
39 | }
40 |
41 | public func isCandidateForKey(_ key: K.Type)
42 | -> Bool
43 | {
44 | return self.typeID == ObjectIdentifier(key)
45 | }
46 |
47 | public func fireInContext(_ context: RuleContext) -> Any? {
48 | return constant
49 | }
50 | }
51 |
52 | extension RuleTypeIDAssignment: CustomStringConvertible {
53 |
54 | public var description: String {
55 | #if DEBUG
56 | return ""
57 | #else
58 | return ""
59 | #endif
60 | }
61 | }
62 |
63 | public extension RuleTypeIDAssignment {
64 |
65 | init(_ key: K.Type, _ constant: K.Value)
66 | where Value == K.Value
67 | {
68 | self.init(ObjectIdentifier(key), constant)
69 | }
70 |
71 | /**
72 | * Careful, this only works properly for single-key keypathes.
73 | *
74 | * E.g. don't: \.person.name = "Hello"
75 | */
76 | init(_ keyPath: KeyPath, _ constant: Value) {
77 | // FIXME: This is not quite what we want yet. The user could "write" to
78 | // an arbitrary keypath, e.g. \.person.name = "Donald".
79 | // I guess in theory it is possible to make that work if OIDs
80 | // of the keypathes are interned?
81 | // Then we could 'assign' them just like any other dynamic envkey?
82 | // Note sure yet.
83 | let typeID = RuleContext.typeIDForKeyPath(keyPath)
84 | #if DEBUG
85 | let debugInfo =
86 | "type=\(typeID.short) keyPath=\(keyPath) constant=\(constant)"
87 | #else
88 | let debugInfo = ""
89 | #endif
90 | self.init(typeID, constant, debugInfo: debugInfo)
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Assignments/RuleTypeIDPathAssignment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleTypeIDPathAssignment.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | /**
10 | * RuleTypeIDPathAssignment
11 | *
12 | * This RuleAction object evaluates the action value as a lookup against the
13 | * _context_. Which then can trigger recursive rule evaluation (if the queried
14 | * key is itself a rule based value).
15 | *
16 | * In a model it looks like:
17 | *
18 | * \user.role == "Manager" => \.bannerColor <= \.defaultColor
19 | *
20 | * The `bannerColor <= defaultColor` represents the RuleKeyAssignment.
21 | * When executed, it will query the RuleContext for the 'defaultColor' and
22 | * will return that in fireInContext().
23 | *
24 | * Note that this object does *not* assign to the \.bannerColor value.
25 | * It simply returns the value in fireInContext() for further processing at
26 | * upper layers.
27 | *
28 | * @see RuleAction
29 | * @see RuleAssignment
30 | */
31 | public struct RuleTypeIDPathAssignment: RuleCandidate, RuleAction {
32 |
33 | public let typeID : ObjectIdentifier
34 | public let keyPath : Swift.KeyPath
35 | #if DEBUG
36 | let debugInfo : String
37 | #endif
38 |
39 | public init(_ typeID: ObjectIdentifier,
40 | _ keyPath: Swift.KeyPath,
41 | debugInfo: String = "")
42 | {
43 | self.typeID = typeID
44 | self.keyPath = keyPath
45 | #if DEBUG
46 | self.debugInfo = debugInfo
47 | #endif
48 | }
49 |
50 | public var candidateKeyType: ObjectIdentifier {
51 | return typeID
52 | }
53 |
54 | public func isCandidateForKey(_ key: K.Type)
55 | -> Bool
56 | {
57 | return self.typeID == ObjectIdentifier(key)
58 | }
59 |
60 | public func fireInContext(_ context: RuleContext) -> Any? {
61 | return context[keyPath: keyPath]
62 | }
63 | }
64 |
65 | extension RuleTypeIDPathAssignment: CustomStringConvertible {
66 |
67 | public var description: String {
68 | #if DEBUG
69 | return ""
70 | #else
71 | return ""
72 | #endif
73 | }
74 | }
75 |
76 | public extension RuleTypeIDPathAssignment {
77 |
78 | /**
79 | * Careful, this only works properly for single-key keypathes.
80 | *
81 | * E.g. don't: \.person.name = "Hello"
82 | */
83 | init(_ keyPath : Swift.KeyPath,
84 | _ valuePath : Swift.KeyPath)
85 | {
86 | // FIXME: This is not quite what we want yet. The user could "write" to
87 | // an arbitrary keypath, e.g. \.person.name = "Donald".
88 | // I guess in theory it is possible to make that work if OIDs
89 | // of the keypathes are interned?
90 | // Then we could 'assign' them just like any other dynamic envkey?
91 | // Note sure yet.
92 | let typeID = RuleContext.typeIDForKeyPath(keyPath)
93 | #if DEBUG
94 | let debugInfo =
95 | "type=\(typeID.short) keyPath=\(keyPath) valuePath=\(valuePath)"
96 | #else
97 | let debugInfo = ""
98 | #endif
99 | self.init(typeID, valuePath, debugInfo: debugInfo)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Assignments/RuleValueAssignment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleValueAssignment.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 23.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | public struct RuleValueAssignment
10 | : RuleCandidate, RuleAction
11 | {
12 | // FIXME: drop this one, not that useful anymore
13 |
14 | public let key : K.Type
15 | public let constant : K.Value
16 |
17 | public init(_ key: K.Type, _ constant: K.Value) {
18 | self.key = key
19 | self.constant = constant
20 | }
21 |
22 | public var candidateKeyType: ObjectIdentifier {
23 | return ObjectIdentifier(key)
24 | }
25 |
26 | public func isCandidateForKey(_ key: K.Type)
27 | -> Bool
28 | {
29 | return self.key == key
30 | }
31 |
32 | public func fireInContext(_ context: RuleContext) -> Any? {
33 | return constant
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/DynamicEnvironment/DynamicEnvironmentKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DynamicEnvironmentKey.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 20.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | import protocol SwiftUI.EnvironmentKey
10 |
11 | /**
12 | * Environment keys which are dynamically evaluated against the RuleContext.
13 | */
14 | public protocol DynamicEnvironmentKey: EnvironmentKey {}
15 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/DynamicEnvironment/DynamicEnvironmentPathes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DynamicEnvironmentPathes.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | import struct SwiftUI.EnvironmentValues
10 |
11 | /**
12 | * Use to share the environment keypathes between
13 | * `EnvironmentValues` and the `RuleContext`.
14 | * Both can then be accessed using `\.entity` like keypathes.
15 | *
16 | * DynamicEnvironmentPathes is only used to share the implementation,
17 | * it is not used as an own type.
18 | */
19 | public protocol DynamicEnvironmentPathes {
20 |
21 | subscript(dynamic key: K.Type) -> K.Value {
22 | set get
23 | }
24 |
25 | }
26 |
27 | extension EnvironmentValues: DynamicEnvironmentPathes {
28 |
29 | public subscript(dynamic key: K.Type) -> K.Value {
30 | set {
31 | // Hm, we really want to write to the environment values? But we can't,
32 | // because we can't check whether the environment contains a value
33 | // w/o fatal-erroring? :-)
34 | // So we need to sideline our own storage?
35 | ruleContext[key] = newValue
36 | }
37 | get { return ruleContext[key] }
38 | }
39 | }
40 |
41 | extension RuleContext: DynamicEnvironmentPathes {
42 |
43 | public subscript(dynamic key: K.Type) -> K.Value {
44 | set { self[key] = newValue }
45 | get { return self[key] }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/DynamicEnvironment/DynamicEnvironmentValues.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DynamicEnvironmentValues.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 28.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | /**
10 | * A DynamicEnvironmentValues can yield values for `DynamicEnvironmentKeys`.
11 | * It is a protocol similar to the `EnvironmentValues` struct in SwiftUI.
12 | *
13 | * User code should not directly use the resolver and store functions, but
14 | * rather rely on the subscript and `optional` functions.
15 | */
16 | public protocol DynamicEnvironmentValues {
17 |
18 | func resolveValueForTypeID(_ typeID: ObjectIdentifier) -> Any?
19 |
20 | mutating func storeValue(_ value: V, forTypeID typeID: ObjectIdentifier)
21 |
22 | func defaultValue(for key: K.Type) -> K.Value
23 | }
24 |
25 | public extension DynamicEnvironmentValues { // lookup using type
26 |
27 | /**
28 | * Returns a value for the `DynamicEnvironmentKey`, or the defaultValue of
29 | * the key if the `resolveValueForTypeID` returns no value.
30 | */
31 | subscript(key: K.Type) -> K.Value {
32 | // Note a subscript w/o a name can be used on types w/o adding `.self`!!
33 | set { storeValue(newValue, forTypeID: ObjectIdentifier(key)) }
34 | get { return optional(key) ?? defaultValue(for: key) }
35 | }
36 |
37 | /**
38 | * Returns a value for the `DynamicEnvironmentKey` or `nil` in case the
39 | * `resolveValueForTypeID` returned no value (it could not be dynamically
40 | * generated).
41 | * Hence the result is optional.
42 | *
43 | * Note: Avoid having to check for optionality in user code. Rather use the
44 | * subscript, which falls back to the defaultValue of the environment
45 | * key.
46 | */
47 | func optional(_ key: K.Type) -> K.Value? {
48 | let typeID = ObjectIdentifier(key)
49 |
50 | if debugPrints {
51 | print("optional lookup key:", key, "typeID:", typeID.short)
52 | }
53 |
54 | if let v = resolveValueForTypeID(typeID) {
55 | guard let typed = v as? K.Value else {
56 | print( // TBD: no generic logger? Want to avoid ZeeQL here
57 | "Could not map rule result to expected value:\n",
58 | " value: ", v, "\n",
59 | " expected:", K.Value.self
60 | )
61 | return nil
62 | }
63 | return typed // TBD: cache?
64 | }
65 |
66 | return nil
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/DynamicEnvironment/README.md:
--------------------------------------------------------------------------------
1 | SwiftUI Dynamic Environment Keys
2 |
4 |
5 |
6 | "Environment Keys" are keys which you can use like so in SwiftUI:
7 |
8 | ```swift
9 | public struct MyView: View {
10 |
11 | @Environment(\.database) private var database // retrieve a key
12 |
13 | var body: some View {
14 | BlaBlub()
15 | .environment(\.database, database) // set a key
16 | }
17 | }
18 | ```
19 |
20 | They are scoped along the view hierarchy.
21 |
22 | `DynamicEnvironmentKeys` are the similar, but can resolve to different values on
23 | demand.
24 |
25 | For example a rule system could evaluate them like so:
26 |
27 | day < 28 => color = green
28 | day >= 28 => color = red
29 | *true* => color = white
30 |
31 | But they don't have the be backed by a rule system.
32 | `DynamicEnvironmentKey` and `DynamicEnvironmentValues`
33 | just form an API to back such keys.
34 |
35 |
36 | ### Implementation
37 |
38 | While the `DynamicEnvironmentKey` forms a statically typed interface,
39 | the actual dynamic workings are defined in terms of the `ObjectIdentifier`s
40 | of those types.
41 | For various reasons :-)
42 |
43 |
44 | ### Allow arbitrary Environment Keys
45 |
46 | It might make sense to allow lookup of any environment key, not just dynamic ones.
47 | TBD.
48 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Operators/AssignmentOperators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssignmentOperators.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 01.09.19.
6 | //
7 |
8 | import protocol SwiftUI.View
9 | import struct SwiftUI.AnyView
10 |
11 | // \.title <= "hello"
12 | public func <= (lhs: Swift.KeyPath, rhs: Value)
13 | -> RuleTypeIDAssignment
14 | {
15 | RuleTypeIDAssignment(lhs, rhs)
16 | }
17 |
18 | // \.title <= "hello"
19 | public func <= (lhs: Swift.KeyPath,
20 | rhs: Swift.KeyPath)
21 | -> RuleTypeIDPathAssignment
22 | {
23 | RuleTypeIDPathAssignment(lhs, rhs)
24 | }
25 |
26 | // \.view <= MyView()
27 | public func <= (lhs: Swift.KeyPath, rhs: V)
28 | -> RuleTypeIDAssignment
29 | {
30 | RuleTypeIDAssignment(lhs, AnyView(rhs))
31 | }
32 | public func <= (lhs: Swift.KeyPath, rhs: AnyView)
33 | -> RuleTypeIDAssignment
34 | {
35 | RuleTypeIDAssignment(lhs, rhs)
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Operators/CompoundPredicateOperators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CompoundPredicateOperators.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 01.09.19.
6 | //
7 |
8 | // e.g. !predicate
9 | public prefix func !(_ base: P) -> RuleNotPredicate {
10 | RuleNotPredicate(predicate: base)
11 | }
12 |
13 | // e.g. \.state == done && \.color == yellow
14 | public
15 | func &&(lhs: LHS, rhs: RHS) -> RuleAndPredicate2
16 | where LHS: RulePredicate, RHS: RulePredicate
17 | {
18 | RuleAndPredicate2(lhs, rhs)
19 | }
20 | // e.g. \.state == done || \.color == yellow
21 | public
22 | func ||(lhs: LHS, rhs: RHS) -> RuleOrPredicate2
23 | where LHS: RulePredicate, RHS: RulePredicate
24 | {
25 | RuleOrPredicate2(lhs, rhs)
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Operators/KeyPathPredicateOperators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PredicateOperators.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 01.09.19.
6 | //
7 |
8 | // MARK: - KeyValue
9 |
10 | // e.g. \.person.name == "Donald"
11 | public
12 | func ==(lhs: Swift.KeyPath, rhs: Value)
13 | -> some RulePredicate where Value : Equatable
14 | {
15 | RuleKeyPathPredicate(keyPath: lhs, value: rhs)
16 | }
17 |
18 | // e.g. \.person.name != "Donald"
19 | public
20 | func !=(lhs: Swift.KeyPath, rhs: Value)
21 | -> some RulePredicate where Value : Equatable
22 | {
23 | RuleNotPredicate(predicate:
24 | RuleKeyPathPredicate(keyPath: lhs, value: rhs))
25 | }
26 |
27 | // e.g. \.person.age < 45
28 | public
29 | func < (lhs: Swift.KeyPath, rhs: Value)
30 | -> some RulePredicate where Value : Comparable
31 | {
32 | RuleKeyPathPredicate(keyPath: lhs, operation: .lessThan, value: rhs)
33 | }
34 | public
35 | func <= (lhs: Swift.KeyPath, rhs: Value)
36 | -> some RulePredicate where Value : Comparable
37 | {
38 | RuleKeyPathPredicate(keyPath: lhs, operation: .lessThanOrEqual,
39 | value: rhs)
40 | }
41 |
42 | // e.g. \.person.age > 45
43 | public
44 | func > (lhs: Swift.KeyPath, rhs: Value)
45 | -> some RulePredicate where Value : Comparable
46 | {
47 | RuleKeyPathPredicate(keyPath: lhs, operation: .greaterThan, value: rhs)
48 | }
49 | public
50 | func >= (lhs: Swift.KeyPath, rhs: Value)
51 | -> some RulePredicate where Value : Comparable
52 | {
53 | RuleKeyPathPredicate(keyPath: lhs, operation: .greaterThanOrEqual,
54 | value: rhs)
55 | }
56 |
57 | // e.g. \.person === manager
58 | public func ===(lhs: Swift.KeyPath, rhs: Value)
59 | -> some RulePredicate where Value : AnyObject
60 | {
61 | RuleKeyPathPredicate() { ruleContext in
62 | ruleContext[keyPath: lhs] === rhs
63 | }
64 | }
65 | // e.g. \.person !== manager
66 | public func !==(lhs: Swift.KeyPath, rhs: Value)
67 | -> some RulePredicate where Value : AnyObject
68 | {
69 | RuleKeyPathPredicate() { ruleContext in
70 | ruleContext[keyPath: lhs] !== rhs
71 | }
72 | }
73 |
74 |
75 | // MARK: - KeyKey
76 |
77 | // e.g. \.person.name == \.manager.name
78 | public
79 | func ==(lhs: Swift.KeyPath,
80 | rhs: Swift.KeyPath)
81 | -> some RulePredicate where Value : Equatable
82 | {
83 | RuleKeyPathPredicate(lhs, rhs)
84 | }
85 |
86 | // e.g. \.person.name != \.manager.name
87 | public
88 | func !=(lhs: Swift.KeyPath,
89 | rhs: Swift.KeyPath)
90 | -> some RulePredicate where Value : Equatable
91 | {
92 | RuleNotPredicate(predicate: RuleKeyPathPredicate(lhs, rhs))
93 | }
94 |
95 | // e.g. \.person.age < \.manager.age
96 | public
97 | func < (lhs: Swift.KeyPath,
98 | rhs: Swift.KeyPath)
99 | -> some RulePredicate where Value : Comparable
100 | {
101 | RuleKeyPathPredicate(lhs, operation: .lessThan, rhs)
102 | }
103 | // e.g. \.person.age <= \.manager.age
104 | public
105 | func <= (lhs: Swift.KeyPath,
106 | rhs: Swift.KeyPath)
107 | -> some RulePredicate where Value : Comparable
108 | {
109 | RuleKeyPathPredicate(lhs, operation: .lessThanOrEqual, rhs)
110 | }
111 |
112 | // e.g. \.person.age > \.manager.age
113 | public
114 | func > (lhs: Swift.KeyPath,
115 | rhs: Swift.KeyPath)
116 | -> some RulePredicate where Value : Comparable
117 | {
118 | RuleKeyPathPredicate(lhs, operation: .greaterThan, rhs)
119 | }
120 | // e.g. \.person.age >= \.manager.age
121 | public
122 | func >= (lhs: Swift.KeyPath,
123 | rhs: Swift.KeyPath)
124 | -> some RulePredicate where Value : Comparable
125 | {
126 | RuleKeyPathPredicate(lhs, operation: .greaterThanOrEqual, rhs)
127 | }
128 |
129 | // e.g. \.person === manager
130 | public func ===(lhs: Swift.KeyPath,
131 | rhs: Swift.KeyPath)
132 | -> some RulePredicate where Value : AnyObject
133 | {
134 | RuleKeyPathPredicate() { ruleContext in
135 | ruleContext[keyPath: lhs] === ruleContext[keyPath: rhs]
136 | }
137 | }
138 | // e.g. \.person !== manager
139 | public func !==(lhs: Swift.KeyPath,
140 | rhs: Swift.KeyPath)
141 | -> some RulePredicate where Value : AnyObject
142 | {
143 | RuleKeyPathPredicate() { ruleContext in
144 | ruleContext[keyPath: lhs] !== ruleContext[keyPath: rhs]
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Operators/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUI Rules Operators
2 |
3 | Not a huge fan of operator overloading, but well :-)
4 |
5 | A set of operators to build rule predicates, assignments and rules.
6 |
7 | Rule Predicate samples:
8 | ```swift
9 | \.count < 10
10 | \.person.name == "Duck"
11 | ```
12 |
13 | Rule Assignment samples:
14 | ```swift
15 | \.title <= "1337"
16 | \.title <= \.defaultTitle
17 | ```
18 |
19 | Rule samples:
20 | ```swift
21 | \.count > 5 => \.color <= .red
22 | \.count > 5 && \.person.name == "Duck" => \.color <= \.defaultColor
23 | ```
24 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Operators/RuleOperators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleOperators.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | infix operator => : AssignmentPrecedence
10 |
11 | /**
12 | * Operator to combine a predicate with an action to form the final rule.
13 | *
14 | * Example:
15 | *
16 | * \.person.name != "Donald" => \.title <= "hello"
17 | *
18 | */
19 | public func =>(lhs: RulePredicate, rhs: RuleCandidate & RuleAction) -> Rule {
20 | Rule(when: lhs, do: rhs)
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Predicates/README.md:
--------------------------------------------------------------------------------
1 | SwiftUI Rule Predicates
2 |
4 |
5 |
6 | The predicate defines whether a rule is active for a given context. For
7 | example if a rule has such a qualifier:
8 |
9 | ```swift
10 | \.user.role == "Manager"
11 | ```
12 |
13 | It will only be considered for evaluation if the current user has a Manager
14 | role.
15 |
16 | The key method which all rule predicates implement is:
17 |
18 | ```swift
19 | func evaluate(in ruleContext: RuleContext) -> Bool
20 | ```
21 |
22 | It just returns true or false depending on whether the predicate matches for the
23 | given `ruleContext`.
24 |
25 | ### Predicate Complexity
26 |
27 | Another `RulePredicate` property is `rulePredicateComplexity`, which defaults to 1.
28 |
29 | The complexity is used to sort predicates based on how many components
30 | or relevance a predicate has. It is used to disambiguate in situations
31 | like:
32 |
33 | ```swift
34 | \.person.name == "Duck" => \.title <= "Donald"
35 | \.task == "show" && \.person.name == "Persons" => \.title <= "Brummel"
36 | ```
37 |
38 | In this case the second rule is checked first, because the predicate
39 | has more components (and hence assumed significance).
40 |
41 | ### KeyPath Predicate
42 |
43 | This is the most common predicate, it takes a keypath and a matching value:
44 |
45 | ```swift
46 | \.person.name == "Duck"
47 | ```
48 |
49 | When it is evaluated, it resolves the keypath against the ruleContext and returns
50 | whether the value matches the constant passed into the predicate.
51 |
52 | ### Compound Predicates
53 |
54 | The usual AND, OR and NOT predicates. Can be constructed using the
55 | `&&`, `||` and `!` operators.
56 |
57 | ```swift
58 | \.person.name == "Duck" && !(\.person.category == "VIP")
59 | ```
60 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Predicates/RuleBoolPredicate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleBoolPredicate.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | public struct RuleBoolPredicate: RulePredicate, Equatable {
10 |
11 | public static let yes = RuleBoolPredicate(value: true)
12 | public static let no = RuleBoolPredicate(value: false)
13 |
14 | private let value : Bool
15 |
16 | public func evaluate(in ruleContext: RuleContext) -> Bool {
17 | return value
18 | }
19 |
20 | public var rulePredicateComplexity: Int {
21 | // This means that a boolean predicate has a lower predicate than
22 | // any other predicate, even simple ones.
23 | return 0
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Predicates/RuleClosurePredicate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleClosurePredicate.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | public struct RuleClosurePredicate: RulePredicate {
10 |
11 | private let predicate : ( RuleContext ) -> Bool
12 |
13 | public init(predicate: @escaping ( RuleContext ) -> Bool) {
14 | self.predicate = predicate
15 | }
16 |
17 | public func evaluate(in ruleContext: RuleContext) -> Bool {
18 | return predicate(ruleContext)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Predicates/RuleCompoundPredicate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleCompoundPredicate.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | // Provide a default set of predicates.
10 |
11 | public protocol RuleCompoundPredicate: RulePredicate {
12 |
13 | var predicates : [ RulePredicate ] { get }
14 | }
15 |
16 | public extension RuleCompoundPredicate {
17 | var rulePredicateComplexity: Int {
18 | predicates.reduce(0) { $0 + $1.rulePredicateComplexity }
19 | }
20 | }
21 |
22 | public struct RuleAndPredicate2: RuleCompoundPredicate
23 | where P1: RulePredicate, P2: RulePredicate
24 | {
25 | public let p1: P1
26 | public let p2: P2
27 |
28 | public var predicates : [ RulePredicate ] { [ p1, p2 ] }
29 |
30 | public init(_ p1: P1, _ p2: P2) {
31 | self.p1 = p1
32 | self.p2 = p2
33 | }
34 | public var rulePredicateComplexity: Int {
35 | p1.rulePredicateComplexity + p2.rulePredicateComplexity
36 | }
37 |
38 | public func evaluate(in ruleContext: RuleContext) -> Bool {
39 | return p1.evaluate(in: ruleContext) && p2.evaluate(in: ruleContext)
40 | }
41 |
42 | }
43 | public struct RuleOrPredicate2: RuleCompoundPredicate
44 | where P1: RulePredicate, P2: RulePredicate
45 | {
46 | public let p1: P1
47 | public let p2: P2
48 |
49 | public var predicates : [ RulePredicate ] { [ p1, p2 ] }
50 |
51 | public init(_ p1: P1, _ p2: P2) {
52 | self.p1 = p1
53 | self.p2 = p2
54 | }
55 | public var rulePredicateComplexity: Int {
56 | p1.rulePredicateComplexity + p2.rulePredicateComplexity
57 | }
58 |
59 | public func evaluate(in ruleContext: RuleContext) -> Bool {
60 | return p1.evaluate(in: ruleContext) || p2.evaluate(in: ruleContext)
61 | }
62 |
63 | }
64 |
65 | public struct RuleAndPredicate: RuleCompoundPredicate {
66 |
67 | public let predicates : [ RulePredicate ]
68 |
69 | public init(predicates: [ RulePredicate ]) {
70 | self.predicates = predicates
71 | }
72 |
73 | public func evaluate(in ruleContext: RuleContext) -> Bool {
74 | for predicate in predicates {
75 | if !predicate.evaluate(in: ruleContext) { return false }
76 | }
77 | return true
78 | }
79 |
80 | }
81 | public struct RuleOrPredicate: RuleCompoundPredicate {
82 |
83 | public let predicates : [ RulePredicate ]
84 |
85 | public init(predicates: [ RulePredicate ]) {
86 | self.predicates = predicates
87 | }
88 |
89 | public func evaluate(in ruleContext: RuleContext) -> Bool {
90 | for predicate in predicates {
91 | if predicate.evaluate(in: ruleContext) { return true }
92 | }
93 | return false
94 | }
95 | }
96 |
97 | public struct RuleNotPredicate: RulePredicate {
98 |
99 | public let predicate : P
100 |
101 | public init(predicate: P) {
102 | self.predicate = predicate
103 | }
104 |
105 | public func evaluate(in ruleContext: RuleContext) -> Bool {
106 | return !predicate.evaluate(in: ruleContext)
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Predicates/RuleKeyPathPredicate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleKeyPathPredicate.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | public struct RuleKeyPathPredicate: RulePredicate {
10 | // This is like a ClosurePredicate, but it preserves the static type.
11 | // Which has to be passed in explicitly! Can't add further constraints to
12 | // the init, like init() where Value: Equatable
13 |
14 | private let predicate : ( RuleContext ) -> Bool
15 | #if DEBUG
16 | private let debugInfo : String
17 | #endif
18 |
19 | public init(debugInfo: String = "",
20 | predicate: @escaping ( RuleContext ) -> Bool)
21 | {
22 | #if DEBUG
23 | self.debugInfo = debugInfo
24 | #endif
25 | self.predicate = predicate
26 | }
27 |
28 | public func evaluate(in ruleContext: RuleContext) -> Bool {
29 | return predicate(ruleContext)
30 | }
31 | }
32 |
33 | extension RuleKeyPathPredicate: CustomStringConvertible {
34 |
35 | public var description: String {
36 | #if DEBUG
37 | return ""
38 | #else
39 | return ""
40 | #endif
41 | }
42 | }
43 |
44 | public enum RuleComparisonOperation {
45 |
46 | case equal
47 | case notEqual
48 | case lessThan
49 | case greaterThan
50 | case lessThanOrEqual
51 | case greaterThanOrEqual
52 |
53 | public func compare(_ lhs: V, _ rhs: V) -> Bool {
54 | switch self {
55 | case .equal: return lhs == rhs
56 | case .notEqual: return lhs != rhs
57 | case .lessThan: return lhs < rhs
58 | case .greaterThan: return lhs > rhs
59 | case .lessThanOrEqual: return lhs <= rhs
60 | case .greaterThanOrEqual: return lhs >= rhs
61 | }
62 | }
63 | }
64 |
65 |
66 | public extension RuleKeyPathPredicate {
67 |
68 | init(keyPath: Swift.KeyPath,
69 | value: Value) where Value: Equatable
70 | {
71 | #if DEBUG
72 | let debugInfo = "keyPath=\(keyPath) value=\(value)"
73 | #else
74 | let debugInfo = ""
75 | #endif
76 |
77 | self.init(debugInfo: debugInfo) { ruleContext in
78 | let other = ruleContext[keyPath: keyPath]
79 | return value == other
80 | }
81 | }
82 |
83 | init(keyPath: Swift.KeyPath,
84 | operation: RuleComparisonOperation = .equal,
85 | value: Value) where Value: Comparable
86 | {
87 | self.init() { ruleContext in
88 | return operation.compare(ruleContext[keyPath: keyPath], value)
89 | }
90 | }
91 | }
92 |
93 | public extension RuleKeyPathPredicate {
94 | init(_ lhs: Swift.KeyPath,
95 | _ rhs: Swift.KeyPath) where Value: Equatable
96 | {
97 | #if DEBUG
98 | let debugInfo = "lhs=\(lhs) rhs=\(rhs)"
99 | #else
100 | let debugInfo = ""
101 | #endif
102 |
103 | self.init(debugInfo: debugInfo) { ruleContext in
104 | let lhsValue = ruleContext[keyPath: lhs]
105 | let rhsValue = ruleContext[keyPath: rhs]
106 | return lhsValue == rhsValue
107 | }
108 | }
109 |
110 | init(_ lhs: Swift.KeyPath,
111 | operation: RuleComparisonOperation = .equal,
112 | _ rhs: Swift.KeyPath) where Value: Comparable
113 | {
114 | self.init() { ruleContext in
115 | let lhsValue = ruleContext[keyPath: lhs]
116 | let rhsValue = ruleContext[keyPath: rhs]
117 | return operation.compare(lhsValue, rhsValue)
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Predicates/RulePredicate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RulePredicate.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 28.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | public protocol RulePredicate {
10 |
11 | /**
12 | * Returns true if a predicate matches in the rule context.
13 | *
14 | * For example:
15 | *
16 | * entity.name = 'Persons'
17 | *
18 | */
19 | func evaluate(in ruleContext: RuleContext) -> Bool
20 |
21 | /**
22 | * The complexity is used to sort predicates based on how many components
23 | * or relevance a predicate has. It is used to disambiguate in situations
24 | * like:
25 | *
26 | * \.person.name == "Duck" => \.title <= "Donald"
27 | * \.task == "show" && \.person.name == "Persons" => \.title <= "Brummel"
28 | *
29 | * In this case the second rule is checked first, because the predicate
30 | * has more components (and hence assumed significance).
31 | */
32 | var rulePredicateComplexity : Int { get }
33 |
34 | }
35 |
36 | public extension RulePredicate {
37 | var rulePredicateComplexity : Int {
38 | return 1
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Rule.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Rule.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 23.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | /**
10 | * A Rule consists of three major components:
11 | * - a predicate, also known as the "left hand side" or lhs
12 | * - an action, also known as the "right hand side" or rhs
13 | * - a priority
14 | *
15 | * The predicate defines whether a rule is active for a given context. For
16 | * example if a rule has such a qualifier:
17 | *
18 | * \.user.role == "Manager"
19 | *
20 | * It will only be considered for evaluation if the current user has a Manager
21 | * role.
22 | *
23 | * The RuleAction defines what gets executed when a rule was selected for
24 | * evaluation. If the rule was the best condidate for a given query, the
25 | * framework will call the `fireInContext()` method on the action.
26 | * Most often the RuleAction will be an object of the RuleAssignment class,
27 | * sometimes a RuleKeyAssignment.
28 | * Sample:
29 | *
30 | * \.user.role == "Manager" => bannerColor <= "red"
31 | *
32 | * Note that the name 'Action' does not imply that the object somehow modifies
33 | * state in the context, it usually does not have any sideeffects. Instead the
34 | * action just *returns* the value for a requested key by performing some
35 | * evaluation/calculation.
36 | *
37 | * Finally the 'priority' is used to select a rule when there are multiple
38 | * matching rules. In models you usually use constants like 'fallback' or
39 | * 'high'.
40 | *
41 | * (\.firstPageName <= "Main' ).priority(.fallback)
42 | * (\.firstPageName <= "MyMain").priority(.default)
43 | *
44 | * The 'fallback' priority is most often in frameworks to specify
45 | * defaults for keys which can then be overridden in user provided models.
46 | */
47 | public final class Rule {
48 | // Note: immutable class because it is passed around a bit (too lazy for 🐄)
49 |
50 | public struct Priority: Comparable, ExpressibleByIntegerLiteral {
51 |
52 | public let rawValue : Int16
53 | @inlinable public init(_ rawValue: Int16) { self.rawValue = rawValue }
54 | @inlinable public init(integerLiteral value: Int16) { self.init(value) }
55 |
56 | public static let important = Priority(1000)
57 | public static let veryHigh = Priority(200)
58 | public static let high = Priority(150)
59 | public static let normal = Priority(100)
60 | public static let low = Priority(50)
61 | public static let veryLow = Priority(5)
62 | public static let fallback = Priority(0)
63 |
64 | @inlinable
65 | public static func < (lhs: Priority, rhs: Priority) -> Bool {
66 | return lhs.rawValue < rhs.rawValue
67 | }
68 | }
69 |
70 | public let predicate : RulePredicate
71 | public let action : RuleCandidate & RuleAction
72 | public let priority : Priority
73 |
74 | public init(when predicate : RulePredicate,
75 | do action : RuleCandidate & RuleAction,
76 | at priority : Priority = .normal)
77 | {
78 | self.predicate = predicate
79 | self.action = action
80 | self.priority = priority
81 | }
82 |
83 | /**
84 | * Return a new rule at a different priority.
85 | *
86 | * (\.isNew == true => \.title <= "Hello").priority(.high)
87 | */
88 | public func priority(_ priority: Priority) -> Rule {
89 | return Rule(when: predicate, do: action, at: priority)
90 | }
91 |
92 | func hasHigherPriority(than other: Rule) -> Bool {
93 | if self === other { return false }
94 |
95 | if priority != other.priority { return priority > other.priority }
96 | return predicate.rulePredicateComplexity
97 | > other.predicate.rulePredicateComplexity
98 | }
99 | }
100 |
101 | extension Rule: RuleCandidate {
102 |
103 | public var candidateKeyType: ObjectIdentifier {
104 | return action.candidateKeyType
105 | }
106 | public func isCandidateForKey(_ key: K.Type) -> Bool {
107 | return action.isCandidateForKey(key)
108 | }
109 |
110 | }
111 |
112 | extension Rule: RuleAction {
113 |
114 | public func fireInContext(_ context: RuleContext) -> Any? {
115 | return action.fireInContext(context)
116 | }
117 |
118 | }
119 |
120 | extension Rule: CustomStringConvertible {
121 |
122 | public var description: String {
123 | var ms = ""
134 | return ms
135 | }
136 | }
137 |
138 | extension Rule.Priority {
139 |
140 | public init?(_ string: String) {
141 | if string.isEmpty { self = .normal; return }
142 | if let i = Int16(string) { self.rawValue = i; return }
143 |
144 | switch string.lowercased() {
145 | case "important": self = .important
146 | case "very high": self = .veryHigh
147 | case "high": self = .high
148 | case "normal": self = .normal
149 | case "default": self = .normal
150 | case "low": self = .low
151 | case "very low": self = .veryLow
152 | case "fallback": self = .fallback
153 | default:
154 | assertionFailure("unexpected priority: \(string)")
155 | return nil
156 | }
157 | }
158 | }
159 |
160 | public extension RuleAction where Self : RuleCandidate {
161 | /**
162 | * Return a new rule wrapping the action at the specified priority:
163 | *
164 | * (\.title <= "Hello").priority(.high)
165 | */
166 | func priority(_ priority: Rule.Priority) -> Rule {
167 | return Rule(when: RuleBoolPredicate.yes, do: self, at: priority)
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/RuleContext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleContext.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 19.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | /**
10 | * The `RuleContext` holds the state of the rule system for a given environment.
11 | *
12 | * It is itself an environment key and can be accessed using
13 | *
14 | * @Environment(\.ruleContext) var ruleContext
15 | *
16 | * Keys in the context can be accessed as regular properties, for example:
17 | *
18 | * ruleContext.color
19 | *
20 | * Or as usual as regular environment keys:
21 | *
22 | * @Environment(\.color) var color
23 | *
24 | * The context has a state dictionary mapping `DynamicEnvironmentKeys` to their
25 | * values. Which means you can explicitly fill in values:
26 | *
27 | * var body: some View {
28 | * Text()
29 | * .environment(\.color, .red)
30 | * }
31 | *
32 | * If a RuleContext has state for a key, it always has preference over the
33 | * rules.
34 | * Its true power stems however from the fact that keys can be looked up using
35 | * the associated `RuleModel`:
36 | *
37 | * ruleModel
38 | * .add(\todo.status == "pending" => \.color <= .yellow)
39 | * .add(\todo.status == "overdue" => \.color <= .red)
40 | * .add(\todo.status == "done" => \.color <= .gray)
41 | *
42 | * That is transparent to the View, it can still access the dynamic key using:
43 | *
44 | * @Environment(\.color) var color
45 | *
46 | */
47 | public struct RuleContext: DynamicEnvironmentValues {
48 |
49 | public let ruleModel : RuleModel
50 |
51 | private var state = [ ObjectIdentifier : Any ]()
52 | // We'd like to avoid this and just use `EnvironmentValues`, but we
53 | // can't. Because `EnvironmentValues` has nothing to check for the
54 | // existance of a key.
55 |
56 | public init(ruleModel: RuleModel) {
57 | self.ruleModel = ruleModel
58 | }
59 |
60 | @inline(__always)
61 | private func clearCache() {} // TBD
62 |
63 | // lookup using typeID
64 |
65 | public func resolveValueForTypeID(_ typeID: ObjectIdentifier) -> Any? {
66 | if crazyTypeQueryHack(typeID) { return nil }
67 |
68 | if let value = state[typeID] {
69 | if debugPrints { print("Resolved from state:", typeID.short, "=>", value) }
70 | return value
71 | }
72 | if let value = ruleModel.resolveValueForTypeID(typeID, in: self) {
73 | if debugPrints {
74 | print("Resolved using model:", typeID.short, "=>", value)
75 | }
76 | // The problem w/ caching is that our structs should stay immutable.
77 | // We could maybe use an object for the cache and be careful w/ CoW.
78 | return value // TBD: cache?
79 | }
80 | return nil
81 | }
82 |
83 | public mutating func storeValue(_ value: V,
84 | forTypeID typeID: ObjectIdentifier)
85 | {
86 | if crazyTypeQueryHack(typeID) { return }
87 |
88 | clearCache()
89 | if debugPrints { print("Push to state:", typeID.short, "=", value) }
90 | state[typeID] = value
91 | }
92 |
93 | public func defaultValue(for key: K.Type) -> K.Value
94 | {
95 | return K.defaultValue
96 | }
97 | }
98 |
99 | extension RuleContext: CustomStringConvertible {
100 |
101 | public var description: String {
102 | var ms = ""
106 | }
107 | else {
108 | ms += " state:\n"
109 | for ( typeID, value ) in state {
110 | ms += " " + typeID.short + "=\(value)"
111 | }
112 | ms += ">"
113 | }
114 | return ms
115 | }
116 |
117 | }
118 |
119 | import protocol SwiftUI.EnvironmentKey
120 | import struct SwiftUI.EnvironmentValues
121 |
122 | /**
123 | * Access the active rule context in the environment.
124 | *
125 | * To access the RuleContext from within your Views, use:
126 | *
127 | * struct MyView: View {
128 | * @Environment(\.ruleContext) var ruleContext
129 | * }
130 | */
131 | public struct RuleContextKey: EnvironmentKey {
132 | public static let defaultValue = RuleContext(ruleModel: RuleModel())
133 | }
134 |
135 | public extension EnvironmentValues {
136 |
137 | /**
138 | * Access the active rule context in the environment.
139 | */
140 | var ruleContext : RuleContext {
141 | set { self[RuleContextKey.self] = newValue }
142 | get { self[RuleContextKey.self] }
143 | }
144 |
145 | }
146 | public extension RuleContext {
147 | var ruleContext : RuleContext { return self }
148 | }
149 |
150 |
151 | // MARK: - KeyPath Type Reflection
152 |
153 | fileprivate var queryTypeMode = false
154 | fileprivate var lastTypeQuery : ObjectIdentifier?
155 |
156 | public extension RuleContext {
157 |
158 | @inline(__always)
159 | fileprivate func crazyTypeQueryHack(_ typeID: ObjectIdentifier) -> Bool {
160 | guard queryTypeMode else { return false }
161 | if debugPrints { print(" crazyTypeQueryHack:", typeID.short) }
162 | assert(lastTypeQuery == nil)
163 | lastTypeQuery = typeID
164 | return true
165 | }
166 |
167 | static
168 | func typeIDForKeyPath(_ keyPath: Swift.KeyPath)
169 | -> ObjectIdentifier
170 | {
171 | // What a dirty hack :->
172 | if debugPrints { print("TypeQuery lookup:", keyPath, Value.self) }
173 |
174 | // sigh, it can be recursive
175 | let storeQueryTypeMode = queryTypeMode
176 | let storeQueryType = lastTypeQuery
177 | queryTypeMode = true
178 | lastTypeQuery = nil
179 | defer {
180 | queryTypeMode = storeQueryTypeMode
181 | lastTypeQuery = storeQueryType
182 | }
183 |
184 | let tempContext = RuleContext(ruleModel: RuleModel(nil))
185 | _ = tempContext[keyPath: keyPath]
186 |
187 | guard let typeID = lastTypeQuery else {
188 | fatalError("could not reflect type of keypath: \(keyPath)")
189 | }
190 |
191 | if debugPrints { print("=>", typeID.short) }
192 |
193 | return typeID
194 | }
195 |
196 | }
197 |
198 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/RuleModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleModel.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 19.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | /**
10 | * A rule model is just an ordered and keyed set of `Rule` values.
11 | *
12 | * To add rules the SwiftUIRule operators are usually used, for example:
13 | *
14 | * ruleModel
15 | * .add(\todo.status == "pending" => \.color <= .yellow)
16 | * .add(\todo.status == "overdue" => \.color <= .red)
17 | * .add(\todo.status == "done" => \.color <= .gray)
18 | *
19 | * You can also use to setup the rules:
20 | *
21 | * RuleModel(
22 | * \todo.status == "pending" => \.color <= .yellow,
23 | * \todo.status == "overdue" => \.color <= .red
24 | * )
25 | *
26 | * Or use the literal convertible:
27 | *
28 | * let ruleModel : RuleModel = [
29 | * \todo.status == "pending" => \.color <= .yellow,
30 | * \todo.status == "overdue" => \.color <= .red
31 | * ]
32 | *
33 | * Though rules can also be constructed manually and then added using `addRule`.
34 | */
35 | public final class RuleModel {
36 |
37 | let fallbackModel: RuleModel? // kinda the model group?
38 |
39 | public init(_ fallbackModel: RuleModel? = nil, _ rules: RuleLiteral...) {
40 | self.fallbackModel = fallbackModel
41 | rules.forEach { $0.addToRuleModel(self) }
42 | if !rules.isEmpty { sortRules() }
43 | }
44 |
45 | /**
46 | * Add a fallback model to the RuleModel. The rules in the fallback model
47 | * will be used when none of the own rules matches (but before resorting
48 | * to defaults!)
49 | */
50 | public func fallback(_ fallbackModel: RuleModel) -> RuleModel {
51 | let newModel : RuleModel
52 | if let oldFallback = self.fallbackModel { // nest fallbacks
53 | newModel = RuleModel(fallbackModel.fallback(oldFallback))
54 | }
55 | else {
56 | newModel = RuleModel(fallbackModel)
57 | }
58 | newModel.oidToRules = self.oidToRules
59 | newModel.sortRequired = self.sortRequired
60 | return newModel
61 | }
62 |
63 | private var oidToRules = [ ObjectIdentifier : [ Rule ] ]()
64 |
65 | private var sortRequired = false
66 |
67 | private func sortRules() {
68 | for ( typeID, rules ) in oidToRules {
69 | if rules.isEmpty { oidToRules.removeValue(forKey: typeID); continue }
70 | oidToRules[typeID] = rules.sorted { $0.hasHigherPriority(than: $1) }
71 | }
72 | sortRequired = false
73 | }
74 |
75 | @discardableResult
76 | public func addRule(_ rule: Rule) -> Self {
77 | let typeID = rule.candidateKeyType
78 | if oidToRules[typeID] == nil {
79 | oidToRules[typeID] = [ rule ]
80 | return self
81 | }
82 | oidToRules[typeID]!.append(rule)
83 | sortRequired = true
84 | return self
85 | }
86 |
87 | func resolveValueForTypeID(_ typeID: ObjectIdentifier,
88 | in context: RuleContext) -> Any?
89 | {
90 | if sortRequired { sortRules() }
91 |
92 | // lookup candidates
93 | let rules = oidToRules[typeID] ?? []
94 |
95 | if debugPrints { print("RULES: lookup:", typeID.short) }
96 |
97 | for rule in rules {
98 | if rule.predicate.evaluate(in: context) {
99 | if debugPrints { print("RULES: match:", rule) }
100 | return rule.fireInContext(context)
101 | }
102 | else {
103 | if debugPrints { print("RULES: no-match:", rule) }
104 | }
105 | }
106 |
107 | if let fallbackModel = fallbackModel {
108 | if debugPrints { print("RULES: test fallback:", typeID.short) }
109 | return fallbackModel.resolveValueForTypeID(typeID, in: context)
110 | }
111 | if debugPrints { print("RULES: non-matched:", typeID.short) }
112 | return nil
113 | }
114 |
115 | }
116 |
117 | extension RuleModel: CustomStringConvertible {
118 |
119 | public var description: String {
120 | if oidToRules.isEmpty { return "" }
121 | var ms = ""
131 | return ms
132 | }
133 | }
134 |
135 |
136 | #if false // Doesn't work w/ candidateKeyType infrastructure
137 | // MARK: - Using RuleModels as Rules
138 |
139 | extension RuleModel: RuleCandidate {
140 | // Note: Does not work yet.
141 |
142 | public var candidateKeyType: ObjectIdentifier {
143 | return // would need to fix this.
144 | }
145 |
146 | public func isCandidateForKey(_ key: K.Type)
147 | -> Bool
148 | {
149 | oidToRules[ObjectIdentifier(key)] != nil
150 | }
151 | }
152 | #endif
153 |
154 |
155 | // MARK: - Convenience adders
156 |
157 | public extension RuleModel {
158 |
159 | @discardableResult
160 | func add(_ rule: Rule) -> Self {
161 | return addRule(rule)
162 | }
163 |
164 | @discardableResult
165 | func add(_ action: RuleCandidate & RuleAction) -> Self {
166 | return addRule(Rule(when: RuleBoolPredicate.yes, do: action))
167 | }
168 |
169 | @discardableResult
170 | func add(_ predicate: @escaping ( RuleContext ) -> Bool,
171 | action: RuleCandidate & RuleAction) -> Self
172 | {
173 | return addRule(Rule(when: RuleClosurePredicate(predicate: predicate),
174 | do: action))
175 | }
176 | }
177 |
178 |
179 | // MARK: - Literals
180 |
181 | /**
182 | * Literal convertible:
183 | *
184 | * let ruleModel : RuleModel = [
185 | * \todo.status == "pending" => \.color <= .yellow,
186 | * \todo.status == "overdue" => \.color <= .red
187 | * ]
188 | *
189 | */
190 | extension RuleModel : ExpressibleByArrayLiteral {
191 | public typealias ArrayLiteralElement = RuleLiteral
192 |
193 | public convenience init(arrayLiteral rules: RuleLiteral...) {
194 | self.init(nil)
195 | rules.forEach { $0.addToRuleModel(self) }
196 | sortRules()
197 | }
198 | }
199 | public protocol RuleLiteral {
200 | func addToRuleModel(_ model: RuleModel)
201 | }
202 | extension Rule: RuleLiteral {
203 | public func addToRuleModel(_ model: RuleModel) {
204 | model.addRule(self)
205 | }
206 | }
207 | extension RuleAction where Self: RuleCandidate {
208 | public func addToRuleModel(_ model: RuleModel) {
209 | model.addRule(Rule(when: RuleBoolPredicate.yes, do: self))
210 | }
211 | }
212 | extension RuleTypeIDAssignment : RuleLiteral {}
213 | extension RuleTypeIDPathAssignment : RuleLiteral {}
214 |
215 | extension RuleModel: RuleLiteral {
216 | public func addToRuleModel(_ model: RuleModel) {
217 | for rules in oidToRules.values {
218 | rules.forEach { model.addRule($0) }
219 | }
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/Sources/SwiftUIRules/Support/RuleDebug.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuleDebug.swift
3 | // SwiftUIRules
4 | //
5 | // Created by Helge Heß on 29.08.19.
6 | // Copyright © 2019 ZeeZide GmbH. All rights reserved.
7 | //
8 |
9 | import class Foundation.ProcessInfo
10 |
11 | internal let debugPrints : Bool = {
12 | guard let v = ProcessInfo.processInfo.environment["RULES_LOG"]?.lowercased()
13 | else { return false }
14 | return v.contains("debug")
15 | }()
16 |
17 | extension ObjectIdentifier {
18 |
19 | var short: String {
20 | let s = String(describing: self)
21 | let match = "ObjectIdentifier(0x0000000"
22 | if s.hasPrefix(match) {
23 | return "0x" + String(s.dropFirst(match.count).dropLast())
24 | }
25 | return s
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/SwiftUIRules.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXAggregateTarget section */
10 | E8A02CFA2319495800C1D879 /* SwiftUIRules-All */ = {
11 | isa = PBXAggregateTarget;
12 | buildConfigurationList = E8A02CFB2319495800C1D879 /* Build configuration list for PBXAggregateTarget "SwiftUIRules-All" */;
13 | buildPhases = (
14 | );
15 | dependencies = (
16 | E8A02CFF2319496100C1D879 /* PBXTargetDependency */,
17 | E8A02D012319496100C1D879 /* PBXTargetDependency */,
18 | E8A02D032319496100C1D879 /* PBXTargetDependency */,
19 | );
20 | name = "SwiftUIRules-All";
21 | productName = "SwiftUIRules-All";
22 | };
23 | /* End PBXAggregateTarget section */
24 |
25 | /* Begin PBXBuildFile section */
26 | E84699C823194CA800794065 /* libSwiftUIRules-Mobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A02CDD231948FE00C1D879 /* libSwiftUIRules-Mobile.a */; };
27 | E84699CF23194DC300794065 /* TestEnvironmentKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84699CE23194DC300794065 /* TestEnvironmentKeys.swift */; };
28 | E87A57592319537E004EFF40 /* SwiftUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87A57582319537D004EFF40 /* SwiftUITests.swift */; };
29 | E8A02CC5231948FE00C1D879 /* RuleKeyPathPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D3231933EF0055A9F9 /* RuleKeyPathPredicate.swift */; };
30 | E8A02CC6231948FE00C1D879 /* RuleContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DE231933EF0055A9F9 /* RuleContext.swift */; };
31 | E8A02CC7231948FE00C1D879 /* DynamicEnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E6231933EF0055A9F9 /* DynamicEnvironmentValues.swift */; };
32 | E8A02CC8231948FE00C1D879 /* RuleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D5231933EF0055A9F9 /* RuleModel.swift */; };
33 | E8A02CC9231948FE00C1D879 /* RuleDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E0231933EF0055A9F9 /* RuleDebug.swift */; };
34 | E8A02CCA231948FE00C1D879 /* DynamicEnvironmentKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E3231933EF0055A9F9 /* DynamicEnvironmentKey.swift */; };
35 | E8A02CCB231948FE00C1D879 /* RuleCompoundPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D1231933EF0055A9F9 /* RuleCompoundPredicate.swift */; };
36 | E8A02CCC231948FE00C1D879 /* RuleTypeIDPathAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D9231933EF0055A9F9 /* RuleTypeIDPathAssignment.swift */; };
37 | E8A02CCD231948FE00C1D879 /* RuleValueAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D7231933EF0055A9F9 /* RuleValueAssignment.swift */; };
38 | E8A02CCE231948FE00C1D879 /* RuleClosurePredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D0231933EF0055A9F9 /* RuleClosurePredicate.swift */; };
39 | E8A02CCF231948FE00C1D879 /* RuleTypeIDAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D8231933EF0055A9F9 /* RuleTypeIDAssignment.swift */; };
40 | E8A02CD0231948FE00C1D879 /* RuleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DA231933EF0055A9F9 /* RuleCandidate.swift */; };
41 | E8A02CD2231948FE00C1D879 /* RuleAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DD231933EF0055A9F9 /* RuleAction.swift */; };
42 | E8A02CD3231948FE00C1D879 /* RulePredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D2231933EF0055A9F9 /* RulePredicate.swift */; };
43 | E8A02CD4231948FE00C1D879 /* DynamicEnvironmentPathes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E4231933EF0055A9F9 /* DynamicEnvironmentPathes.swift */; };
44 | E8A02CD5231948FE00C1D879 /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E1231933EF0055A9F9 /* Rule.swift */; };
45 | E8A02CD6231948FE00C1D879 /* RuleOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E7231933EF0055A9F9 /* RuleOperators.swift */; };
46 | E8A02CD7231948FE00C1D879 /* RuleBoolPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D4231933EF0055A9F9 /* RuleBoolPredicate.swift */; };
47 | E8A02CD8231948FE00C1D879 /* RuleKeyAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DB231933EF0055A9F9 /* RuleKeyAssignment.swift */; };
48 | E8A02CE12319493D00C1D879 /* RuleKeyPathPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D3231933EF0055A9F9 /* RuleKeyPathPredicate.swift */; };
49 | E8A02CE22319493D00C1D879 /* RuleContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DE231933EF0055A9F9 /* RuleContext.swift */; };
50 | E8A02CE32319493D00C1D879 /* DynamicEnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E6231933EF0055A9F9 /* DynamicEnvironmentValues.swift */; };
51 | E8A02CE42319493D00C1D879 /* RuleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D5231933EF0055A9F9 /* RuleModel.swift */; };
52 | E8A02CE52319493D00C1D879 /* RuleDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E0231933EF0055A9F9 /* RuleDebug.swift */; };
53 | E8A02CE62319493D00C1D879 /* DynamicEnvironmentKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E3231933EF0055A9F9 /* DynamicEnvironmentKey.swift */; };
54 | E8A02CE72319493D00C1D879 /* RuleCompoundPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D1231933EF0055A9F9 /* RuleCompoundPredicate.swift */; };
55 | E8A02CE82319493D00C1D879 /* RuleTypeIDPathAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D9231933EF0055A9F9 /* RuleTypeIDPathAssignment.swift */; };
56 | E8A02CE92319493D00C1D879 /* RuleValueAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D7231933EF0055A9F9 /* RuleValueAssignment.swift */; };
57 | E8A02CEA2319493D00C1D879 /* RuleClosurePredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D0231933EF0055A9F9 /* RuleClosurePredicate.swift */; };
58 | E8A02CEB2319493D00C1D879 /* RuleTypeIDAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D8231933EF0055A9F9 /* RuleTypeIDAssignment.swift */; };
59 | E8A02CEC2319493D00C1D879 /* RuleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DA231933EF0055A9F9 /* RuleCandidate.swift */; };
60 | E8A02CEE2319493D00C1D879 /* RuleAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DD231933EF0055A9F9 /* RuleAction.swift */; };
61 | E8A02CEF2319493D00C1D879 /* RulePredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D2231933EF0055A9F9 /* RulePredicate.swift */; };
62 | E8A02CF02319493D00C1D879 /* DynamicEnvironmentPathes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E4231933EF0055A9F9 /* DynamicEnvironmentPathes.swift */; };
63 | E8A02CF12319493D00C1D879 /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E1231933EF0055A9F9 /* Rule.swift */; };
64 | E8A02CF22319493D00C1D879 /* RuleOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E7231933EF0055A9F9 /* RuleOperators.swift */; };
65 | E8A02CF32319493D00C1D879 /* RuleBoolPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D4231933EF0055A9F9 /* RuleBoolPredicate.swift */; };
66 | E8A02CF42319493D00C1D879 /* RuleKeyAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DB231933EF0055A9F9 /* RuleKeyAssignment.swift */; };
67 | E8B6016E231C002000E31BEC /* KeyPathPredicateOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B6016D231C002000E31BEC /* KeyPathPredicateOperators.swift */; };
68 | E8B6016F231C002000E31BEC /* KeyPathPredicateOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B6016D231C002000E31BEC /* KeyPathPredicateOperators.swift */; };
69 | E8B60170231C002000E31BEC /* KeyPathPredicateOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B6016D231C002000E31BEC /* KeyPathPredicateOperators.swift */; };
70 | E8B60173231C011000E31BEC /* CompoundPredicateOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B60172231C011000E31BEC /* CompoundPredicateOperators.swift */; };
71 | E8B60174231C011000E31BEC /* CompoundPredicateOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B60172231C011000E31BEC /* CompoundPredicateOperators.swift */; };
72 | E8B60175231C011000E31BEC /* CompoundPredicateOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B60172231C011000E31BEC /* CompoundPredicateOperators.swift */; };
73 | E8B60177231C018D00E31BEC /* AssignmentOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B60176231C018D00E31BEC /* AssignmentOperators.swift */; };
74 | E8B60178231C018D00E31BEC /* AssignmentOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B60176231C018D00E31BEC /* AssignmentOperators.swift */; };
75 | E8B60179231C018D00E31BEC /* AssignmentOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B60176231C018D00E31BEC /* AssignmentOperators.swift */; };
76 | E8FA13E8231933EF0055A9F9 /* RuleClosurePredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D0231933EF0055A9F9 /* RuleClosurePredicate.swift */; };
77 | E8FA13E9231933EF0055A9F9 /* RuleCompoundPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D1231933EF0055A9F9 /* RuleCompoundPredicate.swift */; };
78 | E8FA13EA231933EF0055A9F9 /* RulePredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D2231933EF0055A9F9 /* RulePredicate.swift */; };
79 | E8FA13EB231933EF0055A9F9 /* RuleKeyPathPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D3231933EF0055A9F9 /* RuleKeyPathPredicate.swift */; };
80 | E8FA13EC231933EF0055A9F9 /* RuleBoolPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D4231933EF0055A9F9 /* RuleBoolPredicate.swift */; };
81 | E8FA13ED231933EF0055A9F9 /* RuleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D5231933EF0055A9F9 /* RuleModel.swift */; };
82 | E8FA13EE231933EF0055A9F9 /* RuleValueAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D7231933EF0055A9F9 /* RuleValueAssignment.swift */; };
83 | E8FA13EF231933EF0055A9F9 /* RuleTypeIDAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D8231933EF0055A9F9 /* RuleTypeIDAssignment.swift */; };
84 | E8FA13F0231933EF0055A9F9 /* RuleTypeIDPathAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13D9231933EF0055A9F9 /* RuleTypeIDPathAssignment.swift */; };
85 | E8FA13F1231933EF0055A9F9 /* RuleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DA231933EF0055A9F9 /* RuleCandidate.swift */; };
86 | E8FA13F2231933EF0055A9F9 /* RuleKeyAssignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DB231933EF0055A9F9 /* RuleKeyAssignment.swift */; };
87 | E8FA13F4231933EF0055A9F9 /* RuleAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DD231933EF0055A9F9 /* RuleAction.swift */; };
88 | E8FA13F5231933EF0055A9F9 /* RuleContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13DE231933EF0055A9F9 /* RuleContext.swift */; };
89 | E8FA13F6231933EF0055A9F9 /* RuleDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E0231933EF0055A9F9 /* RuleDebug.swift */; };
90 | E8FA13F7231933EF0055A9F9 /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E1231933EF0055A9F9 /* Rule.swift */; };
91 | E8FA13F8231933EF0055A9F9 /* DynamicEnvironmentKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E3231933EF0055A9F9 /* DynamicEnvironmentKey.swift */; };
92 | E8FA13F9231933EF0055A9F9 /* DynamicEnvironmentPathes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E4231933EF0055A9F9 /* DynamicEnvironmentPathes.swift */; };
93 | E8FA13FA231933EF0055A9F9 /* DynamicEnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E6231933EF0055A9F9 /* DynamicEnvironmentValues.swift */; };
94 | E8FA13FB231933EF0055A9F9 /* RuleOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FA13E7231933EF0055A9F9 /* RuleOperators.swift */; };
95 | /* End PBXBuildFile section */
96 |
97 | /* Begin PBXContainerItemProxy section */
98 | E84699C923194CA800794065 /* PBXContainerItemProxy */ = {
99 | isa = PBXContainerItemProxy;
100 | containerPortal = E8FA13BF231926E50055A9F9 /* Project object */;
101 | proxyType = 1;
102 | remoteGlobalIDString = E8A02CC2231948FE00C1D879;
103 | remoteInfo = "SwiftUIRules-Mobile";
104 | };
105 | E8A02CFE2319496100C1D879 /* PBXContainerItemProxy */ = {
106 | isa = PBXContainerItemProxy;
107 | containerPortal = E8FA13BF231926E50055A9F9 /* Project object */;
108 | proxyType = 1;
109 | remoteGlobalIDString = E8FA13C8231929B40055A9F9;
110 | remoteInfo = "SwiftUIRules-Mac";
111 | };
112 | E8A02D002319496100C1D879 /* PBXContainerItemProxy */ = {
113 | isa = PBXContainerItemProxy;
114 | containerPortal = E8FA13BF231926E50055A9F9 /* Project object */;
115 | proxyType = 1;
116 | remoteGlobalIDString = E8A02CC2231948FE00C1D879;
117 | remoteInfo = "SwiftUIRules-Mobile";
118 | };
119 | E8A02D022319496100C1D879 /* PBXContainerItemProxy */ = {
120 | isa = PBXContainerItemProxy;
121 | containerPortal = E8FA13BF231926E50055A9F9 /* Project object */;
122 | proxyType = 1;
123 | remoteGlobalIDString = E8A02CDE2319493D00C1D879;
124 | remoteInfo = "SwiftUIRules-Watch";
125 | };
126 | /* End PBXContainerItemProxy section */
127 |
128 | /* Begin PBXFileReference section */
129 | E84699C323194CA800794065 /* SwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
130 | E84699C723194CA800794065 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info.plist; path = Tests/SwiftUITests/Info.plist; sourceTree = ""; };
131 | E84699CE23194DC300794065 /* TestEnvironmentKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestEnvironmentKeys.swift; path = Tests/SwiftUITests/TestEnvironmentKeys.swift; sourceTree = SOURCE_ROOT; };
132 | E87A57582319537D004EFF40 /* SwiftUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftUITests.swift; path = Tests/SwiftUITests/SwiftUITests.swift; sourceTree = SOURCE_ROOT; };
133 | E8A02CC02319442300C1D879 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
134 | E8A02CC12319446200C1D879 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
135 | E8A02CDD231948FE00C1D879 /* libSwiftUIRules-Mobile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSwiftUIRules-Mobile.a"; sourceTree = BUILT_PRODUCTS_DIR; };
136 | E8A02CF92319493D00C1D879 /* libSwiftUIRules-Watch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSwiftUIRules-Watch.a"; sourceTree = BUILT_PRODUCTS_DIR; };
137 | E8B6016D231C002000E31BEC /* KeyPathPredicateOperators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPathPredicateOperators.swift; sourceTree = ""; };
138 | E8B60171231C003F00E31BEC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
139 | E8B60172231C011000E31BEC /* CompoundPredicateOperators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundPredicateOperators.swift; sourceTree = ""; };
140 | E8B60176231C018D00E31BEC /* AssignmentOperators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssignmentOperators.swift; sourceTree = ""; };
141 | E8FA13C9231929B40055A9F9 /* libSwiftUIRules-Mac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSwiftUIRules-Mac.a"; sourceTree = BUILT_PRODUCTS_DIR; };
142 | E8FA13D0231933EF0055A9F9 /* RuleClosurePredicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleClosurePredicate.swift; sourceTree = ""; };
143 | E8FA13D1231933EF0055A9F9 /* RuleCompoundPredicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleCompoundPredicate.swift; sourceTree = ""; };
144 | E8FA13D2231933EF0055A9F9 /* RulePredicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulePredicate.swift; sourceTree = ""; };
145 | E8FA13D3231933EF0055A9F9 /* RuleKeyPathPredicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleKeyPathPredicate.swift; sourceTree = ""; };
146 | E8FA13D4231933EF0055A9F9 /* RuleBoolPredicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleBoolPredicate.swift; sourceTree = ""; };
147 | E8FA13D5231933EF0055A9F9 /* RuleModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleModel.swift; sourceTree = ""; };
148 | E8FA13D7231933EF0055A9F9 /* RuleValueAssignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleValueAssignment.swift; sourceTree = ""; };
149 | E8FA13D8231933EF0055A9F9 /* RuleTypeIDAssignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleTypeIDAssignment.swift; sourceTree = ""; };
150 | E8FA13D9231933EF0055A9F9 /* RuleTypeIDPathAssignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleTypeIDPathAssignment.swift; sourceTree = ""; };
151 | E8FA13DA231933EF0055A9F9 /* RuleCandidate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleCandidate.swift; sourceTree = ""; };
152 | E8FA13DB231933EF0055A9F9 /* RuleKeyAssignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleKeyAssignment.swift; sourceTree = ""; };
153 | E8FA13DD231933EF0055A9F9 /* RuleAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleAction.swift; sourceTree = ""; };
154 | E8FA13DE231933EF0055A9F9 /* RuleContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleContext.swift; sourceTree = ""; };
155 | E8FA13E0231933EF0055A9F9 /* RuleDebug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleDebug.swift; sourceTree = ""; };
156 | E8FA13E1231933EF0055A9F9 /* Rule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rule.swift; sourceTree = ""; };
157 | E8FA13E3231933EF0055A9F9 /* DynamicEnvironmentKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicEnvironmentKey.swift; sourceTree = ""; };
158 | E8FA13E4231933EF0055A9F9 /* DynamicEnvironmentPathes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicEnvironmentPathes.swift; sourceTree = ""; };
159 | E8FA13E5231933EF0055A9F9 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
160 | E8FA13E6231933EF0055A9F9 /* DynamicEnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicEnvironmentValues.swift; sourceTree = ""; };
161 | E8FA13E7231933EF0055A9F9 /* RuleOperators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleOperators.swift; sourceTree = ""; };
162 | E8FA13FC231933F50055A9F9 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
163 | E8FA13FD231933FA0055A9F9 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; };
164 | E8FA13FF2319340A0055A9F9 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = ""; };
165 | /* End PBXFileReference section */
166 |
167 | /* Begin PBXFrameworksBuildPhase section */
168 | E84699C023194CA800794065 /* Frameworks */ = {
169 | isa = PBXFrameworksBuildPhase;
170 | buildActionMask = 2147483647;
171 | files = (
172 | E84699C823194CA800794065 /* libSwiftUIRules-Mobile.a in Frameworks */,
173 | );
174 | runOnlyForDeploymentPostprocessing = 0;
175 | };
176 | E8A02CD9231948FE00C1D879 /* Frameworks */ = {
177 | isa = PBXFrameworksBuildPhase;
178 | buildActionMask = 2147483647;
179 | files = (
180 | );
181 | runOnlyForDeploymentPostprocessing = 0;
182 | };
183 | E8A02CF52319493D00C1D879 /* Frameworks */ = {
184 | isa = PBXFrameworksBuildPhase;
185 | buildActionMask = 2147483647;
186 | files = (
187 | );
188 | runOnlyForDeploymentPostprocessing = 0;
189 | };
190 | E8FA13C7231929B40055A9F9 /* Frameworks */ = {
191 | isa = PBXFrameworksBuildPhase;
192 | buildActionMask = 2147483647;
193 | files = (
194 | );
195 | runOnlyForDeploymentPostprocessing = 0;
196 | };
197 | /* End PBXFrameworksBuildPhase section */
198 |
199 | /* Begin PBXGroup section */
200 | E84699C423194CA800794065 /* SwiftUITests */ = {
201 | isa = PBXGroup;
202 | children = (
203 | E84699CE23194DC300794065 /* TestEnvironmentKeys.swift */,
204 | E87A57582319537D004EFF40 /* SwiftUITests.swift */,
205 | E84699C723194CA800794065 /* Info.plist */,
206 | );
207 | path = SwiftUITests;
208 | sourceTree = "";
209 | };
210 | E8B6016C231C000B00E31BEC /* Operators */ = {
211 | isa = PBXGroup;
212 | children = (
213 | E8B60171231C003F00E31BEC /* README.md */,
214 | E8FA13E7231933EF0055A9F9 /* RuleOperators.swift */,
215 | E8B6016D231C002000E31BEC /* KeyPathPredicateOperators.swift */,
216 | E8B60172231C011000E31BEC /* CompoundPredicateOperators.swift */,
217 | E8B60176231C018D00E31BEC /* AssignmentOperators.swift */,
218 | );
219 | path = Operators;
220 | sourceTree = "";
221 | };
222 | E8FA13BE231926E50055A9F9 = {
223 | isa = PBXGroup;
224 | children = (
225 | E8FA13FC231933F50055A9F9 /* README.md */,
226 | E8FA13FD231933FA0055A9F9 /* Package.swift */,
227 | E8FA13CE231933EF0055A9F9 /* SwiftUIRules */,
228 | E8FA13FE2319340A0055A9F9 /* xcconfig */,
229 | E84699C423194CA800794065 /* SwiftUITests */,
230 | E8FA13CA231929B40055A9F9 /* Products */,
231 | );
232 | sourceTree = "";
233 | };
234 | E8FA13CA231929B40055A9F9 /* Products */ = {
235 | isa = PBXGroup;
236 | children = (
237 | E8FA13C9231929B40055A9F9 /* libSwiftUIRules-Mac.a */,
238 | E8A02CDD231948FE00C1D879 /* libSwiftUIRules-Mobile.a */,
239 | E8A02CF92319493D00C1D879 /* libSwiftUIRules-Watch.a */,
240 | E84699C323194CA800794065 /* SwiftUITests.xctest */,
241 | );
242 | name = Products;
243 | sourceTree = "";
244 | };
245 | E8FA13CE231933EF0055A9F9 /* SwiftUIRules */ = {
246 | isa = PBXGroup;
247 | children = (
248 | E8FA13DE231933EF0055A9F9 /* RuleContext.swift */,
249 | E8FA13D5231933EF0055A9F9 /* RuleModel.swift */,
250 | E8FA13E1231933EF0055A9F9 /* Rule.swift */,
251 | E8B6016C231C000B00E31BEC /* Operators */,
252 | E8FA13E2231933EF0055A9F9 /* DynamicEnvironment */,
253 | E8FA13CF231933EF0055A9F9 /* Predicates */,
254 | E8FA13D6231933EF0055A9F9 /* Assignments */,
255 | E8FA13DF231933EF0055A9F9 /* Support */,
256 | );
257 | name = SwiftUIRules;
258 | path = Sources/SwiftUIRules;
259 | sourceTree = "";
260 | };
261 | E8FA13CF231933EF0055A9F9 /* Predicates */ = {
262 | isa = PBXGroup;
263 | children = (
264 | E8A02CC02319442300C1D879 /* README.md */,
265 | E8FA13D2231933EF0055A9F9 /* RulePredicate.swift */,
266 | E8FA13D1231933EF0055A9F9 /* RuleCompoundPredicate.swift */,
267 | E8FA13D3231933EF0055A9F9 /* RuleKeyPathPredicate.swift */,
268 | E8FA13D4231933EF0055A9F9 /* RuleBoolPredicate.swift */,
269 | E8FA13D0231933EF0055A9F9 /* RuleClosurePredicate.swift */,
270 | );
271 | path = Predicates;
272 | sourceTree = "";
273 | };
274 | E8FA13D6231933EF0055A9F9 /* Assignments */ = {
275 | isa = PBXGroup;
276 | children = (
277 | E8A02CC12319446200C1D879 /* README.md */,
278 | E8FA13DA231933EF0055A9F9 /* RuleCandidate.swift */,
279 | E8FA13DD231933EF0055A9F9 /* RuleAction.swift */,
280 | E8FA13D8231933EF0055A9F9 /* RuleTypeIDAssignment.swift */,
281 | E8FA13D9231933EF0055A9F9 /* RuleTypeIDPathAssignment.swift */,
282 | E8FA13D7231933EF0055A9F9 /* RuleValueAssignment.swift */,
283 | E8FA13DB231933EF0055A9F9 /* RuleKeyAssignment.swift */,
284 | );
285 | path = Assignments;
286 | sourceTree = "";
287 | };
288 | E8FA13DF231933EF0055A9F9 /* Support */ = {
289 | isa = PBXGroup;
290 | children = (
291 | E8FA13E0231933EF0055A9F9 /* RuleDebug.swift */,
292 | );
293 | path = Support;
294 | sourceTree = "";
295 | };
296 | E8FA13E2231933EF0055A9F9 /* DynamicEnvironment */ = {
297 | isa = PBXGroup;
298 | children = (
299 | E8FA13E5231933EF0055A9F9 /* README.md */,
300 | E8FA13E3231933EF0055A9F9 /* DynamicEnvironmentKey.swift */,
301 | E8FA13E4231933EF0055A9F9 /* DynamicEnvironmentPathes.swift */,
302 | E8FA13E6231933EF0055A9F9 /* DynamicEnvironmentValues.swift */,
303 | );
304 | path = DynamicEnvironment;
305 | sourceTree = "";
306 | };
307 | E8FA13FE2319340A0055A9F9 /* xcconfig */ = {
308 | isa = PBXGroup;
309 | children = (
310 | E8FA13FF2319340A0055A9F9 /* Base.xcconfig */,
311 | );
312 | path = xcconfig;
313 | sourceTree = "";
314 | };
315 | /* End PBXGroup section */
316 |
317 | /* Begin PBXHeadersBuildPhase section */
318 | E8A02CC3231948FE00C1D879 /* Headers */ = {
319 | isa = PBXHeadersBuildPhase;
320 | buildActionMask = 2147483647;
321 | files = (
322 | );
323 | runOnlyForDeploymentPostprocessing = 0;
324 | };
325 | E8A02CDF2319493D00C1D879 /* Headers */ = {
326 | isa = PBXHeadersBuildPhase;
327 | buildActionMask = 2147483647;
328 | files = (
329 | );
330 | runOnlyForDeploymentPostprocessing = 0;
331 | };
332 | E8FA13C5231929B40055A9F9 /* Headers */ = {
333 | isa = PBXHeadersBuildPhase;
334 | buildActionMask = 2147483647;
335 | files = (
336 | );
337 | runOnlyForDeploymentPostprocessing = 0;
338 | };
339 | /* End PBXHeadersBuildPhase section */
340 |
341 | /* Begin PBXNativeTarget section */
342 | E84699C223194CA800794065 /* SwiftUITests */ = {
343 | isa = PBXNativeTarget;
344 | buildConfigurationList = E84699CD23194CA800794065 /* Build configuration list for PBXNativeTarget "SwiftUITests" */;
345 | buildPhases = (
346 | E84699BF23194CA800794065 /* Sources */,
347 | E84699C023194CA800794065 /* Frameworks */,
348 | E84699C123194CA800794065 /* Resources */,
349 | );
350 | buildRules = (
351 | );
352 | dependencies = (
353 | E84699CA23194CA800794065 /* PBXTargetDependency */,
354 | );
355 | name = SwiftUITests;
356 | productName = SwiftUITests;
357 | productReference = E84699C323194CA800794065 /* SwiftUITests.xctest */;
358 | productType = "com.apple.product-type.bundle.unit-test";
359 | };
360 | E8A02CC2231948FE00C1D879 /* SwiftUIRules-Mobile */ = {
361 | isa = PBXNativeTarget;
362 | buildConfigurationList = E8A02CDA231948FE00C1D879 /* Build configuration list for PBXNativeTarget "SwiftUIRules-Mobile" */;
363 | buildPhases = (
364 | E8A02CC3231948FE00C1D879 /* Headers */,
365 | E8A02CC4231948FE00C1D879 /* Sources */,
366 | E8A02CD9231948FE00C1D879 /* Frameworks */,
367 | );
368 | buildRules = (
369 | );
370 | dependencies = (
371 | );
372 | name = "SwiftUIRules-Mobile";
373 | productName = SwiftUIRules;
374 | productReference = E8A02CDD231948FE00C1D879 /* libSwiftUIRules-Mobile.a */;
375 | productType = "com.apple.product-type.library.static";
376 | };
377 | E8A02CDE2319493D00C1D879 /* SwiftUIRules-Watch */ = {
378 | isa = PBXNativeTarget;
379 | buildConfigurationList = E8A02CF62319493D00C1D879 /* Build configuration list for PBXNativeTarget "SwiftUIRules-Watch" */;
380 | buildPhases = (
381 | E8A02CDF2319493D00C1D879 /* Headers */,
382 | E8A02CE02319493D00C1D879 /* Sources */,
383 | E8A02CF52319493D00C1D879 /* Frameworks */,
384 | );
385 | buildRules = (
386 | );
387 | dependencies = (
388 | );
389 | name = "SwiftUIRules-Watch";
390 | productName = SwiftUIRules;
391 | productReference = E8A02CF92319493D00C1D879 /* libSwiftUIRules-Watch.a */;
392 | productType = "com.apple.product-type.library.static";
393 | };
394 | E8FA13C8231929B40055A9F9 /* SwiftUIRules-Mac */ = {
395 | isa = PBXNativeTarget;
396 | buildConfigurationList = E8FA13CB231929B40055A9F9 /* Build configuration list for PBXNativeTarget "SwiftUIRules-Mac" */;
397 | buildPhases = (
398 | E8FA13C5231929B40055A9F9 /* Headers */,
399 | E8FA13C6231929B40055A9F9 /* Sources */,
400 | E8FA13C7231929B40055A9F9 /* Frameworks */,
401 | );
402 | buildRules = (
403 | );
404 | dependencies = (
405 | );
406 | name = "SwiftUIRules-Mac";
407 | productName = SwiftUIRules;
408 | productReference = E8FA13C9231929B40055A9F9 /* libSwiftUIRules-Mac.a */;
409 | productType = "com.apple.product-type.library.static";
410 | };
411 | /* End PBXNativeTarget section */
412 |
413 | /* Begin PBXProject section */
414 | E8FA13BF231926E50055A9F9 /* Project object */ = {
415 | isa = PBXProject;
416 | attributes = {
417 | LastSwiftUpdateCheck = 1100;
418 | LastUpgradeCheck = 1100;
419 | TargetAttributes = {
420 | E84699C223194CA800794065 = {
421 | CreatedOnToolsVersion = 11.0;
422 | };
423 | E8A02CFA2319495800C1D879 = {
424 | CreatedOnToolsVersion = 11.0;
425 | };
426 | E8FA13C8231929B40055A9F9 = {
427 | CreatedOnToolsVersion = 11.0;
428 | };
429 | };
430 | };
431 | buildConfigurationList = E8FA13C2231926E50055A9F9 /* Build configuration list for PBXProject "SwiftUIRules" */;
432 | compatibilityVersion = "Xcode 9.3";
433 | developmentRegion = en;
434 | hasScannedForEncodings = 0;
435 | knownRegions = (
436 | en,
437 | Base,
438 | );
439 | mainGroup = E8FA13BE231926E50055A9F9;
440 | productRefGroup = E8FA13CA231929B40055A9F9 /* Products */;
441 | projectDirPath = "";
442 | projectRoot = "";
443 | targets = (
444 | E8FA13C8231929B40055A9F9 /* SwiftUIRules-Mac */,
445 | E8A02CC2231948FE00C1D879 /* SwiftUIRules-Mobile */,
446 | E8A02CDE2319493D00C1D879 /* SwiftUIRules-Watch */,
447 | E8A02CFA2319495800C1D879 /* SwiftUIRules-All */,
448 | E84699C223194CA800794065 /* SwiftUITests */,
449 | );
450 | };
451 | /* End PBXProject section */
452 |
453 | /* Begin PBXResourcesBuildPhase section */
454 | E84699C123194CA800794065 /* Resources */ = {
455 | isa = PBXResourcesBuildPhase;
456 | buildActionMask = 2147483647;
457 | files = (
458 | );
459 | runOnlyForDeploymentPostprocessing = 0;
460 | };
461 | /* End PBXResourcesBuildPhase section */
462 |
463 | /* Begin PBXSourcesBuildPhase section */
464 | E84699BF23194CA800794065 /* Sources */ = {
465 | isa = PBXSourcesBuildPhase;
466 | buildActionMask = 2147483647;
467 | files = (
468 | E84699CF23194DC300794065 /* TestEnvironmentKeys.swift in Sources */,
469 | E87A57592319537E004EFF40 /* SwiftUITests.swift in Sources */,
470 | );
471 | runOnlyForDeploymentPostprocessing = 0;
472 | };
473 | E8A02CC4231948FE00C1D879 /* Sources */ = {
474 | isa = PBXSourcesBuildPhase;
475 | buildActionMask = 2147483647;
476 | files = (
477 | E8A02CC5231948FE00C1D879 /* RuleKeyPathPredicate.swift in Sources */,
478 | E8A02CC6231948FE00C1D879 /* RuleContext.swift in Sources */,
479 | E8B60178231C018D00E31BEC /* AssignmentOperators.swift in Sources */,
480 | E8A02CC7231948FE00C1D879 /* DynamicEnvironmentValues.swift in Sources */,
481 | E8A02CC8231948FE00C1D879 /* RuleModel.swift in Sources */,
482 | E8A02CC9231948FE00C1D879 /* RuleDebug.swift in Sources */,
483 | E8A02CCA231948FE00C1D879 /* DynamicEnvironmentKey.swift in Sources */,
484 | E8A02CCB231948FE00C1D879 /* RuleCompoundPredicate.swift in Sources */,
485 | E8A02CCC231948FE00C1D879 /* RuleTypeIDPathAssignment.swift in Sources */,
486 | E8A02CCD231948FE00C1D879 /* RuleValueAssignment.swift in Sources */,
487 | E8A02CCE231948FE00C1D879 /* RuleClosurePredicate.swift in Sources */,
488 | E8B60174231C011000E31BEC /* CompoundPredicateOperators.swift in Sources */,
489 | E8A02CCF231948FE00C1D879 /* RuleTypeIDAssignment.swift in Sources */,
490 | E8A02CD0231948FE00C1D879 /* RuleCandidate.swift in Sources */,
491 | E8A02CD2231948FE00C1D879 /* RuleAction.swift in Sources */,
492 | E8A02CD3231948FE00C1D879 /* RulePredicate.swift in Sources */,
493 | E8B6016F231C002000E31BEC /* KeyPathPredicateOperators.swift in Sources */,
494 | E8A02CD4231948FE00C1D879 /* DynamicEnvironmentPathes.swift in Sources */,
495 | E8A02CD5231948FE00C1D879 /* Rule.swift in Sources */,
496 | E8A02CD6231948FE00C1D879 /* RuleOperators.swift in Sources */,
497 | E8A02CD7231948FE00C1D879 /* RuleBoolPredicate.swift in Sources */,
498 | E8A02CD8231948FE00C1D879 /* RuleKeyAssignment.swift in Sources */,
499 | );
500 | runOnlyForDeploymentPostprocessing = 0;
501 | };
502 | E8A02CE02319493D00C1D879 /* Sources */ = {
503 | isa = PBXSourcesBuildPhase;
504 | buildActionMask = 2147483647;
505 | files = (
506 | E8A02CE12319493D00C1D879 /* RuleKeyPathPredicate.swift in Sources */,
507 | E8A02CE22319493D00C1D879 /* RuleContext.swift in Sources */,
508 | E8B60179231C018D00E31BEC /* AssignmentOperators.swift in Sources */,
509 | E8A02CE32319493D00C1D879 /* DynamicEnvironmentValues.swift in Sources */,
510 | E8A02CE42319493D00C1D879 /* RuleModel.swift in Sources */,
511 | E8A02CE52319493D00C1D879 /* RuleDebug.swift in Sources */,
512 | E8A02CE62319493D00C1D879 /* DynamicEnvironmentKey.swift in Sources */,
513 | E8A02CE72319493D00C1D879 /* RuleCompoundPredicate.swift in Sources */,
514 | E8A02CE82319493D00C1D879 /* RuleTypeIDPathAssignment.swift in Sources */,
515 | E8A02CE92319493D00C1D879 /* RuleValueAssignment.swift in Sources */,
516 | E8A02CEA2319493D00C1D879 /* RuleClosurePredicate.swift in Sources */,
517 | E8B60175231C011000E31BEC /* CompoundPredicateOperators.swift in Sources */,
518 | E8A02CEB2319493D00C1D879 /* RuleTypeIDAssignment.swift in Sources */,
519 | E8A02CEC2319493D00C1D879 /* RuleCandidate.swift in Sources */,
520 | E8A02CEE2319493D00C1D879 /* RuleAction.swift in Sources */,
521 | E8A02CEF2319493D00C1D879 /* RulePredicate.swift in Sources */,
522 | E8B60170231C002000E31BEC /* KeyPathPredicateOperators.swift in Sources */,
523 | E8A02CF02319493D00C1D879 /* DynamicEnvironmentPathes.swift in Sources */,
524 | E8A02CF12319493D00C1D879 /* Rule.swift in Sources */,
525 | E8A02CF22319493D00C1D879 /* RuleOperators.swift in Sources */,
526 | E8A02CF32319493D00C1D879 /* RuleBoolPredicate.swift in Sources */,
527 | E8A02CF42319493D00C1D879 /* RuleKeyAssignment.swift in Sources */,
528 | );
529 | runOnlyForDeploymentPostprocessing = 0;
530 | };
531 | E8FA13C6231929B40055A9F9 /* Sources */ = {
532 | isa = PBXSourcesBuildPhase;
533 | buildActionMask = 2147483647;
534 | files = (
535 | E8FA13EB231933EF0055A9F9 /* RuleKeyPathPredicate.swift in Sources */,
536 | E8FA13F5231933EF0055A9F9 /* RuleContext.swift in Sources */,
537 | E8B60177231C018D00E31BEC /* AssignmentOperators.swift in Sources */,
538 | E8FA13FA231933EF0055A9F9 /* DynamicEnvironmentValues.swift in Sources */,
539 | E8FA13ED231933EF0055A9F9 /* RuleModel.swift in Sources */,
540 | E8FA13F6231933EF0055A9F9 /* RuleDebug.swift in Sources */,
541 | E8FA13F8231933EF0055A9F9 /* DynamicEnvironmentKey.swift in Sources */,
542 | E8FA13E9231933EF0055A9F9 /* RuleCompoundPredicate.swift in Sources */,
543 | E8FA13F0231933EF0055A9F9 /* RuleTypeIDPathAssignment.swift in Sources */,
544 | E8FA13EE231933EF0055A9F9 /* RuleValueAssignment.swift in Sources */,
545 | E8FA13E8231933EF0055A9F9 /* RuleClosurePredicate.swift in Sources */,
546 | E8B60173231C011000E31BEC /* CompoundPredicateOperators.swift in Sources */,
547 | E8FA13EF231933EF0055A9F9 /* RuleTypeIDAssignment.swift in Sources */,
548 | E8FA13F1231933EF0055A9F9 /* RuleCandidate.swift in Sources */,
549 | E8FA13F4231933EF0055A9F9 /* RuleAction.swift in Sources */,
550 | E8FA13EA231933EF0055A9F9 /* RulePredicate.swift in Sources */,
551 | E8B6016E231C002000E31BEC /* KeyPathPredicateOperators.swift in Sources */,
552 | E8FA13F9231933EF0055A9F9 /* DynamicEnvironmentPathes.swift in Sources */,
553 | E8FA13F7231933EF0055A9F9 /* Rule.swift in Sources */,
554 | E8FA13FB231933EF0055A9F9 /* RuleOperators.swift in Sources */,
555 | E8FA13EC231933EF0055A9F9 /* RuleBoolPredicate.swift in Sources */,
556 | E8FA13F2231933EF0055A9F9 /* RuleKeyAssignment.swift in Sources */,
557 | );
558 | runOnlyForDeploymentPostprocessing = 0;
559 | };
560 | /* End PBXSourcesBuildPhase section */
561 |
562 | /* Begin PBXTargetDependency section */
563 | E84699CA23194CA800794065 /* PBXTargetDependency */ = {
564 | isa = PBXTargetDependency;
565 | target = E8A02CC2231948FE00C1D879 /* SwiftUIRules-Mobile */;
566 | targetProxy = E84699C923194CA800794065 /* PBXContainerItemProxy */;
567 | };
568 | E8A02CFF2319496100C1D879 /* PBXTargetDependency */ = {
569 | isa = PBXTargetDependency;
570 | target = E8FA13C8231929B40055A9F9 /* SwiftUIRules-Mac */;
571 | targetProxy = E8A02CFE2319496100C1D879 /* PBXContainerItemProxy */;
572 | };
573 | E8A02D012319496100C1D879 /* PBXTargetDependency */ = {
574 | isa = PBXTargetDependency;
575 | target = E8A02CC2231948FE00C1D879 /* SwiftUIRules-Mobile */;
576 | targetProxy = E8A02D002319496100C1D879 /* PBXContainerItemProxy */;
577 | };
578 | E8A02D032319496100C1D879 /* PBXTargetDependency */ = {
579 | isa = PBXTargetDependency;
580 | target = E8A02CDE2319493D00C1D879 /* SwiftUIRules-Watch */;
581 | targetProxy = E8A02D022319496100C1D879 /* PBXContainerItemProxy */;
582 | };
583 | /* End PBXTargetDependency section */
584 |
585 | /* Begin XCBuildConfiguration section */
586 | E84699CB23194CA800794065 /* Debug */ = {
587 | isa = XCBuildConfiguration;
588 | buildSettings = {
589 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
590 | CLANG_CXX_LIBRARY = "libc++";
591 | CODE_SIGN_STYLE = Automatic;
592 | COPY_PHASE_STRIP = NO;
593 | ENABLE_STRICT_OBJC_MSGSEND = YES;
594 | ENABLE_TESTABILITY = YES;
595 | GCC_DYNAMIC_NO_PIC = NO;
596 | GCC_OPTIMIZATION_LEVEL = 0;
597 | GCC_PREPROCESSOR_DEFINITIONS = (
598 | "DEBUG=1",
599 | "$(inherited)",
600 | );
601 | INFOPLIST_FILE = Tests/SwiftUITests/Info.plist;
602 | LD_RUNPATH_SEARCH_PATHS = (
603 | "$(inherited)",
604 | "@executable_path/Frameworks",
605 | "@loader_path/Frameworks",
606 | );
607 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
608 | MTL_FAST_MATH = YES;
609 | ONLY_ACTIVE_ARCH = YES;
610 | PRODUCT_BUNDLE_IDENTIFIER = de.zeezide.swiftui.rules.SwiftUITests;
611 | PRODUCT_NAME = "$(TARGET_NAME)";
612 | SDKROOT = iphoneos;
613 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
614 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
615 | TARGETED_DEVICE_FAMILY = "1,2";
616 | };
617 | name = Debug;
618 | };
619 | E84699CC23194CA800794065 /* Release */ = {
620 | isa = XCBuildConfiguration;
621 | buildSettings = {
622 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
623 | CLANG_CXX_LIBRARY = "libc++";
624 | CODE_SIGN_STYLE = Automatic;
625 | COPY_PHASE_STRIP = NO;
626 | ENABLE_NS_ASSERTIONS = NO;
627 | ENABLE_STRICT_OBJC_MSGSEND = YES;
628 | INFOPLIST_FILE = Tests/SwiftUITests/Info.plist;
629 | LD_RUNPATH_SEARCH_PATHS = (
630 | "$(inherited)",
631 | "@executable_path/Frameworks",
632 | "@loader_path/Frameworks",
633 | );
634 | MTL_ENABLE_DEBUG_INFO = NO;
635 | MTL_FAST_MATH = YES;
636 | PRODUCT_BUNDLE_IDENTIFIER = de.zeezide.swiftui.rules.SwiftUITests;
637 | PRODUCT_NAME = "$(TARGET_NAME)";
638 | SDKROOT = iphoneos;
639 | SWIFT_COMPILATION_MODE = wholemodule;
640 | SWIFT_OPTIMIZATION_LEVEL = "-O";
641 | TARGETED_DEVICE_FAMILY = "1,2";
642 | VALIDATE_PRODUCT = YES;
643 | };
644 | name = Release;
645 | };
646 | E8A02CDB231948FE00C1D879 /* Debug */ = {
647 | isa = XCBuildConfiguration;
648 | buildSettings = {
649 | CODE_SIGN_STYLE = Automatic;
650 | ENABLE_TESTABILITY = YES;
651 | EXECUTABLE_PREFIX = lib;
652 | GCC_OPTIMIZATION_LEVEL = 0;
653 | GCC_PREPROCESSOR_DEFINITIONS = (
654 | "DEBUG=1",
655 | "$(inherited)",
656 | );
657 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
658 | ONLY_ACTIVE_ARCH = YES;
659 | PRODUCT_MODULE_NAME = SwiftUIRules;
660 | PRODUCT_NAME = "$(TARGET_NAME)";
661 | SDKROOT = iphoneos;
662 | };
663 | name = Debug;
664 | };
665 | E8A02CDC231948FE00C1D879 /* Release */ = {
666 | isa = XCBuildConfiguration;
667 | buildSettings = {
668 | CODE_SIGN_STYLE = Automatic;
669 | ENABLE_NS_ASSERTIONS = NO;
670 | EXECUTABLE_PREFIX = lib;
671 | MTL_ENABLE_DEBUG_INFO = NO;
672 | PRODUCT_MODULE_NAME = SwiftUIRules;
673 | PRODUCT_NAME = "$(TARGET_NAME)";
674 | SDKROOT = iphoneos;
675 | };
676 | name = Release;
677 | };
678 | E8A02CF72319493D00C1D879 /* Debug */ = {
679 | isa = XCBuildConfiguration;
680 | buildSettings = {
681 | CODE_SIGN_STYLE = Automatic;
682 | ENABLE_TESTABILITY = YES;
683 | EXECUTABLE_PREFIX = lib;
684 | GCC_OPTIMIZATION_LEVEL = 0;
685 | GCC_PREPROCESSOR_DEFINITIONS = (
686 | "DEBUG=1",
687 | "$(inherited)",
688 | );
689 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
690 | ONLY_ACTIVE_ARCH = YES;
691 | PRODUCT_MODULE_NAME = SwiftUIRules;
692 | PRODUCT_NAME = "$(TARGET_NAME)";
693 | SDKROOT = watchos;
694 | };
695 | name = Debug;
696 | };
697 | E8A02CF82319493D00C1D879 /* Release */ = {
698 | isa = XCBuildConfiguration;
699 | buildSettings = {
700 | CODE_SIGN_STYLE = Automatic;
701 | ENABLE_NS_ASSERTIONS = NO;
702 | EXECUTABLE_PREFIX = lib;
703 | MTL_ENABLE_DEBUG_INFO = NO;
704 | PRODUCT_MODULE_NAME = SwiftUIRules;
705 | PRODUCT_NAME = "$(TARGET_NAME)";
706 | SDKROOT = watchos;
707 | };
708 | name = Release;
709 | };
710 | E8A02CFC2319495800C1D879 /* Debug */ = {
711 | isa = XCBuildConfiguration;
712 | buildSettings = {
713 | CODE_SIGN_STYLE = Automatic;
714 | PRODUCT_NAME = "$(TARGET_NAME)";
715 | };
716 | name = Debug;
717 | };
718 | E8A02CFD2319495800C1D879 /* Release */ = {
719 | isa = XCBuildConfiguration;
720 | buildSettings = {
721 | CODE_SIGN_STYLE = Automatic;
722 | PRODUCT_NAME = "$(TARGET_NAME)";
723 | };
724 | name = Release;
725 | };
726 | E8FA13C3231926E50055A9F9 /* Debug */ = {
727 | isa = XCBuildConfiguration;
728 | baseConfigurationReference = E8FA13FF2319340A0055A9F9 /* Base.xcconfig */;
729 | buildSettings = {
730 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
731 | };
732 | name = Debug;
733 | };
734 | E8FA13C4231926E50055A9F9 /* Release */ = {
735 | isa = XCBuildConfiguration;
736 | baseConfigurationReference = E8FA13FF2319340A0055A9F9 /* Base.xcconfig */;
737 | buildSettings = {
738 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
739 | };
740 | name = Release;
741 | };
742 | E8FA13CC231929B40055A9F9 /* Debug */ = {
743 | isa = XCBuildConfiguration;
744 | buildSettings = {
745 | CODE_SIGN_STYLE = Automatic;
746 | ENABLE_TESTABILITY = YES;
747 | EXECUTABLE_PREFIX = lib;
748 | GCC_OPTIMIZATION_LEVEL = 0;
749 | GCC_PREPROCESSOR_DEFINITIONS = (
750 | "DEBUG=1",
751 | "$(inherited)",
752 | );
753 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
754 | ONLY_ACTIVE_ARCH = YES;
755 | PRODUCT_MODULE_NAME = SwiftUIRules;
756 | PRODUCT_NAME = "$(TARGET_NAME)";
757 | SDKROOT = macosx;
758 | };
759 | name = Debug;
760 | };
761 | E8FA13CD231929B40055A9F9 /* Release */ = {
762 | isa = XCBuildConfiguration;
763 | buildSettings = {
764 | CODE_SIGN_STYLE = Automatic;
765 | ENABLE_NS_ASSERTIONS = NO;
766 | EXECUTABLE_PREFIX = lib;
767 | MTL_ENABLE_DEBUG_INFO = NO;
768 | PRODUCT_MODULE_NAME = SwiftUIRules;
769 | PRODUCT_NAME = "$(TARGET_NAME)";
770 | SDKROOT = macosx;
771 | };
772 | name = Release;
773 | };
774 | /* End XCBuildConfiguration section */
775 |
776 | /* Begin XCConfigurationList section */
777 | E84699CD23194CA800794065 /* Build configuration list for PBXNativeTarget "SwiftUITests" */ = {
778 | isa = XCConfigurationList;
779 | buildConfigurations = (
780 | E84699CB23194CA800794065 /* Debug */,
781 | E84699CC23194CA800794065 /* Release */,
782 | );
783 | defaultConfigurationIsVisible = 0;
784 | defaultConfigurationName = Release;
785 | };
786 | E8A02CDA231948FE00C1D879 /* Build configuration list for PBXNativeTarget "SwiftUIRules-Mobile" */ = {
787 | isa = XCConfigurationList;
788 | buildConfigurations = (
789 | E8A02CDB231948FE00C1D879 /* Debug */,
790 | E8A02CDC231948FE00C1D879 /* Release */,
791 | );
792 | defaultConfigurationIsVisible = 0;
793 | defaultConfigurationName = Release;
794 | };
795 | E8A02CF62319493D00C1D879 /* Build configuration list for PBXNativeTarget "SwiftUIRules-Watch" */ = {
796 | isa = XCConfigurationList;
797 | buildConfigurations = (
798 | E8A02CF72319493D00C1D879 /* Debug */,
799 | E8A02CF82319493D00C1D879 /* Release */,
800 | );
801 | defaultConfigurationIsVisible = 0;
802 | defaultConfigurationName = Release;
803 | };
804 | E8A02CFB2319495800C1D879 /* Build configuration list for PBXAggregateTarget "SwiftUIRules-All" */ = {
805 | isa = XCConfigurationList;
806 | buildConfigurations = (
807 | E8A02CFC2319495800C1D879 /* Debug */,
808 | E8A02CFD2319495800C1D879 /* Release */,
809 | );
810 | defaultConfigurationIsVisible = 0;
811 | defaultConfigurationName = Release;
812 | };
813 | E8FA13C2231926E50055A9F9 /* Build configuration list for PBXProject "SwiftUIRules" */ = {
814 | isa = XCConfigurationList;
815 | buildConfigurations = (
816 | E8FA13C3231926E50055A9F9 /* Debug */,
817 | E8FA13C4231926E50055A9F9 /* Release */,
818 | );
819 | defaultConfigurationIsVisible = 0;
820 | defaultConfigurationName = Release;
821 | };
822 | E8FA13CB231929B40055A9F9 /* Build configuration list for PBXNativeTarget "SwiftUIRules-Mac" */ = {
823 | isa = XCConfigurationList;
824 | buildConfigurations = (
825 | E8FA13CC231929B40055A9F9 /* Debug */,
826 | E8FA13CD231929B40055A9F9 /* Release */,
827 | );
828 | defaultConfigurationIsVisible = 0;
829 | defaultConfigurationName = Release;
830 | };
831 | /* End XCConfigurationList section */
832 | };
833 | rootObject = E8FA13BF231926E50055A9F9 /* Project object */;
834 | }
835 |
--------------------------------------------------------------------------------
/SwiftUIRules.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUIRules.xcodeproj/xcshareddata/xcschemes/SwiftUIRules-Mac.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/SwiftUIRules.xcodeproj/xcshareddata/xcschemes/SwiftUIRules-Mobile.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
67 |
68 |
74 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/SwiftUIRules.xcodeproj/xcshareddata/xcschemes/SwiftUIRules-Watch.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Tests/SwiftUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Tests/SwiftUITests/SwiftUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUITests.swift
3 | // SwiftUITests
4 | //
5 | // Created by Helge Heß on 30.08.19.
6 | //
7 |
8 | import XCTest
9 | @testable import SwiftUIRules
10 |
11 | class SwiftUITests: XCTestCase {
12 |
13 | func testSimpleModelDefaultEnvironment() throws {
14 | let context = RuleContext(ruleModel: [
15 | \.verb == "view" => \.title <= "Hello!"
16 | ])
17 | XCTAssertEqual(context.title, "Hello!")
18 | }
19 |
20 | func testSimpleModelCustomEnvironment() throws {
21 | var context = RuleContext(ruleModel: [
22 | \.verb == "edit" => \.title <= "Work it.",
23 | \.verb == "view" => \.title <= "Hello!"
24 | ])
25 | context.verb = "edit"
26 |
27 | XCTAssertEqual(context.title, "Work it.")
28 | }
29 |
30 | func testPathCompare() throws {
31 | let context = RuleContext(ruleModel: [
32 | \.todo.priority == .low => \.color <= .gray,
33 | \.todo.priority == .high => \.color <= .red
34 | ])
35 | XCTAssertEqual(context.color, .red)
36 | }
37 |
38 | func testRuleComplexity() throws {
39 | let context = RuleContext(ruleModel: [
40 | \.todo.priority == .high => \.color <= .red,
41 | \.todo.priority == .high && \.verb == "view" => \.color <= .gray
42 | ])
43 | XCTAssertEqual(context.color, .gray)
44 | }
45 |
46 | func testSimplePathAssignment() throws {
47 | let context = RuleContext(ruleModel: [ \.navigationBarTitle <= \.title ])
48 | XCTAssertEqual(context.navigationBarTitle, TitleEnvironmentKey.defaultValue)
49 | }
50 |
51 | func testOtherPathAssignment() throws {
52 | let context = RuleContext(ruleModel: [ \.title <= \.verb ])
53 | XCTAssertEqual(context.title, VerbEnvironmentKey.defaultValue)
54 | }
55 |
56 | func testNestedPathAssignment() throws {
57 | let context = RuleContext(ruleModel: [
58 | \.navigationBarTitle <= \.title,
59 | \.title <= \.verb
60 | ])
61 | XCTAssertEqual(context.title, VerbEnvironmentKey.defaultValue)
62 | XCTAssertEqual(context.navigationBarTitle, VerbEnvironmentKey.defaultValue)
63 | }
64 |
65 | func testKeyPath2Predicate() throws {
66 | var context = RuleContext(ruleModel: [
67 | \.title == \.navigationBarTitle => \.color <= .red,
68 | \.color <= .green
69 | ])
70 | context.title = "blub"
71 | context.navigationBarTitle = "blub"
72 | XCTAssertEqual(context.color, .red)
73 | }
74 |
75 | func CRASHtestSelfAssignment() throws {
76 | // This results in a regular recursion. It looks like it doesn't make
77 | // too much sense to protect against such simple ones, as the user can
78 | // always build recursions by other means.
79 | // We'd have to count the recursion in the context somehow and eventually
80 | // stop.
81 | let context = RuleContext(ruleModel: [
82 | \.title <= \.title
83 | ])
84 | _ = context.title
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Tests/SwiftUITests/TestEnvironmentKeys.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestEnvironmentKeys.swift
3 | // SwiftUITests
4 | //
5 | // Created by Helge Heß on 30.08.19.
6 | //
7 |
8 | #if canImport(UIKit)
9 | import class UIKit.UIColor
10 | typealias UXColor = UIColor
11 | #elseif canImport(AppKit)
12 | import class AppKit.NSColor
13 | typealias UXColor = NSColor
14 | #endif
15 |
16 | import SwiftUIRules
17 |
18 | struct Todo {
19 | enum Priority {
20 | case low, normal, high
21 | }
22 |
23 | var title : String
24 | var completed : Bool
25 | var priority : Priority
26 | }
27 |
28 | struct VerbEnvironmentKey: DynamicEnvironmentKey {
29 | public static let defaultValue = "view"
30 | }
31 |
32 | struct TitleEnvironmentKey: DynamicEnvironmentKey {
33 | public static let defaultValue = "Hello World"
34 | }
35 |
36 | struct NavigationBarTitleEnvironmentKey: DynamicEnvironmentKey {
37 | public static let defaultValue = TitleEnvironmentKey.defaultValue
38 | }
39 |
40 | struct ColorEnvironmentKey: DynamicEnvironmentKey {
41 | public static let defaultValue = UXColor.black
42 | }
43 |
44 | struct TodoEnvironmentKey: DynamicEnvironmentKey {
45 | public static let defaultValue =
46 | Todo(title: "Buy Beer", completed: false, priority: .high)
47 | }
48 |
49 | extension DynamicEnvironmentPathes {
50 |
51 | var verb : String {
52 | set { self[dynamic: VerbEnvironmentKey.self] = newValue }
53 | get { self[dynamic: VerbEnvironmentKey.self] }
54 | }
55 | var title : String {
56 | set { self[dynamic: TitleEnvironmentKey.self] = newValue }
57 | get { self[dynamic: TitleEnvironmentKey.self] }
58 | }
59 | var navigationBarTitle : String {
60 | set { self[dynamic: NavigationBarTitleEnvironmentKey.self] = newValue }
61 | get { self[dynamic: NavigationBarTitleEnvironmentKey.self] }
62 | }
63 |
64 | var color : UXColor {
65 | set { self[dynamic: ColorEnvironmentKey.self] = newValue }
66 | get { self[dynamic: ColorEnvironmentKey.self] }
67 | }
68 |
69 | var todo : Todo {
70 | set { self[dynamic: TodoEnvironmentKey.self] = newValue }
71 | get { self[dynamic: TodoEnvironmentKey.self] }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/xcconfig/Base.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (C) 2017-2019 ZeeZide GmbH, All Rights Reserved
3 | // Created by Helge Hess on 23/01/2017.
4 | //
5 |
6 | // -------------------------- Base config -----------------------------
7 |
8 | SWIFT_VERSION = 5.1
9 |
10 | // Deployment Targets
11 | MACOSX_DEPLOYMENT_TARGET = 10.15
12 | IPHONEOS_DEPLOYMENT_TARGET = 13.0
13 | WATCHOS_DEPLOYMENT_TARGET = 6.0
14 | TVOS_DEPLOYMENT_TARGET = 13.0
15 | SUPPORTS_MACCATALYST = NO
16 |
17 | // Signing
18 | CODE_SIGN_IDENTITY = -
19 | DEVELOPMENT_TEAM =
20 |
21 | // Include
22 | ALWAYS_SEARCH_USER_PATHS = NO
23 |
24 | // Language
25 | GCC_C_LANGUAGE_STANDARD = gnu11
26 | GCC_NO_COMMON_BLOCKS = YES
27 | CLANG_ENABLE_MODULES = YES
28 | CLANG_ENABLE_OBJC_ARC = YES
29 | CLANG_ENABLE_OBJC_WEAK = YES
30 | ENABLE_STRICT_OBJC_MSGSEND = YES
31 |
32 | // Warnings
33 | CLANG_WARN_BOOL_CONVERSION = YES
34 | CLANG_WARN_CONSTANT_CONVERSION = YES
35 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
36 | CLANG_WARN_EMPTY_BODY = YES
37 | CLANG_WARN_ENUM_CONVERSION = YES
38 | CLANG_WARN_INT_CONVERSION = YES
39 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
40 | CLANG_WARN_UNREACHABLE_CODE = YES
41 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
42 | CLANG_WARN_INFINITE_RECURSION = YES
43 | CLANG_WARN_SUSPICIOUS_MOVE = YES
44 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES
45 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
46 | CLANG_WARN_COMMA = YES
47 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
48 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
49 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
50 | CLANG_WARN_STRICT_PROTOTYPES = YES
51 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
52 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
53 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
54 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES
55 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR
56 | GCC_WARN_UNDECLARED_SELECTOR = YES
57 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE
58 | GCC_WARN_UNUSED_FUNCTION = YES
59 | GCC_WARN_UNUSED_VARIABLE = YES
60 |
61 | // Analyzer
62 | CLANG_ANALYZER_NONNULL = YES
63 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES
64 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE
65 |
66 | // Debug
67 | DEBUG_INFORMATION_FORMAT = dwarf
68 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
69 |
70 | // Swift Optimization
71 | SWIFT_OPTIMIZATION_LEVEL_Release = -Owholemodule
72 | SWIFT_OPTIMIZATION_LEVEL_Debug = -Onone
73 | SWIFT_OPTIMIZATION_LEVEL = $(SWIFT_OPTIMIZATION_LEVEL_$(CONFIGURATION))
74 |
75 | SWIFT_ACTIVE_COMPILATION_CONDITIONS_Release =
76 | SWIFT_ACTIVE_COMPILATION_CONDITIONS_Debug = DEBUG
77 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(SWIFT_ACTIVE_COMPILATION_CONDITIONS_$(CONFIGURATION))
78 |
79 | MTL_FAST_MATH = YES
80 |
--------------------------------------------------------------------------------