@@ -2,6 +2,7 @@ import XCTest
22import Combine
33@testable import PublishedObject
44
5+ @available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
56class Outer : ObservableObject {
67 @PublishedObject var innerPublishedObject : Inner
78 @Published var innerPublished : Inner
@@ -12,6 +13,7 @@ class Outer: ObservableObject {
1213 }
1314}
1415
16+ @available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
1517class Inner : ObservableObject {
1618 @Published var value : Int
1719
@@ -20,6 +22,18 @@ class Inner: ObservableObject {
2022 }
2123}
2224
25+ @available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
26+ class OuterOptional : ObservableObject {
27+ @PublishedObject var innerPublishedObject : Inner ?
28+ @Published var innerPublished : Inner ?
29+
30+ init ( _ value: Int ? ) {
31+ self . innerPublishedObject = value. map ( Inner . init)
32+ self . innerPublished = value. map ( Inner . init)
33+ }
34+ }
35+
36+ @available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
2337final class PublishedObjectTests : XCTestCase {
2438 var cancellables : Set < AnyCancellable > = [ ]
2539
@@ -115,9 +129,97 @@ final class PublishedObjectTests: XCTestCase {
115129 outer. innerPublished. value = 3
116130 wait ( for: [ exp5] , timeout: 0.1 )
117131 }
132+
133+ func testOptionalWithValue( ) throws {
134+ let outer = OuterOptional ( 1 )
135+
136+ // Setting property on Inner from the optional init (This will only send an update when using @PublishedObject)
137+
138+ let exp5 = XCTestExpectation ( description: " outer.objectWillChange will be called " )
139+ outer. objectWillChange. first ( ) . sink { exp5. fulfill ( ) } . store ( in: & cancellables)
140+ outer. innerPublishedObject? . value = 3
141+ wait ( for: [ exp5] , timeout: 0.1 )
142+
143+ let exp6 = XCTestExpectation ( description: " outer.objectWillChange will NOT be called " )
144+ exp6. isInverted = true
145+ outer. objectWillChange. first ( ) . sink { exp6. fulfill ( ) } . store ( in: & cancellables)
146+ outer. innerPublished? . value = 3
147+ wait ( for: [ exp6] , timeout: 0.1 )
148+
149+ // Setting property on Outer (This will send an update with either @Published or @PublishedObject)
150+
151+ let exp1 = XCTestExpectation ( description: " outer.objectWillChange will be called " )
152+ outer. objectWillChange. first ( ) . sink { exp1. fulfill ( ) } . store ( in: & cancellables)
153+ outer. innerPublishedObject = Inner ( 2 )
154+ wait ( for: [ exp1] , timeout: 0.1 )
155+
156+ let exp2 = XCTestExpectation ( description: " outer.objectWillChange will be called " )
157+ outer. objectWillChange. first ( ) . sink { exp2. fulfill ( ) } . store ( in: & cancellables)
158+ outer. innerPublished = Inner ( 2 )
159+ wait ( for: [ exp2] , timeout: 0.1 )
160+
161+ // Setting property on Inner (This will only send an update when using @PublishedObject)
162+
163+ let exp3 = XCTestExpectation ( description: " outer.objectWillChange will be called " )
164+ outer. objectWillChange. first ( ) . sink { exp3. fulfill ( ) } . store ( in: & cancellables)
165+ outer. innerPublishedObject? . value = 3
166+ wait ( for: [ exp3] , timeout: 0.1 )
167+
168+ let exp4 = XCTestExpectation ( description: " outer.objectWillChange will NOT be called " )
169+ exp4. isInverted = true
170+ outer. objectWillChange. first ( ) . sink { exp4. fulfill ( ) } . store ( in: & cancellables)
171+ outer. innerPublished? . value = 3
172+ wait ( for: [ exp4] , timeout: 0.1 )
173+ }
174+
175+ func testOptionalWithoutValue( ) throws {
176+ let outer = OuterOptional ( nil )
177+
178+ // Setting property on Inner while it is nil
179+ // (this should never call objectWillChange because the Inner obj is not there so nothing is changed)
180+
181+ let exp5 = XCTestExpectation ( description: " uhhhhm " )
182+ exp5. isInverted = true
183+ outer. objectWillChange. first ( ) . sink { exp5. fulfill ( ) } . store ( in: & cancellables)
184+ outer. innerPublishedObject? . value = 3
185+ wait ( for: [ exp5] , timeout: 0.1 )
186+
187+ let exp6 = XCTestExpectation ( description: " uhhhhm " )
188+ exp6. isInverted = true
189+ outer. objectWillChange. first ( ) . sink { exp6. fulfill ( ) } . store ( in: & cancellables)
190+ outer. innerPublished? . value = 3
191+ wait ( for: [ exp6] , timeout: 0.1 )
192+
193+ // Setting property on Outer (This will send an update with either @Published or @PublishedObject)
194+
195+ let exp1 = XCTestExpectation ( description: " outer.objectWillChange will be called " )
196+ outer. objectWillChange. first ( ) . sink { exp1. fulfill ( ) } . store ( in: & cancellables)
197+ outer. innerPublishedObject = Inner ( 2 )
198+ wait ( for: [ exp1] , timeout: 0.1 )
199+
200+ let exp2 = XCTestExpectation ( description: " outer.objectWillChange will be called " )
201+ outer. objectWillChange. first ( ) . sink { exp2. fulfill ( ) } . store ( in: & cancellables)
202+ outer. innerPublished = Inner ( 2 )
203+ wait ( for: [ exp2] , timeout: 0.1 )
204+
205+ // Setting property on Inner (This will only send an update when using @PublishedObject)
206+
207+ let exp3 = XCTestExpectation ( description: " outer.objectWillChange will be called " )
208+ outer. objectWillChange. first ( ) . sink { exp3. fulfill ( ) } . store ( in: & cancellables)
209+ outer. innerPublishedObject? . value = 3
210+ wait ( for: [ exp3] , timeout: 0.1 )
211+
212+ let exp4 = XCTestExpectation ( description: " outer.objectWillChange will NOT be called " )
213+ exp4. isInverted = true
214+ outer. objectWillChange. first ( ) . sink { exp4. fulfill ( ) } . store ( in: & cancellables)
215+ outer. innerPublished? . value = 3
216+ wait ( for: [ exp4] , timeout: 0.1 )
217+ }
118218
119219 static var allTests = [
120220 ( " testObjectWillChange " , testObjectWillChange) ,
121221 ( " testProjectedValue " , testProjectedValue) ,
222+ ( " testOptionalWithValue " , testOptionalWithValue) ,
223+ ( " testOptionalWithoutValue " , testOptionalWithoutValue) ,
122224 ]
123225}
0 commit comments