Observable implementation

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
1
down vote

favorite
1












I have implemented a simple Observable class. I think that there is still room for improvement. Especially the fact that I have separate array to observe results and values what if I want to observe errors for example, I think that this solution can be improved to be scalable. Also I'm not sure who is responsible for the threading the class implementation or the caller.



the desired result is to have an interface that allows:



1) Observable declaration:



private let isLoadingObservable = Observable<Bool>(false)



2) Update value:



self.isLoadingObservable.value = true



3) Observe values changes:



override func viewDidLoad() 
super.viewDidLoad()

isLoadingObservable.observeValues(on: self) isLoading in
print(isLoading)




Also in case where failure is possible (network call for example) we can user Result :



1) Observable declaration:



private let dataObservable = Observable<[User]>()



2) Update value:



dataObservable.result = Result.failure(URLError.badURL)



3) Observe result changes:



 dataObservable.observeResults(on: self) result in
switch result
case .success(let value):
print(value)
case .failure(let error):
print(error)




where we have



struct User 
let name: String


enum ServerError: Error
case invalidDataError



The implementation:



import Foundation

public enum Result<Value>
case success(Value)
case failure(Error)

var value: Value?
switch self
case .success(let value):
return value
case .failure:
return nil





class ResultObserver<Value>
typealias ResultObserverBlock = (Result<Value>) -> Void

weak var observer: AnyObject?
let block: ResultObserverBlock

init(observer: AnyObject, block: @escaping ResultObserverBlock)
self.observer = observer
self.block = block



class ValueObserver<Value>
typealias ValueObserverBlock = (Value) -> Void

weak var observer: AnyObject?
let block: ValueObserverBlock

init(observer: AnyObject, block: @escaping ValueObserverBlock)
self.observer = observer
self.block = block



public class Observable<Value>

typealias ResultObserverBlock = (Result<Value>) -> Void
typealias ValueObserverBlock = (Value) -> Void

//MARK: - Private properties
private var valueObservers = [ValueObserver<Value>]()
private var resultObservers = [ResultObserver<Value>]()

//MARK: - Public properties
public var result : Result<Value>
didSet
self.notifyObservers()



public var value: Value?
get
return self.result.value

set
if let value = newValue
self.result = Result.success(value)




//MARK: - Struct lifecycle
public init(_ result: Result<Value>)
self.result = result


public convenience init(_ value: Value)
self.init(Result.success(value))


public convenience init(_ error: Error)
self.init(Result.failure(error))


//MARK: - Observation
func observeResults(on observer: AnyObject, observerBlock: @escaping ResultObserverBlock)
self.resultObservers.append(ResultObserver(observer: observer, block: observerBlock))
observerBlock(result)


func observeValues(on observer: AnyObject, observerBlock: @escaping ValueObserverBlock)
self.valueObservers.append(ValueObserver(observer: observer, block: observerBlock))
if let value = value
observerBlock(value)



func remove(observer: AnyObject)
self.resultObservers = self.resultObservers.filter($0.observer !== observer)
self.valueObservers = self.valueObservers.filter($0.observer !== observer)


//MARK: - Helpers
private func notifyObservers()
for observer in self.valueObservers
if let value = value
observer.block(value)


for observer in self.resultObservers
observer.block(result)









share|improve this question





















  • If Result is the only thing that you need from Alamofire then I would suggest to include the definition here directly, to make your code independent of an (otherwise unrelated) framework.
    – Martin R
    Jan 24 at 12:35










  • @MartinR done, hope that it's easier to be reviewed now
    – iOSGeek
    Jan 24 at 12:38










  • That does not compile, there are several errors (related to Result<Value> having too few parameters). – Also a (minimal) main program demonstrating the usage might be helpful.
    – Martin R
    Jan 24 at 12:42











  • @MartinR now it compiles and I have added usage information
    – iOSGeek
    Jan 24 at 13:13
















up vote
1
down vote

favorite
1












I have implemented a simple Observable class. I think that there is still room for improvement. Especially the fact that I have separate array to observe results and values what if I want to observe errors for example, I think that this solution can be improved to be scalable. Also I'm not sure who is responsible for the threading the class implementation or the caller.



