diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5f82205..6fce731 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -37,7 +37,10 @@ jobs:
-scheme OpenCombine-Package \
-sdk macosx10.15 \
-derivedDataPath DerivedData \
+ | tee xcodebuild_build-for-testing.log \
| xcpretty
+ - store_artifacts:
+ path: xcodebuild_build-for-testing.log
- run:
name: Testing on macOS 10.15.0 with xcodebuild
command: |
@@ -46,7 +49,10 @@ jobs:
-scheme OpenCombine-Package \
-sdk macosx10.15 \
-derivedDataPath DerivedData \
+ | tee xcodebuild_test-without-building.log \
| xcpretty --report junit -o build/reports/results.xml
+ - store_artifacts:
+ path: xcodebuild_test-without-building.log
- store_test_results:
path: build/reports
- run:
@@ -72,7 +78,10 @@ jobs:
-scheme OpenCombine-Package \
-destination "platform=iOS Simulator,name=iPhone 11,OS=13.2.2" \
-derivedDataPath DerivedData \
+ | tee xcodebuild_build-for-testing.log \
| xcpretty
+ - store_artifacts:
+ path: xcodebuild_build-for-testing.log
- run:
name: Testing against Combine on iOS 13.2.2 with xcodebuild
command: |
@@ -81,7 +90,10 @@ jobs:
-scheme OpenCombine-Package \
-destination "platform=iOS Simulator,name=iPhone 11,OS=13.2.2" \
-derivedDataPath DerivedData \
+ | tee xcodebuild_test-without-building.log \
| xcpretty --report junit -o build/reports/results.xml
+ - store_artifacts:
+ path: xcodebuild_test-without-building.log
- store_test_results:
path: build/reports
@@ -123,7 +135,10 @@ jobs:
-scheme OpenCombine-Package \
-destination "platform=iOS Simulator,name=iPhone 4s,OS=9.3" \
-derivedDataPath DerivedData \
+ | tee xcodebuild_build-for-testing.log \
| xcpretty
+ - store_artifacts:
+ path: xcodebuild_build-for-testing.log
- run:
name: Testing on iOS 9.3 with xcodebuild
command: |
@@ -132,7 +147,10 @@ jobs:
-scheme OpenCombine-Package \
-destination "platform=iOS Simulator,name=iPhone 4s,OS=9.3" \
-derivedDataPath DerivedData \
+ | tee xcodebuild_test-without-building.log \
| xcpretty --report junit -o build/reports/results.xml
+ - store_artifacts:
+ path: xcodebuild_test-without-building.log
- store_test_results:
path: build/reports
- run:
diff --git a/RemainingCombineInterface.swift b/RemainingCombineInterface.swift
index 413ca6d..e9c4f5a 100644
--- a/RemainingCombineInterface.swift
+++ b/RemainingCombineInterface.swift
@@ -2094,68 +2094,3 @@ extension Publishers.Zip4 : Equatable where A : Equatable, B : Equatable, C : Eq
/// - rhs: Another value to compare.
public static func == (lhs: Publishers.Zip4, rhs: Publishers.Zip4) -> Bool
}
-
-/// A type of object with a publisher that emits before the object has changed.
-///
-/// By default an `ObservableObject` will synthesize an `objectWillChange`
-/// publisher that emits before any of its `@Published` properties changes:
-///
-/// class Contact: ObservableObject {
-/// @Published var name: String
-/// @Published var age: Int
-///
-/// init(name: String, age: Int) {
-/// self.name = name
-/// self.age = age
-/// }
-///
-/// func haveBirthday() -> Int {
-/// age += 1
-/// return age
-/// }
-/// }
-///
-/// let john = Contact(name: "John Appleseed", age: 24)
-/// john.objectWillChange.sink { _ in print("\(john.age) will change") }
-/// print(john.haveBirthday())
-/// // Prints "24 will change"
-/// // Prints "25"
-///
-public protocol ObservableObject : AnyObject {
-
- /// The type of publisher that emits before the object has changed.
- associatedtype ObjectWillChangePublisher : Publisher = ObservableObjectPublisher where Self.ObjectWillChangePublisher.Failure == Never
-
- /// A publisher that emits before the object has changed.
- var objectWillChange: Self.ObjectWillChangePublisher { get }
-}
-
-extension ObservableObject where Self.ObjectWillChangePublisher == ObservableObjectPublisher {
-
- /// A publisher that emits before the object has changed.
- public var objectWillChange: ObservableObjectPublisher { get }
-}
-
-/// The default publisher of an `ObservableObject`.
-final public class ObservableObjectPublisher : Publisher {
-
- /// The kind of values published by this publisher.
- public typealias Output = Void
-
- /// The kind of errors this publisher might publish.
- ///
- /// Use `Never` if this `Publisher` does not publish errors.
- public typealias Failure = Never
-
- public init()
-
- /// This function is called to attach the specified `Subscriber` to this `Publisher` by `subscribe(_:)`
- ///
- /// - SeeAlso: `subscribe(_:)`
- /// - Parameters:
- /// - subscriber: The subscriber to attach to this `Publisher`.
- /// once attached it can begin to receive values.
- final public func receive(subscriber: S) where S : Subscriber, S.Failure == ObservableObjectPublisher.Failure, S.Input == ObservableObjectPublisher.Output
-
- final public func send()
-}
diff --git a/Sources/OpenCombine/ObservableObject.swift b/Sources/OpenCombine/ObservableObject.swift
new file mode 100644
index 0000000..d8133a4
--- /dev/null
+++ b/Sources/OpenCombine/ObservableObject.swift
@@ -0,0 +1,85 @@
+//
+// ObservableObject.swift
+//
+//
+// Created by Sergej Jaskiewicz on 08/09/2019.
+//
+
+/// A type of object with a publisher that emits before the object has changed.
+///
+/// By default an `ObservableObject` will synthesize an `objectWillChange`
+/// publisher that emits before any of its `@Published` properties changes:
+///
+/// class Contact : ObservableObject {
+/// @Published var name: String
+/// @Published var age: Int
+///
+/// init(name: String, age: Int) {
+/// self.name = name
+/// self.age = age
+/// }
+///
+/// func haveBirthday() -> Int {
+/// age += 1
+/// }
+/// }
+///
+/// let john = Contact(name: "John Appleseed", age: 24)
+/// john.objectWillChange.sink { _ in print("will change") }
+/// print(john.haveBirthday)
+/// // Prints "will change"
+/// // Prints "25"
+///
+public protocol ObservableObject: AnyObject {
+
+ /// The type of publisher that emits before the object has changed.
+ associatedtype ObjectWillChangePublisher: Publisher = ObservableObjectPublisher
+ where ObjectWillChangePublisher.Failure == Never
+
+ /// A publisher that emits before the object has changed.
+ var objectWillChange: ObjectWillChangePublisher { get }
+}
+
+extension ObservableObject where ObjectWillChangePublisher == ObservableObjectPublisher {
+ // swiftlint:disable let_var_whitespace
+#if swift(>=5.1)
+ /// A publisher that emits before the object has changed.
+ @available(*, unavailable, message: """
+ The default implementation of objectWillChange is not available yet. \
+ It's being worked on in \
+ https://github.com/broadwaylamb/OpenCombine/pull/97
+ """)
+ public var objectWillChange: ObservableObjectPublisher {
+ fatalError("unimplemented")
+ }
+#else
+ public var objectWillChange: ObservableObjectPublisher {
+ return ObservableObjectPublisher()
+ }
+#endif
+ // swiftlint:enable let_var_whitespace
+}
+
+/// The default publisher of an `ObservableObject`.
+public final class ObservableObjectPublisher: Publisher {
+
+ public typealias Output = Void
+
+ public typealias Failure = Never
+
+ private let subject: PassthroughSubject
+
+ public init() {
+ subject = .init()
+ }
+
+ public func receive(subscriber: Downstream)
+ where Downstream.Input == Void, Downstream.Failure == Never
+ {
+ subject.subscribe(subscriber)
+ }
+
+ public func send() {
+ subject.send()
+ }
+}
diff --git a/Sources/OpenCombine/Published.swift b/Sources/OpenCombine/Published.swift
index 038ce52..510d795 100644
--- a/Sources/OpenCombine/Published.swift
+++ b/Sources/OpenCombine/Published.swift
@@ -14,16 +14,18 @@
/// of the property first.
/// Note that the `@Published` property is class-constrained.
/// Use it with properties of classes, not with non-class types like structures.
+@available(swift, introduced: 5.1)
@propertyWrapper
public struct Published {
- /// Initialize the storage of the Published
- /// property as well as the corresponding `Publisher`.
+ /// Initialize the storage of the `Published` property as well as the corresponding
+ /// `Publisher`.
public init(initialValue: Value) {
- value = initialValue
+ self.init(wrappedValue: initialValue)
}
- @available(*, unavailable)
+ /// Initialize the storage of the `Published` property as well as the corresponding
+ /// `Publisher`.
public init(wrappedValue: Value) {
value = wrappedValue
}
@@ -31,21 +33,10 @@ public struct Published {
/// A publisher for properties marked with the `@Published` attribute.
public struct Publisher: OpenCombine.Publisher {
- /// The kind of values published by this publisher.
public typealias Output = Value
- /// The kind of errors this publisher might publish.
- ///
- /// Use `Never` if this `Publisher` does not publish errors.
public typealias Failure = Never
- /// This function is called to attach the specified
- /// `Subscriber` to this `Publisher` by `subscribe(_:)`
- ///
- /// - SeeAlso: `subscribe(_:)`
- /// - Parameters:
- /// - subscriber: The subscriber to attach to this `Publisher`.
- /// once attached it can begin to receive values.
public func receive(subscriber: Downstream)
where Downstream.Input == Value, Downstream.Failure == Never
{
@@ -61,8 +52,12 @@ public struct Published {
private var value: Value
- /// The property that can be accessed with the
- /// `$` syntax and allows access to the `Publisher`
+ private var publisher: Publisher?
+
+ internal var objectWillChange: ObservableObjectPublisher?
+
+ /// The property that can be accessed with the `$` syntax and allows access to
+ /// the `Publisher`
public var projectedValue: Publisher {
mutating get {
if let publisher = publisher {
@@ -74,28 +69,35 @@ public struct Published {
}
}
- @available(*, unavailable, message:
- "@Published is only available on properties of classes")
-
+ // swiftlint:disable let_var_whitespace
+ @available(*, unavailable, message: """
+ @Published is only available on properties of classes
+ """)
public var wrappedValue: Value {
- get { value }
- set {
- value = newValue
- publisher?.subject.value = newValue
- }
+ get { fatalError() }
+ set { fatalError() } // swiftlint:disable:this unused_setter_value
}
+ // swiftlint:enable let_var_whitespace
- private var publisher: Publisher?
-
- @available(*, unavailable, message:
- "This subscript is unavailable in OpenCombine yet")
public static subscript(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath,
storage storageKeyPath: ReferenceWritableKeyPath>
) -> Value {
- get { fatalError() }
- set { fatalError() }
+ get {
+ return object[keyPath: storageKeyPath].value
+ }
+ set {
+ object[keyPath: storageKeyPath].objectWillChange?.send()
+ object[keyPath: storageKeyPath].publisher?.subject.send(newValue)
+ object[keyPath: storageKeyPath].value = newValue
+ }
+ // TODO: Benchmark and explore a possibility to use _modify
}
}
-#endif
+#else
+
+@available(swift, introduced: 5.1)
+public typealias Published = Never
+
+#endif // swift(>=5.1)
diff --git a/Tests/OpenCombineTests/Helpers/TrackingSubscriber.swift b/Tests/OpenCombineTests/Helpers/TrackingSubscriber.swift
index 1b50361..bc67db5 100644
--- a/Tests/OpenCombineTests/Helpers/TrackingSubscriber.swift
+++ b/Tests/OpenCombineTests/Helpers/TrackingSubscriber.swift
@@ -195,6 +195,18 @@ extension TrackingSubscriberBase where Value: Equatable {
}
}
+@available(macOS 10.15, iOS 13.0, *)
+extension TrackingSubscriberBase where Value == Void {
+ func assertHistoryEqual(_ expected: [Event],
+ file: StaticString = #file,
+ line: UInt = #line) {
+ assertHistoryEqual(expected,
+ valueComparator: { _, _ in true },
+ file: file,
+ line: line)
+ }
+}
+
@available(macOS 10.15, iOS 13.0, *)
extension TrackingSubscriberBase.Event {
func isEqual(to other: TrackingSubscriberBase.Event,
@@ -222,12 +234,23 @@ extension TrackingSubscriberBase.Event {
@available(macOS 10.15, iOS 13.0, *)
extension TrackingSubscriberBase.Event: Equatable where Value: Equatable {
- static func == (lhs: TrackingSubscriberBase.Event,
- rhs: TrackingSubscriberBase.Event) -> Bool {
+ static func == (lhs: TrackingSubscriberBase.Event,
+ rhs: TrackingSubscriberBase.Event) -> Bool {
return lhs.isEqual(to: rhs, valueComparator: ==)
}
}
+@available(macOS 10.15, iOS 13.0, *)
+extension TrackingSubscriberBase.Event where Value == Void {
+
+ static var signal: TrackingSubscriberBase.Event { return .value(()) }
+
+ static func == (lhs: TrackingSubscriberBase.Event,
+ rhs: TrackingSubscriberBase.Event) -> Bool {
+ return lhs.isEqual(to: rhs, valueComparator: { _, _ in true })
+ }
+}
+
@available(macOS 10.15, iOS 13.0, *)
typealias TrackingSubject = TrackingSubjectBase