Consider the following synthetic scenario:
import Combine
let publisher1 = PassthroughSubject<Int, Never>().eraseToAnyPublisher()
let publisher2 = PassthroughSubject<Int, Never>()
publisher1.sink { value in
publisher2.send(value)
}
We have 2 publishers, I'd like to propagate any value of the publisher1 to the publisher2. The code I've shown does the job, but I'm interested whether there is a cleaner, declarative approach to this.
Note: both publisher1 and publisher2 are of the same type.
Details on the problem
publisher2 is a part of the API that is exposed by the "core" class. The "core" class has a "has a" relationship to a "child" class which in turn has a publisher1 as it's API.
Over the lifetime of the "core" class, the "child" class can be allocated and deallocated multiple times. This should be transparent to the subscribers of the "core" class which won't need to subscribe to the publisher2.
Code:
import UIKit
import Combine
class ChildClass {
let publisher1 = PassthroughSubject<Int, Never>().eraseToAnyPublisher()
}
class CoreClass {
let publisher2 = PassthroughSubject<Int, Never>()
private var childClass: ChildClass?
init() {
allocateChildClass()
}
func allocateChildClass() {
let child = ChildClass()
childClass = child
// Any way to simplify this?
child.publisher1.sink { value in
publisher2.send(value)
}
}
func deallocateChildClass() {
childClass = nil
}
}
class Client {
let core = CoreClass()
init() {
// Doesn't care about allocating or deallocating of the ChildClass
core.publisher2.sink { value in
print(value)
}
}
}
Trying to subscribe one publisher to another doesn't work:
publisher2
.subscribe(publisher1)
No exact matches in call to instance method 'subscribe'
It is a delightful feature of PassthroughSubject that it is both a publisher and an operator: a Subject can be chained directly to a pipeline.
So just say
publisher1.subscribe(publisher2)
and you're all set.
Related
I am setting up a sink like so:
name.publisher
.removeDuplicates()
.receive(on: RunLoop.main)
.sink { [weak self] config in
guard let self = self else { return }
// this closure gets called right away at setup even though the #Published property `name` was already setup and did not change
}.store(in: &subscribers)
The property is declared like so in an observable object:
#Published var name:String = ""
So, I'm obviously missing something here. Why is sink called once at setup even though name did not change? I can avoid this behavior by using the dropFirst() operator but, I'd like to understand why the closure is always called once immediately after setup?
Why is that?
Here's a playground that uses debugPrint to show you what you get from name.publisher:
import UIKit
import Combine
//Holds the score
class ViewModel : ObservableObject {
#Published var name = "0"
}
let viewModel = ViewModel()
debugPrint(viewModel.name.publisher)
What you get is
Combine.Publishers.Sequence<Swift.String, Swift.Never>(sequence: "0")
So you get a sequence publisher that has a single item, "0". It will publish that value once and the sequence will end. Each time a subscriber attaches to that sequence it will get all the items in the sequence (there's only one) and the end.
This is probably not what you want.
Instead I think you want to use $name to access the published property:
import UIKit
import Combine
//Holds the score
class ViewModel : ObservableObject {
#Published var name = "0"
}
let viewModel = ViewModel()
let subscription = viewModel.$name.sink { print($0) }
viewModel.name = "Alex"
When you subscribe to the published property you will still get a posted event that is the current value of the property. However, by using $name you are attaching to a stream that will send you the current value on subscription AND each subsequent value.
Context, an iOS app using UIKit and MVC.
Model A has a singleton object, and one of its property value (foo in this case) can be changed during the runtime. Model B has a property, Model C, that is initialized using the property value of Model A.
class ModelA {
private(set) var foo: CustomObjectClassName
static let shared = ModelA()
}
class ModelB {
private var bar: ModelC
init() {
self.bar = ModelC(ModelA.shared.foo)
}
// TODO: Observer `foo` value change in Model A and then
// reinit ModelC to replace the old `bar` object
}
What is the common design pattern or mechanism that should be used to let Model B know about property change in Model A and re-initialize its own property?
I found two patterns that can be used; however, the more I read about them, they seem to be designed for communication between Model and Controller.
NotificationCenter (Notification & Observer)
Key-value Observing
Related Information
Using Key-Value Observing in Swift, https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift
NotificationCenter, https://developer.apple.com/documentation/foundation/notificationcenter
Thanks for El Tomato comment, delegation can be a pattern be used.
A delegation pattern would work since in my case Model A is only used by a single entity Model B.
protocol ModelADelegate {
func fooDidChange() -> Void
}
class ModelA {
public var delegate: ModelADelegate?
private(set) var foo: CustomObjectClassName {
didSet {
delegate.fooDidChange()
}
}
static let shared = ModelA()
}
class ModelB, ModelADelegate {
private var bar: ModelC
init() {
ModelA.shared.delegate = self
self.bar = ModelC(ModelA.shared.foo)
}
func fooDidChange() {
self.bar = ModelC(ModelA.shared.foo)
}
}
I have a singleton class:
class SomeManager {
static let sharedInstance = SomeManager()
let serverService = SomerServerService()
let musicService = SomeMusicService()
}
I try to use
class SomeMusicService
{
let serverService = SomeManager.sharedInstance.serverService //here seems I get cycle.
}
Should I use lazy or some other initialization.
As you can see let musicService = SomeMusicService() initializes an object and then in the same object SomeMusicService it tries to call sharedInstance of SomeManager singleton to get another service at start.
So this is a full listing:
import Foundation
class ServerService
{
func downloadMusic()
{
print("Download music and play it after that.")
}
}
class MusicService
{
let serverService = Singleton.shared.serverService
func playMusic()
{
serverService.downloadMusic()
}
}
class Singleton
{
static let shared = Singleton()
let serverService = ServerService()
let musicService = MusicService()
}
let s = Singleton.shared
print("Hello, World!")
We never get print("Hello, World!") line to be invoked.
You could use weak to avoid the retain cycle, but the better answer is a computed property:
class SomeMusicService {
var serverService: SomeService { return SomeManager.sharedInstance.serverService }
}
I see from your updated code what the cycle is. Here's how it plays out:
Call Singleton.shared
Begin to construct Singleton
Construct ServerService (for serverService property)
Begin to construct MusicService (for musicService property)
Call Singleton.shared (for serverService property)
Block waiting for Singleton.shared to complete
The program is now deadlocked waiting on itself.
The right answer is to use a computed property so that there is no need to call Singleton.shared during construction. A lazy property would work as well, but seems a lot of trouble for this (and risks creating retain loops between the services).
I'm trying to create a way to build compassable objects in Swift. I feel like I'm almost there with what I have but it's still not 100% correct.
What I'm aiming for is to have a FlowController object that can create our UIViewControllers and then give them any of the dependencies that they need.
What I'd also like to do is make this work as loosely as possible.
I have a small example here that works but is not ideal. I'll explain...
Here are two objects that can be used as components... Wallet and User.
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
class User {
func sayHello() {
Print("Hello, world!")
}
}
We then define a Component enum that has cases for each of these...
enum Component {
case Wallet
case User
}
... And a protocol that defines a method requiresComponents that returns an array of Components.
This is where the problem arises. In order for the "factory object" to put the components into a Composable object we need to define the user and wallet properties in the protocol also.
protocol Composable {
var user: User? {get set}
var wallet: Wallet? {get set}
func requiresComponents() -> [Component]
}
In an attempt to make these properties "optional" (not Optional) I have defined an extension to the Composable protocol that defines these vars as nil.
extension Composable {
var user: User? {
get {return nil}
set {}
}
var wallet: Wallet? {
get {return nil}
set {}
}
}
Now I declare the class that I want to make Composable. As you can see it requires the User component and declares the variable.
class SomeComposableClass: Composable {
var user: User?
func requiresComponents() -> [Component] {
return [.User]
}
}
Now the FlowController that will create these and add the components to them. You can see here that I have had to take the object, create a local var version of it and then return the updated object. I think this is because it doesn't know the type of objects that will be conforming to the protocol so the parameter can't be mutated.
class FlowController {
func addComponents<T: Composable>(toComposableObject object: T) -> T {
var localObject = object
for component in object.requiresComponents() {
switch component {
case .Wallet:
localObject.wallet = Wallet()
print("Wallet")
case .User:
localObject.user = User()
print("User")
}
}
return localObject
}
}
Here I create the objects.
let flowController = FlowController()
let composable = SomeComposableClass()
And here I add the components. In production this would be done all inside the FlowController.
flowController.addComponents(toComposableObject: composable) // prints "User" when adding the user component
compassable.user?.sayHello() // prints "Hello, world!"
As you can see, it works here. The user object is added.
However, as you can also see. Because I have declared the vars in the protocol the composable object also has a reference to a wallet component (although it will always be nil).
composable.wallet // nil
I feel like I'm about 95% of the way there with this but what I'd like to be able to do is improve how the properties are declared. What I'd like is for that last line... composable.wallet to be a compile error.
I could do this by moving the declaration of the properties out of the protocol but then I have the problem of not being able to add the properties to any object that conforms to the Composable protocol.
What would be awesome is for the factory object to be able to add the properties without relying on the declaration. Or even have some sort of guard that says "if this object has a property call user then add the user component to it". Or something like that.
If anyone knows how I could get the other 5% of this working it would be awesome. Like I said, this works, just not in an ideal way.
Thanks :D
Hacky Edit
Hmm... As a quick tacky, horrible, "no-one-should-do-this" edit. I have changed my protocol extension to be like this...
extension Composable {
var user: User? {
get {fatalError("Access user")}
set {fatalError("Set user")}
}
var wallet: Wallet? {
get {fatalError("Access wallet")}
set {fatalError("Set waller")}
}
}
Now at least the program will crash if I try to access a variable I have not defined. But it's still not ideal.
Edit after reading Daniel's blog
OK, I think I've done what I wanted. Just not sure that it's exactly Swifty. Although, I also think it might be. Looking for a second opinion :)
So, my components and protocols have become this...
// these are unchanged
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
// each component gets a protocol
protocol WalletComposing {
var wallet: Wallet? {get set}
}
class User {
func sayHello() {
print("Hello, world!")
}
}
protocol UserComposing {
var user: User? {get set}
}
Now the factory method has changed...
// this is the bit I'm unsure about.
// I now have to check for conformance to each protocol
// and add the components accordingly.
// does this look OK?
func addComponents(toComposableObject object: AnyObject) {
if var localObject = object as? UserComposing {
localObject.user = User()
print("User")
}
if var localObject = object as? WalletComposing {
localObject.wallet = Wallet()
print("Wallet")
}
}
This allows me to do this...
class SomeComposableClass: UserComposing {
var user: User?
}
class OtherClass: UserComposing, WalletComposing {
var user: User?
var wallet: Wallet?
}
let flowController = FlowController()
let composable = SomeComposableClass()
flowController.addComponents(toComposableObject: composable)
composable.user?.sayHello()
composable.wallet?.topUp(amount: 20) // this is now a compile time error which is what I wanted :D
let other = OtherClass()
flowController.addComponents(toComposableObject: other)
other.user?.sayHello()
other.wallet?.topUp(amount: 10)
This seems like a good case for applying the Interface Segregation Principle
Specifically, rather than having a master Composable protocol, have many smaller protocols like UserComposing and WalletComposing. Then your concrete types that wish to compose those various traits, would just list their "requiredComponents" as protocols they conform to, i.e:
class FlowController : UserComposing, WalletComposing
I actually wrote a blog post that talks about this more extensively and gives more detailed examples at http://www.danielhall.io/a-swift-y-approach-to-dependency-injection
UPDATE:
Looking at the updated question and sample code, I would only suggest the following refinement:
Going back to your original design, it might make sense to define a base Composing protocol that requires any conforming class to create storage for composed traits as a dictionary. Something like this:
protocol Composing : class {
var traitDictionary:[String:Any] { get, set }
}
Then, use protocol extensions to add the actual composable trait as a computed property, which reduces the boilerplate of having to create those properties in every conforming class. This way any class can conform to any number of trait protocols without having to declare a specific var for each. Here's a more complete example implementation:
class FlowController {
static func userFor(instance:UserComposing) -> User {
return User()
}
static func walletFor(instance:WalletComposing) -> Wallet {
return Wallet()
}
}
protocol Composing : class {
var traitDictionary:[String:Any] { get, set }
}
protocol UserComposing : Composing {}
extension UserComposing {
var user:User {
get {
if let user = traitDictionary["user"] as? User {
return user
}
else {
let user = FlowController.userFor(self)
traitDictionary["user"] = user
return user
}
}
}
}
protocol WalletComposing {}
extension WalletComposing {
var wallet:Wallet {
get {
if let wallet = traitDictionary["wallet"] as? Wallet {
return wallet
}
else {
let wallet = FlowController.walletFor(self)
traitDictionary["wallet"] = wallet
return wallet
}
}
}
}
class AbstractComposing {
var traitDictionary = [String:Any]()
}
Not only does this get rid of those pesky optionals you have to unwrap everywhere, but it makes the injection of user and wallet implicit and automatic. That means that your classes will already have the right values for those traits even inside their own initializers, no need to explicitly pass each new instance to an instance of FlowController every time.
For example, your last code snippet would now become simply:
class SomeComposableClass: AbstractComposing, UserComposing {} // no need to declare var anymore
class OtherClass: AbstractComposing, UserComposing, WalletComposing {} //no vars here either!
let composable = SomeComposableClass() // No need to instantiate FlowController and pass in this instance
composable.user.sayHello() // No unwrapping the optional, this is guaranteed
composable.wallet.topUp(amount: 20) // this is still a compile time error which is what you wanted :D
let other = OtherClass() // No need to instantiate FlowController and pass in this instance
other.user.sayHello()
other.wallet.topUp(amount: 10) // It all "just works" ;)
I've defined a global struct with static properties with values I use in many of my view controllers, like this:
public struct AppGlobal {
static var currentUser = UserModel()
static let someManager = SomeManager()
// Prevent others from initializing
private init() { }
}
Then in my UIViewController, I can do something like this:
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
AppGlobal.currentUser.prop1 = "abc123"
AppGlobal.someManager.startUpdating()
}
}
This is obviously very convenient, but smells really bad. I believe dependency injection would come in handy here, but not sure how. Is there a more elegant alternative to creating the AppGlobal singleton properties?
I can't see why you need to access userModel or someManager through a global state (and yes — Singletons are just that).
Why not just set it where you need it?
"Dependency Injection" is a 25-dollar term for a 5-cent concept.
That's not to say that it's a bad term…
[…]
Dependency injection means
giving an object its instance variables. Really. That's it.
– James Shore: Dependency Injection Demystified
Either do it during constructing
class C {
let currentUser: UserModel
let someManager: SomeManager
init(currentUser:UserModel, someManger:SomeManager) {
self.currentUser = currentUser
self.someManager = someManager
}
}
or through properties. If you need to make sure that all properties are set, do something like this:
class MyController: UIViewController {
var currentUser: UserModel? {
didSet{
self.configureIfPossible()
}
}
var someManager: SomeManager?{
didSet{
self.configureIfPossible()
}
}
func configureIfPossible(){
if let currentUser = self.currentUser, someManager = self.someManager {
// configure
}
}
}
In my current project we have the policy that every dependency must be visible and configurable from outside the class.
An example:
class LibrarySegmentViewController: BaseContentViewController {
var userDefaults: NSUserDefaults?
var previousSorting : LibrarySortingOrder = .AZ
var sorting : LibrarySortingOrder {
set{
self.previousSorting = sorting
if let filterMode = self.filterMode {
self.userDefaults?.setInteger(newValue.rawValue, forKey: "\(filterMode)_LibrarySorting")
}
self.setupIfReady()
}
get{
if let filterMode = self.filterMode {
if let s = LibrarySortingOrder(rawValue: self.userDefaults!.integerForKey("\(filterMode)_LibrarySorting")) {
return s
}
}
return .Date
}
}
}
So as you can see, we even use properties to reference NSUserDefaults.standardUserDefaults(). We do this as we can pass in fresh instances during testing, without bigger mocking hassle.
And this is the most importing reason why not to use singletons directly: The dependencies are hidden and might bite you during testing and refactoring. Another example would be an API client singleton that is hidden in the code and performs unwanted networking requests during testing. If it is set from outside of the tested class you can just pass in a mocked networking client that does not perform any requests but returns test data.
So even if you use singletons, you should pass it in as a dependencies.
If this question is about global or not, you should see this thread :
What is so bad about singletons?
But if you want a better design for your implementation of a singleton you can try something like this :
class SingletonExample: NSObject {
static let sharedInstance: SingletonExample()
}
class OtherSingletonExample: NSObject {
static let sharedInstance: OtherSingletonExample()
}
Then you can use SingletonExample.sharedInstance and OtherSingletonExample.sharedInstance anywhere in your code.
The idea is to isolate one singleton from another and access it as a class attribute instead of creating a big global struct for anything.