the desired result is to have an interface that allows:



1) Observable declaration:



private let isLoadingObservable = Observable<Bool>(false)



2) Update value:



self.isLoadingObservable.value = true



3) Observe values changes:



override func viewDidLoad() 
super.viewDidLoad()

isLoadingObservable.observeValues(on: self) isLoading in
print(isLoading)




Also in case where failure is possible (network call for example) we can user Result :



1) Observable declaration:



private let dataObservable = Observable<[User]>()



2) Update value:



dataObservable.result = Result.failure(URLError.badURL)



3) Observe result changes:



 dataObservable.observeResults(on: self) result in
switch result
case .success(let value):
print(value)
case .failure(let error):
print(error)




where we have



struct User 
let name: String


enum ServerError: Error
case invalidDataError



The implementation:



import Foundation

public enum Result<Value>
case success(Value)
case failure(Error)

var value: Value?
switch self
case .success(let value):
return value
case .failure:
return nil





class ResultObserver<Value>
typealias ResultObserverBlock = (Result<Value>) -> Void

weak var observer: AnyObject?
let block: ResultObserverBlock

init(observer: AnyObject, block: @escaping ResultObserverBlock)
self.observer = observer
self.block = block



class ValueObserver<Value>
typealias ValueObserverBlock = (Value) -> Void

weak var observer: AnyObject?
let block: ValueObserverBlock

init(observer: AnyObject, block: @escaping ValueObserverBlock)
self.observer = observer
self.block = block



public class Observable<Value>

typealias ResultObserverBlock = (Result<Value>) -> Void
typealias ValueObserverBlock = (Value) -> Void

//MARK: - Private properties
private var valueObservers = [ValueObserver<Value>]()
private var resultObservers = [ResultObserver<Value>]()

//MARK: - Public properties
public var result : Result<Value>
didSet
self.notifyObservers()



public var value: Value?
get
return self.result.value

set
if let value = newValue
self.result = Result.success(value)




//MARK: - Struct lifecycle
public init(_ result: Result<Value>)
self.result = result


public convenience init(_ value: Value)
self.init(Result.success(value))


public convenience init(_ error: Error)
self.init(Result.failure(error))


//MARK: - Observation
func observeResults(on observer: AnyObject, observerBlock: @escaping ResultObserverBlock)
self.resultObservers.append(ResultObserver(observer: observer, block: observerBlock))
observerBlock(result)


func observeValues(on observer: AnyObject, observerBlock: @escaping ValueObserverBlock)
self.valueObservers.append(ValueObserver(observer: observer, block: observerBlock))
if let value = value
observerBlock(value)



func remove(observer: AnyObject)
self.resultObservers = self.resultObservers.filter($0.observer !== observer)
self.valueObservers = self.valueObservers.filter($0.observer !== observer)


//MARK: - Helpers
private func notifyObservers()
for observer in self.valueObservers
if let value = value
observer.block(value)


for observer in self.resultObservers
observer.block(result)









share|improve this question





















  • If Result is the only thing that you need from Alamofire then I would suggest to include the definition here directly, to make your code independent of an (otherwise unrelated) framework.
    – Martin R
    Jan 24 at 12:35










  • @MartinR done, hope that it's easier to be reviewed now
    – iOSGeek
    Jan 24 at 12:38










  • That does not compile, there are several errors (related to Result<Value> having too few parameters). – Also a (minimal) main program demonstrating the usage might be helpful.
    – Martin R
    Jan 24 at 12:42











  • @MartinR now it compiles and I have added usage information
    – iOSGeek
    Jan 24 at 13:13












up vote
1
down vote

favorite
1









up vote
1
down vote

favorite
1






1





I have implemented a simple Observable class. I think that there is still room for improvement. Especially the fact that I have separate array to observe results and values what if I want to observe errors for example, I think that this solution can be improved to be scalable. Also I'm not sure who is responsible for the threading the class implementation or the caller.



the desired result is to have an interface that allows:



