Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Usbergo committed Mar 15, 2020
1 parent 0f9a92b commit 9f8989a
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 71 deletions.
6 changes: 6 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import PackageDescription

let package = Package(
name: "Proxy",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13),
.watchOS(.v6)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img src="https://raw.githubusercontent.com/alexdrone/Proxy/master/Docs/logo.png" width=150 alt="Proxy" align=right />

Swift package that implements mutable and immutable *proxy objects* through `@dynamicMemberLookup`,
and lazy proxy-based object builders.
and lazy proxy-based object builders (`Partials`).

#### TL;DR

Expand All @@ -23,10 +23,10 @@ mutableProxy.label // "Initial"
mutableProxy.label = "New"
mutableProxy.label // "New"

var proxyBuilder = ProxyBuilder(createInstanceClosure: { Foo() })
proxyBuilder.label = "Bar"
proxyBuilder.number = 1
let obj = proxyBuilder.build()
var partial = Partial(createInstanceClosure: { Foo() })
partial.label = "Bar"
partial.number = 1
let obj = partial.build()
obj.label // "Bar"
obj.number // 1

Expand Down
33 changes: 12 additions & 21 deletions Sources/Proxy/ImmutableProxyRef.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import Foundation

#if canImport(Combine)
import Combine
#endif
import Combine

public protocol ImmutableProxyProtocol {
associatedtype ProxyType

/// The wrapped proxied object.
var wrappedValue: ProxyType { get set }
}
Expand All @@ -18,45 +14,40 @@ extension ImmutableProxyProtocol {
}
}

