@@ -19,24 +19,79 @@ import SwiftUI
1919/// [here](https://gist.github.com/mbrandonw/dee2ceac2c316a1619cfdf1dc7945f66)).
2020public struct WithViewStore < State, Action, Content> {
2121 private let content : ( ViewStore < State , Action > ) -> Content
22- private var prefix : String ?
22+ #if DEBUG
23+ private let file : StaticString
24+ private let line : UInt
25+ private var prefix : String ?
26+ private var previousState : ( State ) -> State ?
27+ #endif
2328 @ObservedObject private var viewStore : ViewStore < State , Action >
2429
30+ fileprivate init (
31+ store: Store < State , Action > ,
32+ removeDuplicates isDuplicate: @escaping ( State , State ) -> Bool ,
33+ file: StaticString = #fileID,
34+ line: UInt = #line,
35+ content: @escaping ( ViewStore < State , Action > ) -> Content
36+ ) {
37+ self . content = content
38+ #if DEBUG
39+ self . file = file
40+ self . line = line
41+ var previousState : State ? = nil
42+ self . previousState = { currentState in
43+ defer { previousState = currentState }
44+ return previousState
45+ }
46+ #endif
47+ self . viewStore = ViewStore ( store, removeDuplicates: isDuplicate)
48+ }
49+
2550 /// Prints debug information to the console whenever the view is computed.
2651 ///
2752 /// - Parameter prefix: A string with which to prefix all debug messages.
2853 /// - Returns: A structure that prints debug messages for all computations.
2954 public func debug( _ prefix: String = " " ) -> Self {
3055 var view = self
31- view. prefix = prefix
56+ #if DEBUG
57+ view. prefix = prefix
58+ #endif
3259 return view
3360 }
61+
62+ fileprivate var _body : Content {
63+ #if DEBUG
64+ if let prefix = self . prefix {
65+ let difference = self . previousState ( self . viewStore. state)
66+ . map {
67+ debugDiff ( $0, self . viewStore. state) . map { " (Changed state) \n \( $0) " }
68+ ?? " (No difference in state detected) "
69+ }
70+ ?? " (Initial state) \n \( debugOutput ( self . viewStore. state, indent: 2 ) ) "
71+ func typeName( _ type: Any . Type ) -> String {
72+ var name = String ( reflecting: type)
73+ if let index = name. firstIndex ( of: " . " ) {
74+ name. removeSubrange ( ... index)
75+ }
76+ return name
77+ }
78+ print (
79+ """
80+ \( prefix. isEmpty ? " " : " \( prefix) : " ) \
81+ WithViewStore< \( typeName ( State . self) ) , \( typeName ( Action . self) ) , _> \
82+ @ \( self . file) : \( self . line) \( difference)
83+ """
84+ )
85+ }
86+ #endif
87+ return self . content ( self . viewStore)
88+ }
3489}
3590
3691extension WithViewStore : View where Content: View {
3792 /// Initializes a structure that transforms a store into an observable view store in order to
3893 /// compute views from store state.
39-
94+ ///
4095 /// - Parameters:
4196 /// - store: A store.
4297 /// - isDuplicate: A function to determine when two `State` values are equal. When values are
@@ -45,28 +100,25 @@ extension WithViewStore: View where Content: View {
45100 public init (
46101 _ store: Store < State , Action > ,
47102 removeDuplicates isDuplicate: @escaping ( State , State ) -> Bool ,
103+ file: StaticString = #fileID,
104+ line: UInt = #line,
48105 @ViewBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
49106 ) {
50- self . content = content
51- self . viewStore = ViewStore ( store, removeDuplicates: isDuplicate)
107+ self . init (
108+ store: store,
109+ removeDuplicates: isDuplicate,
110+ file: file,
111+ line: line,
112+ content: content
113+ )
52114 }
53115
54116 public var body : Content {
55- #if DEBUG
56- if let prefix = self . prefix {
57- print (
58- """
59- \( prefix. isEmpty ? " " : " \( prefix) : " ) \
60- Evaluating WithViewStore< \( State . self) , \( Action . self) , ...>.body
61- """
62- )
63- }
64- #endif
65- return self . content ( self . viewStore)
117+ self . _body
66118 }
67119}
68120
69- extension WithViewStore where Content : View , State : Equatable {
121+ extension WithViewStore where State : Equatable , Content : View {
70122 /// Initializes a structure that transforms a store into an observable view store in order to
71123 /// compute views from equatable store state.
72124 ///
@@ -75,13 +127,15 @@ extension WithViewStore where Content: View, State: Equatable {
75127 /// - content: A function that can generate content from a view store.
76128 public init (
77129 _ store: Store < State , Action > ,
130+ file: StaticString = #fileID,
131+ line: UInt = #line,
78132 @ViewBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
79133 ) {
80- self . init ( store, removeDuplicates: == , content: content)
134+ self . init ( store, removeDuplicates: == , file : file , line : line , content: content)
81135 }
82136}
83137
84- extension WithViewStore where Content : View , State == Void {
138+ extension WithViewStore where State == Void , Content : View {
85139 /// Initializes a structure that transforms a store into an observable view store in order to
86140 /// compute views from equatable store state.
87141 ///
@@ -90,9 +144,11 @@ extension WithViewStore where Content: View, State == Void {
90144 /// - content: A function that can generate content from a view store.
91145 public init (
92146 _ store: Store < State , Action > ,
147+ file: StaticString = #fileID,
148+ line: UInt = #line,
93149 @ViewBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
94150 ) {
95- self . init ( store, removeDuplicates: == , content: content)
151+ self . init ( store, removeDuplicates: == , file : file , line : line , content: content)
96152 }
97153}
98154
@@ -104,74 +160,69 @@ extension WithViewStore: DynamicViewContent where State: Collection, Content: Dy
104160 }
105161}
106162
107- #if compiler(>=5.3)
108- import SwiftUI
109-
110- /// A structure that transforms a store into an observable view store in order to compute scenes
111- /// from store state.
112- @available ( iOS 14 , macOS 11 , tvOS 14 , watchOS 7 , * )
113- extension WithViewStore : Scene where Content: Scene {
114- /// Initializes a structure that transforms a store into an observable view store in order to
115- /// compute scenes from store state.
116-
117- /// - Parameters:
118- /// - store: A store.
119- /// - isDuplicate: A function to determine when two `State` values are equal. When values are
120- /// equal, repeat view computations are removed,
121- /// - content: A function that can generate content from a view store.
122- public init (
123- _ store: Store < State , Action > ,
124- removeDuplicates isDuplicate: @escaping ( State , State ) -> Bool ,
125- @SceneBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
126- ) {
127- self . content = content
128- self . viewStore = ViewStore ( store, removeDuplicates: isDuplicate)
129- }
163+ @available ( iOS 14 , macOS 11 , tvOS 14 , watchOS 7 , * )
164+ extension WithViewStore : Scene where Content: Scene {
165+ /// Initializes a structure that transforms a store into an observable view store in order to
166+ /// compute views from store state.
167+ ///
168+ /// - Parameters:
169+ /// - store: A store.
170+ /// - isDuplicate: A function to determine when two `State` values are equal. When values are
171+ /// equal, repeat view computations are removed,
172+ /// - content: A function that can generate content from a view store.
173+ public init (
174+ _ store: Store < State , Action > ,
175+ removeDuplicates isDuplicate: @escaping ( State , State ) -> Bool ,
176+ file: StaticString = #fileID,
177+ line: UInt = #line,
178+ @SceneBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
179+ ) {
180+ self . init (
181+ store: store,
182+ removeDuplicates: isDuplicate,
183+ file: file,
184+ line: line,
185+ content: content
186+ )
187+ }
130188
131- public var body : Content {
132- #if DEBUG
133- if let prefix = self . prefix {
134- print (
135- """
136- \( prefix. isEmpty ? " " : " \( prefix) : " ) \
137- Evaluating WithViewStore< \( State . self) , \( Action . self) , ...>.body
138- """
139- )
140- }
141- #endif
142- return self . content ( self . viewStore)
143- }
189+ public var body : Content {
190+ self . _body
144191 }
192+ }
145193
146- @available ( iOS 14 , macOS 11 , tvOS 14 , watchOS 7 , * )
147- extension WithViewStore where Content: Scene , State: Equatable {
148- /// Initializes a structure that transforms a store into an observable view store in order to
149- /// compute views from equatable store state.
150- ///
151- /// - Parameters:
152- /// - store: A store of equatable state.
153- /// - content: A function that can generate content from a view store.
154- public init (
155- _ store: Store < State , Action > ,
156- @SceneBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
157- ) {
158- self . init ( store, removeDuplicates: == , content: content)
159- }
194+ @available ( iOS 14 , macOS 11 , tvOS 14 , watchOS 7 , * )
195+ extension WithViewStore where State: Equatable , Content: Scene {
196+ /// Initializes a structure that transforms a store into an observable view store in order to
197+ /// compute scenes from equatable store state.
198+ ///
199+ /// - Parameters:
200+ /// - store: A store of equatable state.
201+ /// - content: A function that can generate content from a view store.
202+ public init (
203+ _ store: Store < State , Action > ,
204+ file: StaticString = #fileID,
205+ line: UInt = #line,
206+ @SceneBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
207+ ) {
208+ self . init ( store, removeDuplicates: == , file: file, line: line, content: content)
160209 }
210+ }
161211
162- @available ( iOS 14 , macOS 11 , tvOS 14 , watchOS 7 , * )
163- extension WithViewStore where Content: Scene , State == Void {
164- /// Initializes a structure that transforms a store into an observable view store in order to
165- /// compute views from equatable store state.
166- ///
167- /// - Parameters:
168- /// - store: A store of equatable state.
169- /// - content: A function that can generate content from a view store.
170- public init (
171- _ store: Store < State , Action > ,
172- @SceneBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
173- ) {
174- self . init ( store, removeDuplicates: == , content: content)
175- }
212+ @available ( iOS 14 , macOS 11 , tvOS 14 , watchOS 7 , * )
213+ extension WithViewStore where State == Void , Content: Scene {
214+ /// Initializes a structure that transforms a store into an observable view store in order to
215+ /// compute scenes from equatable store state.
216+ ///
217+ /// - Parameters:
218+ /// - store: A store of equatable state.
219+ /// - content: A function that can generate content from a view store.
220+ public init (
221+ _ store: Store < State , Action > ,
222+ file: StaticString = #fileID,
223+ line: UInt = #line,
224+ @SceneBuilder content: @escaping ( ViewStore < State , Action > ) -> Content
225+ ) {
226+ self . init ( store, removeDuplicates: == , file: file, line: line, content: content)
176227 }
177- #endif
228+ }
0 commit comments