1) Observable declaration:



private let isLoadingObservable = Observable<Bool>(false)



2) Update value:



self.isLoadingObservable.value = true



3) Observe values changes:



override func viewDidLoad() 
super.viewDidLoad()

isLoadingObservable.observeValues(on: self) isLoading in
print(isLoading)




Also in case where failure is possible (network call for example) we can user Result :



1) Observable declaration:



private let dataObservable = Observable<[User]>()



2) Update value:



dataObservable.result = Result.failure(URLError.badURL)



3) Observe result changes:



 dataObservable.observeResults(on: self) result in
switch result
case .success(let value):
print(value)
case .failure(let error):
print(error)




where we have



struct User 
let name: String


enum ServerError: Error
case invalidDataError



The implementation:



import Foundation

public enum Result<Value>
case success(Value)
case failure(Error)

var value: Value?
switch self
case .success(let value):
return value
case .failure:
return nil





class ResultObserver<Value>
typealias ResultObserverBlock = (Result<Value>) -> Void

weak var observer: AnyObject?
let block: ResultObserverBlock

init(observer: AnyObject, block: @escaping ResultObserverBlock)
self.observer = observer
self.block = block



class ValueObserver<Value>
typealias ValueObserverBlock = (Value) -> Void

weak var observer: AnyObject?
let block: ValueObserverBlock

init(observer: AnyObject, block: @escaping ValueObserverBlock)
self.observer = observer
self.block = block



public class Observable<Value>

typealias ResultObserverBlock = (Result<Value>) -> Void
typealias ValueObserverBlock = (Value) -> Void

//MARK: - Private properties
private var valueObservers = [ValueObserver<Value>]()
private var resultObservers = [ResultObserver<Value>]()

//MARK: - Public properties
public var result : Result<Value>
didSet
self.notifyObservers()



public var value: Value?
get
return self.result.value

set
if let value = newValue
self.result = Result.success(value)




//MARK: - Struct lifecycle
public init(_ result: Result<Value>)
self.result = result


public convenience init(_ value: Value)
self.init(Result.success(value))


public convenience init(_ error: Error)
self.init(Result.failure(error))


//MARK: - Observation
func observeResults(on observer: AnyObject, observerBlock: @escaping ResultObserverBlock)
self.resultObservers.append(ResultObserver(observer: observer, block: observerBlock))
observerBlock(result)


func observeValues(on observer: AnyObject, observerBlock: @escaping ValueObserverBlock)
self.valueObservers.append(ValueObserver(observer: observer, block: observerBlock))
if let value = value
observerBlock(value)



func remove(observer: AnyObject)
self.resultObservers = self.resultObservers.filter($0.observer !== observer)
self.valueObservers = self.valueObservers.filter($0.observer !== observer)


//MARK: - Helpers
private func notifyObservers()
for observer in self.valueObservers
if let value = value
observer.block(value)


for observer in self.resultObservers
observer.block(result)









share|improve this question













I have implemented a simple Observable class. I think that there is still room for improvement. Especially the fact that I have separate array to observe results and values what if I want to observe errors for example, I think that this solution can be improved to be scalable. Also I'm not sure who is responsible for the threading the class implementation or the caller.



the desired result is to have an interface that allows:



1) Observable declaration:



private let isLoadingObservable = Observable<Bool>(false)



2) Update value:



self.isLoadingObservable.value = true



3) Observe values changes:



override func viewDidLoad() 
super.viewDidLoad()

isLoadingObservable.observeValues(on: self) isLoading in
print(isLoading)




Also in case where failure is possible (network call for example) we can user Result :



1) Observable declaration:



private let dataObservable = Observable<[User]>()



2) Update value:



dataObservable.result = Result.failure(URLError.badURL)



3) Observe result changes:



 dataObservable.observeResults(on: self) result in
switch result
case .success(let value):
print(value)
case .failure(let error):
print(error)




where we have



struct User 
let name: String


enum ServerError: Error
case invalidDataError



The implementation:



import Foundation

public enum Result<Value>
case success(Value)
case failure(Error)