@available(OSX 10.15, iOS 13.0, *)
@dynamicMemberLookup
@propertyWrapper
open class ImmutableProxyRef<T>:
ImmutableProxyProtocol, AnySubscription, ObservableObject, PropertyObservableObject
{

ImmutableProxyProtocol,
AnySubscription,
ObservableObject,
PropertyObservableObject {
// Observable internals.
public var objectWillChangeSubscriber: Cancellable?

public var propertyDidChangeSubscriber: Cancellable?
public var propertyDidChange = PassthroughSubject<AnyPropertyChangeEvent, Never>()

open var wrappedValue: T

/// Constructs a new proxy for the object passed as argument.
/// Constructs a new read-only proxy for the object passed as argument.
init(of object: T) {
wrappedValue = object
}
}

@available(OSX 10.15, iOS 13.0, *)
extension ImmutableProxyRef where T: PropertyObservableObject {
/// Forwards the `ObservableObject.objectWillChangeSubscriber` to this proxy.
func propagatePropertyObservableObject() {
propertyDidChangeSubscriber
= wrappedValue.propertyDidChange.sink { [weak self] change in
self?.propertyDidChange.send(change)
}
propertyDidChangeSubscriber = wrappedValue.propertyDidChange.sink { [weak self] change in
self?.propertyDidChange.send(change)
}
}
}

@available(OSX 10.15, iOS 13.0, *)
extension ImmutableProxyRef where T: ObservableObject {
/// Forwards the `ObservableObject.objectWillChangeSubscriber` to this proxy.
func propagateObservableObject() {
objectWillChangeSubscriber
= wrappedValue.objectWillChange.sink { [weak self] change in
self?.objectWillChange.send()
}
objectWillChangeSubscriber = wrappedValue.objectWillChange.sink { [weak self] change in
self?.objectWillChange.send()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import Foundation
import Combine

#if canImport(Combine)
import Combine
#endif

@available(OSX 10.15, iOS 13.0, *)
public protocol PropertyObservableObject: class {
/// A publisher that emits when an object property has changed.
var propertyDidChange: PassthroughSubject<AnyPropertyChangeEvent, Never> { get }
}

@available(OSX 10.15, iOS 13.0, *)
public protocol AnySubscription: class {
/// Used to subscribe to any `ObservableObject`.
var objectWillChangeSubscriber: Cancellable? { get set }

/// Used to subscribe to any `PropertyObservableObject`.
var propertyDidChangeSubscriber: Cancellable? { get set }
}
Expand All @@ -23,10 +17,8 @@ public protocol AnySubscription: class {
public struct AnyPropertyChangeEvent {
/// The proxy's wrapped value.
public let object: Any

/// The mutated keyPath.
public let keyPath: AnyKeyPath?

/// Returns a new `allChanged` event.
public static func allChangedEvent<T>(object: T) -> AnyPropertyChangeEvent {
return AnyPropertyChangeEvent(object: object, keyPath: nil)
Expand Down
15 changes: 5 additions & 10 deletions Sources/Proxy/Builder.swift → Sources/Proxy/Partial.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import Foundation

// MARK: - ProxyBuilder

public protocol ProxyBuilderProtocol {
public protocol PartialProtocol {
associatedtype ObjectType

/// The initial instance that is going to be used by the builder.
var createInstanceClosure: () -> ObjectType { get }

/// All of the `set` commands that will performed by this builder.
/// All of the `set` commands that will performed once the object is built.
var keypathSetValueDictionary: [AnyKeyPath: (inout ObjectType) -> Void] { get set }

/// All of the values currently set.
/// All of the values currently set in this partial.
var keypathGetValueDictionary: [AnyKeyPath: Any] { get set }
}

extension ProxyBuilderProtocol {
extension PartialProtocol {
/// Use `@dynamicMemberLookup` keypath subscript to store the object configuration and postpone
/// the object construction.
public subscript<T>(dynamicMember keyPath: WritableKeyPath<ObjectType, T>) -> T? {
Expand Down Expand Up @@ -46,7 +41,7 @@ extension ProxyBuilderProtocol {
}

@dynamicMemberLookup
open class ProxyBuilder<T>: ProxyBuilderProtocol {
open class Partial<T>: PartialProtocol {
public let createInstanceClosure: () -> T
public var keypathSetValueDictionary: [AnyKeyPath: (inout T) -> Void] = [:]
public var keypathGetValueDictionary: [AnyKeyPath: Any] = [:]
Expand Down
42 changes: 17 additions & 25 deletions Sources/Proxy/Proxy.swift → Sources/Proxy/ProxyRef.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import Foundation

#if canImport(Combine)
import Combine
#endif
import Combine

public protocol MutableProxyProtocol: ImmutableProxyProtocol {
/// The proxied object is about to be mutated with value `value`.
func willSetValue<V>(keyPath: KeyPath<ProxyType, V>, value: V)

/// The proxied object was mutated with value `value`.
func didSetValue<V>(keyPath: KeyPath<ProxyType, V>, value: V)
}
Expand All @@ -26,16 +22,16 @@ extension MutableProxyProtocol {
}
}

@available(OSX 10.15, iOS 13.0, *)
@dynamicMemberLookup
@propertyWrapper
open class ProxyRef<T>:
MutableProxyProtocol, AnySubscription, ObservableObject, PropertyObservableObject, NSCopying
{

MutableProxyProtocol,
AnySubscription,
ObservableObject,
PropertyObservableObject,
NSCopying {
// Observable internals.
public var objectWillChangeSubscriber: Cancellable?

public var propertyDidChangeSubscriber: Cancellable?
public var propertyDidChange = PassthroughSubject<AnyPropertyChangeEvent, Never>()

Expand All @@ -51,51 +47,47 @@ open class ProxyRef<T>:
return ProxyRef(of: wrappedValue)
}

open func willSetValue<V>(keyPath: KeyPath<T, V>, value: V) {
// Subclasses to implement this method.
}
/// Subclasses to override this method.
/// - note: Remember to invoke the `super` implementation.
open func willSetValue<V>(keyPath: KeyPath<T, V>, value: V) { }

/// Subclasses to override this method.
/// - note: Remember to invoke the `super` implementation.
open func didSetValue<V>(keyPath: KeyPath<T, V>, value: V) {
objectWillChange.send()
propertyDidChange.send(AnyPropertyChangeEvent(object: self.wrappedValue, keyPath: keyPath))
// Subclasses to implement this method.
}
}

@available(OSX 10.15, iOS 13.0, *)
extension ProxyRef: Equatable where T: Equatable {
/// Two `MutableObservableProxy` are considered equal if they are proxies for the same object.
public static func == (lhs: ProxyRef<T>, rhs: ProxyRef<T>) -> Bool {
return lhs.wrappedValue == rhs.wrappedValue
}
}

@available(OSX 10.15, iOS 13.0, *)
extension ProxyRef: Hashable where T: Hashable {
/// Hashes the essential components of this value by feeding them into the given hasher.
public func hash(into hasher: inout Hasher) {
return wrappedValue.hash(into: &hasher)
}
}

@available(OSX 10.15, iOS 13.0, *)
extension ProxyRef where T: PropertyObservableObject {
/// Forwards the `ObservableObject.objectWillChangeSubscriber` to this proxy.
func propagatePropertyObservableObject() {
propertyDidChangeSubscriber
= wrappedValue.propertyDidChange.sink { [weak self] change in
self?.propertyDidChange.send(change)
}
propertyDidChangeSubscriber = wrappedValue.propertyDidChange.sink { [weak self] change in
self?.propertyDidChange.send(change)
}
}
}

@available(OSX 10.15, iOS 13.0, *)
extension ProxyRef where T: ObservableObject {
/// Forwards the `ObservableObject.objectWillChangeSubscriber` to this proxy.
func propagateObservableObject() {
objectWillChangeSubscriber
= wrappedValue.objectWillChange.sink { [weak self] change in
self?.objectWillChange.send()
}
objectWillChangeSubscriber = wrappedValue.objectWillChange.sink { [weak self] change in
self?.objectWillChange.send()
}
}
}
2 changes: 1 addition & 1 deletion Tests/ProxyTests/ProxyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ final class ProxyTests: XCTestCase {
}

func testProxyBuilder() {
var builder = ProxyBuilder(createInstanceClosure: { Foo() })
var builder = Partial(createInstanceClosure: { Foo() })
builder.label = "New"
builder.number = 1
XCTAssert(builder.label == "New")
Expand Down

0 comments on commit 9f8989a

Please sign in to comment.