3

I'm building a graphics app for iOS. Here is my code.

class Group {

    /// All the shapes contained in the group
    public var shapes: CurrentValueSubject<[Shape], Never>

    /// The frame of the group
    var frame: CurrentValueSubject<CGRect, Never>

    /// The path to be calculated and displayed to users from the contained shapes
    var cgPath: CurrentValueSubject<CGPath, Never>
}

class Shape {
    var path: CurrentValueSubject<Path, Never>  = .init(Path())
}

struct Path {
    public var points = [CGPoint]()
}

So, here is what I want to do but don't know how to do it with Combine.

I want Group to observe it's own frame, shapes, and the path of it's shapes (I need to merge all this), so every time each of they change, I can calculate the new CGPath to display and assign it to the cgPath property (which will be observed by the View that draws everything).

Please, let me know if this is possible or if there is a better approach to all this.

Thanks in advance.

1 Answer 1

3

Using CombineLatest

Just add the @Published property wrapper to the properties you are interested in. Combine already has a predefined CombineLatest3 method to create a Publisher that you can subscribe to. Have fun with it.

import Foundation
import Combine
import CoreGraphics

class Group {

    init(shapes: [Shape], frame: CGRect, path: Path) {
        self.shapes = shapes
        self.frame = frame
        self.path = path
        self.observer = Publishers.CombineLatest3($shapes, $frame, $path)
            .sink(receiveCompletion: { _ in }, receiveValue: { (combined) in
                let (shapes, frame, path) = combined
                // do something
                print(shapes, frame, path)
            })
    }

    @Published var shapes: [Shape]
    @Published var frame: CGRect
    @Published var path: Path

    private var observer: AnyCancellable!
}

class Shape {
    var path: CurrentValueSubject<Path, Never>  = .init(Path())
}

struct Path {
    var points = [CGPoint]()
}

Notice how every change triggers the sink closure.

let group = Group(shapes: [Shape(), Shape()], frame: CGRect.zero, path: Path())
group.shapes = [Shape(), Shape(), Shape()]
group.frame = CGRect(x: 1, y: 1, width: 1, height: 1)
group.path.points = [CGPoint(x: 1, y: 1)]
Sign up to request clarification or add additional context in comments.

4 Comments

Does the group.path.points assignment line also trigger the observers??
Yes, I tested it in a playground. Note that Path is a struct. I would not be triggered if it was a class.
Exactly right. Classes won’t propagate mutations up the dependency chain.
After all, I ended up changing my architecture, I went for something more simple, but you guys gave me some great ideas and the correct answers for my question. Thanks a lot!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.