var value: Value?
switch self
case .success(let value):
return value
case .failure:
return nil





class ResultObserver<Value>
typealias ResultObserverBlock = (Result<Value>) -> Void

weak var observer: AnyObject?
let block: ResultObserverBlock

init(observer: AnyObject, block: @escaping ResultObserverBlock)
self.observer = observer
self.block = block



class ValueObserver<Value>
typealias ValueObserverBlock = (Value) -> Void

weak var observer: AnyObject?
let block: ValueObserverBlock

init(observer: AnyObject, block: @escaping ValueObserverBlock)
self.observer = observer
self.block = block



public class Observable<Value>

typealias ResultObserverBlock = (Result<Value>) -> Void
typealias ValueObserverBlock = (Value) -> Void

//MARK: - Private properties
private var valueObservers = [ValueObserver<Value>]()
private var resultObservers = [ResultObserver<Value>]()

//MARK: - Public properties
public var result : Result<Value>
didSet
self.notifyObservers()



public var value: Value?
get
return self.result.value

set
if let value = newValue
self.result = Result.success(value)




//MARK: - Struct lifecycle
public init(_ result: Result<Value>)
self.result = result


public convenience init(_ value: Value)
self.init(Result.success(value))


public convenience init(_ error: Error)
self.init(Result.failure(error))


//MARK: - Observation
func observeResults(on observer: AnyObject, observerBlock: @escaping ResultObserverBlock)
self.resultObservers.append(ResultObserver(observer: observer, block: observerBlock))
observerBlock(result)


func observeValues(on observer: AnyObject, observerBlock: @escaping ValueObserverBlock)
self.valueObservers.append(ValueObserver(observer: observer, block: observerBlock))
if let value = value
observerBlock(value)



func remove(observer: AnyObject)
self.resultObservers = self.resultObservers.filter($0.observer !== observer)
self.valueObservers = self.valueObservers.filter($0.observer !== observer)


//MARK: - Helpers
private func notifyObservers()
for observer in self.valueObservers
if let value = value
observer.block(value)


for observer in self.resultObservers
observer.block(result)











share|improve this question












share|improve this question




share|improve this question








edited Jan 26 at 12:02
























asked Jan 24 at 12:07









iOSGeek

1737




1737











  • If Result is the only thing that you need from Alamofire then I would suggest to include the definition here directly, to make your code independent of an (otherwise unrelated) framework.
    – Martin R
    Jan 24 at 12:35










  • @MartinR done, hope that it's easier to be reviewed now
    – iOSGeek
    Jan 24 at 12:38










  • That does not compile, there are several errors (related to Result<Value> having too few parameters). – Also a (minimal) main program demonstrating the usage might be helpful.
    – Martin R
    Jan 24 at 12:42











  • @MartinR now it compiles and I have added usage information
    – iOSGeek
    Jan 24 at 13:13
















  • If Result is the only thing that you need from Alamofire then I would suggest to include the definition here directly, to make your code independent of an (otherwise unrelated) framework.
    – Martin R
    Jan 24 at 12:35










  • @MartinR done, hope that it's easier to be reviewed now
    – iOSGeek
    Jan 24 at 12:38










  • That does not compile, there are several errors (related to Result<Value> having too few parameters). – Also a (minimal) main program demonstrating the usage might be helpful.
    – Martin R
    Jan 24 at 12:42











  • @MartinR now it compiles and I have added usage information
    – iOSGeek
    Jan 24 at 13:13















If Result is the only thing that you need from Alamofire then I would suggest to include the definition here directly, to make your code independent of an (otherwise unrelated) framework.
– Martin R
Jan 24 at 12:35




If Result is the only thing that you need from Alamofire then I would suggest to include the definition here directly, to make your code independent of an (otherwise unrelated) framework.
– Martin R
Jan 24 at 12:35












@MartinR done, hope that it's easier to be reviewed now
– iOSGeek
Jan 24 at 12:38




@MartinR done, hope that it's easier to be reviewed now
– iOSGeek
Jan 24 at 12:38












That does not compile, there are several errors (related to Result<Value> having too few parameters). – Also a (minimal) main program demonstrating the usage might be helpful.
– Martin R
Jan 24 at 12:42





That does not compile, there are several errors (related to Result<Value> having too few parameters). – Also a (minimal) main program demonstrating the usage might be helpful.
– Martin R
Jan 24 at 12:42













@MartinR now it compiles and I have added usage information
– iOSGeek
Jan 24 at 13:13




@MartinR now it compiles and I have added usage information
– iOSGeek
Jan 24 at 13:13










1 Answer
1






active

oldest

votes

















up vote
1
down vote













There is quite a lot of "almost similar" code due to the fact that you
treat "value observers" and "result observers" separately:
Two classes



class ResultObserver<Value>
class ValueObserver<Value>


with corresponding type aliases:



typealias ResultObserverBlock = (Result<Value>) -> Void
typealias ValueObserverBlock = (Value) -> Void


and instance variables



private var valueObservers = [ValueObserver<Value>]()
private var resultObservers = [ResultObserver<Value>]()


three init methods, two observeResults() methods, etc.



This would be greatly simplified by just observing a generic
type Value, which in particular can be a Result<>:



class Observer<Value> 
typealias ObserverBlock = (Value) -> Void

weak var observer: AnyObject?
let block: ObserverBlock

init(observer: AnyObject, block: @escaping ObserverBlock)
self.observer = observer
self.block = block



public class Observable<Value>

//MARK: - Private properties
private var observers = [Observer<Value>]()

//MARK: - Public properties
public var value : Value
didSet
self.notifyObservers()



//MARK: - Struct lifecycle
public init(_ value: Value)
self.value = value


//MARK: - Observation
func observe(on observer: AnyObject, observerBlock: @escaping Observer<Value>.ObserverBlock)
self.observers.append(Observer(observer: observer, block: observerBlock))
observerBlock(value)


func remove(observer: AnyObject)
self.observers = self.observers.filter( $0.observer !== observer )


//MARK: - Helpers
private func notifyObservers()
for observer in self.observers
observer.block(value)





Note also the use of Observer<Value>.ObserverBlock in order to
avoid defining the same closure type twice.



Now you can observe a simple (boolean) value:



private let isLoadingObservable = Observable(false)

// ...

isLoadingObservable.observe(on: self) isLoading in
print("observed:", isLoading)



or a result:



private let dataObservable = Observable(Result<[User]>.success())

// ...

dataObservable.observe(on: self) result in
switch result
case .success(let value):
print("Value:", value)
case .failure(let error):
print("Error:", error)




Another possible improvement could be to check in func notifyObservers() if the observing object still is alive, and
remove it from the list otherwise.






share|improve this answer





















    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185872%2fobservable-implementation%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote













    There is quite a lot of "almost similar" code due to the fact that you
    treat "value observers" and "result observers" separately:
    Two classes



    class ResultObserver<Value>
    class ValueObserver<Value>


    with corresponding type aliases:



    typealias ResultObserverBlock = (Result<Value>) -> Void
    typealias ValueObserverBlock = (Value) -> Void


    and instance variables



    private var valueObservers = [ValueObserver<Value>]()
    private var resultObservers = [ResultObserver<Value>]()


    three init methods, two observeResults() methods, etc.



    This would be greatly simplified by just observing a generic
    type Value, which in particular can be a Result<>:



    class Observer<Value> 
    typealias ObserverBlock = (Value) -> Void

    weak var observer: AnyObject?
    let block: ObserverBlock

    init(observer: AnyObject, block: @escaping ObserverBlock)
    self.observer = observer
    self.block = block



    public class Observable<Value>

    //MARK: - Private properties
    private var observers = [Observer<Value>]()

    //MARK: - Public properties
    public var value : Value
    didSet
    self.notifyObservers()



    //MARK: - Struct lifecycle
    public init(_ value: Value)
    self.value = value


    //MARK: - Observation
    func observe(on observer: AnyObject, observerBlock: @escaping Observer<Value>.ObserverBlock)
    self.observers.append(Observer(observer: observer, block: observerBlock))
    observerBlock(value)


    func remove(observer: AnyObject)
    self.observers = self.observers.filter( $0.observer !== observer )


    //MARK: - Helpers
    private func notifyObservers()
    for observer in self.observers
    observer.block(value)





    Note also the use of Observer<Value>.ObserverBlock in order to
    avoid defining the same closure type twice.



    Now you can observe a simple (boolean) value:



    private let isLoadingObservable = Observable(false)

    // ...

    isLoadingObservable.observe(on: self) isLoading in
    print("observed:", isLoading)



    or a result:



    private let dataObservable = Observable(Result<[User]>.success())

    // ...

    dataObservable.observe(on: self) result in
    switch result
    case .success(let value):
    print("Value:", value)
    case .failure(let error):
    print("Error:", error)




    Another possible improvement could be to check in func notifyObservers() if the observing object still is alive, and
    remove it from the list otherwise.






    share|improve this answer

























      up vote
      1
      down vote













      There is quite a lot of "almost similar" code due to the fact that you
      treat "value observers" and "result observers" separately:
      Two classes



      class ResultObserver<Value>
      class ValueObserver<Value>


      with corresponding type aliases:



      typealias ResultObserverBlock = (Result<Value>) -> Void
      typealias ValueObserverBlock = (Value) -> Void


      and instance variables



      private var valueObservers = [ValueObserver<Value>]()
      private var resultObservers = [ResultObserver<Value>]()


      three init methods, two observeResults() methods, etc.



      This would be greatly simplified by just observing a generic
      type Value, which in particular can be a Result<>:



      class Observer<Value> 
      typealias ObserverBlock = (Value) -> Void

      weak var observer: AnyObject?
      let block: ObserverBlock

      init(observer: AnyObject, block: @escaping ObserverBlock)
      self.observer = observer
      self.block = block



      public class Observable<Value>

      //MARK: - Private properties
      private var observers = [Observer<Value>]()

      //MARK: - Public properties
      public var value : Value
      didSet
      self.notifyObservers()



      //MARK: - Struct lifecycle
      public init(_ value: Value)
      self.value = value


      //MARK: - Observation
      func observe(on observer: AnyObject, observerBlock: @escaping Observer<Value>.ObserverBlock)
      self.observers.append(Observer(observer: observer, block: observerBlock))
      observerBlock(value)


      func remove(observer: AnyObject)
      self.observers = self.observers.filter( $0.observer !== observer )


      //MARK: - Helpers
      private func notifyObservers()
      for observer in self.observers
      observer.block(value)





      Note also the use of Observer<Value>.ObserverBlock in order to
      avoid defining the same closure type twice.



      Now you can observe a simple (boolean) value:



      private let isLoadingObservable = Observable(false)

      // ...

      isLoadingObservable.observe(on: self) isLoading in
      print("observed:", isLoading)



      or a result:



      private let dataObservable = Observable(Result<[User]>.success())

      // ...

      dataObservable.observe(on: self) result in
      switch result
      case .success(let value):
      print("Value:", value)
      case .failure(let error):
      print("Error:", error)




      Another possible improvement could be to check in func notifyObservers() if the observing object still is alive, and
      remove it from the list otherwise.






      share|improve this answer























        up vote
        1
        down vote










        up vote
        1
        down vote









        There is quite a lot of "almost similar" code due to the fact that you
        treat "value observers" and "result observers" separately:
        Two classes



        class ResultObserver<Value>
        class ValueObserver<Value>


        with corresponding type aliases:



        typealias ResultObserverBlock = (Result<Value>) -> Void
        typealias ValueObserverBlock = (Value) -> Void


        and instance variables



        private var valueObservers = [ValueObserver<Value>]()
        private var resultObservers = [ResultObserver<Value>]()


        three init methods, two observeResults() methods, etc.



        This would be greatly simplified by just observing a generic
        type Value, which in particular can be a Result<>:



        class Observer<Value> 
        typealias ObserverBlock = (Value) -> Void

        weak var observer: AnyObject?
        let block: ObserverBlock

        init(observer: AnyObject, block: @escaping ObserverBlock)
        self.observer = observer
        self.block = block



        public class Observable<Value>

        //MARK: - Private properties
        private var observers = [Observer<Value>]()

        //MARK: - Public properties
        public var value : Value
        didSet
        self.notifyObservers()



        //MARK: - Struct lifecycle
        public init(_ value: Value)
        self.value = value


        //MARK: - Observation
        func observe(on observer: AnyObject, observerBlock: @escaping Observer<Value>.ObserverBlock)
        self.observers.append(Observer(observer: observer, block: observerBlock))
        observerBlock(value)


        func remove(observer: AnyObject)
        self.observers = self.observers.filter( $0.observer !== observer )


        //MARK: - Helpers
        private func notifyObservers()
        for observer in self.observers
        observer.block(value)





        Note also the use of Observer<Value>.ObserverBlock in order to
        avoid defining the same closure type twice.



        Now you can observe a simple (boolean) value:



        private let isLoadingObservable = Observable(false)

        // ...

        isLoadingObservable.observe(on: self) isLoading in
        print("observed:", isLoading)



        or a result:



        private let dataObservable = Observable(Result<[User]>.success())

        // ...

        dataObservable.observe(on: self) result in
        switch result
        case .success(let value):
        print("Value:", value)
        case .failure(let error):
        print("Error:", error)




        Another possible improvement could be to check in func notifyObservers() if the observing object still is alive, and
        remove it from the list otherwise.






        share|improve this answer













        There is quite a lot of "almost similar" code due to the fact that you
        treat "value observers" and "result observers" separately:
        Two classes



        class ResultObserver<Value>
        class ValueObserver<Value>


        with corresponding type aliases:



        typealias ResultObserverBlock = (Result<Value>) -> Void
        typealias ValueObserverBlock = (Value) -> Void


        and instance variables



        private var valueObservers = [ValueObserver<Value>]()
        private var resultObservers = [ResultObserver<Value>]()


        three init methods, two observeResults() methods, etc.



        This would be greatly simplified by just observing a generic
        type Value, which in particular can be a Result<>:



        class Observer<Value> 
        typealias ObserverBlock = (Value) -> Void

        weak var observer: AnyObject?
        let block: ObserverBlock

        init(observer: AnyObject, block: @escaping ObserverBlock)
        self.observer = observer
        self.block = block



        public class Observable<Value>

        //MARK: - Private properties
        private var observers = [Observer<Value>]()

        //MARK: - Public properties
        public var value : Value
        didSet
        self.notifyObservers()



        //MARK: - Struct lifecycle
        public init(_ value: Value)
        self.value = value


        //MARK: - Observation
        func observe(on observer: AnyObject, observerBlock: @escaping Observer<Value>.ObserverBlock)
        self.observers.append(Observer(observer: observer, block: observerBlock))
        observerBlock(value)


        func remove(observer: AnyObject)
        self.observers = self.observers.filter( $0.observer !== observer )


        //MARK: - Helpers
        private func notifyObservers()
        for observer in self.observers
        observer.block(value)





        Note also the use of Observer<Value>.ObserverBlock in order to
        avoid defining the same closure type twice.



        Now you can observe a simple (boolean) value:



        private let isLoadingObservable = Observable(false)

        // ...

        isLoadingObservable.observe(on: self) isLoading in
        print("observed:", isLoading)



        or a result:



        private let dataObservable = Observable(Result<[User]>.success())

        // ...

        dataObservable.observe(on: self) result in
        switch result
        case .success(let value):
        print("Value:", value)
        case .failure(let error):
        print("Error:", error)




        Another possible improvement could be to check in func notifyObservers() if the observing object still is alive, and
        remove it from the list otherwise.







        share|improve this answer













        share|improve this answer



        share|improve this answer











        answered Jan 25 at 21:09









        Martin R

        14.1k12257




        14.1k12257






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185872%2fobservable-implementation%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Chat program with C++ and SFML

            Function to Return a JSON Like Objects Using VBA Collections and Arrays

            Will my employers contract hold up in court?