Best practice to observe livedata from data class - android-livedata

I need to observe a LiveData object from a data class. I have observed these two methods are working:
Method 1: Convert the class to a LifecycleOwner
class MyObservingClass: LifecycleOwner {
private val lifecycleRegistry: LifecycleRegistry
var receivedData: String = ""
init {
lifecycleRegistry = LifecycleRegistry(this)
Server.dataObject.observe(this) {
receivedData = it
Log.d(DEBUG_TAG, "observed : $it")
}
lifecycleRegistry.currentState = Lifecycle.State.STARTED
}
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
}
Method 2: Use observeForever
class MyObservingClass {
var receivedData: String = ""
init {
Server.dataObject.observeForever {
receivedData = it
Log.d(DEBUG_TAG, "observed : $it")
}
}
}
Then I am using it as val obj = MyObservingClass() in some other place. As previously stated both of these approaches are working and I can observe the LiveData.
My question is, will these two methods prevent garbage collection of the objects as they are observing indefinitely? Are both these methods flawed? I am making an Android library and don't have life-cycle aware components like Activity.
Thanks for the replies :)

Related

How one model can notify another model for specific property has changed?

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)
}
}

Swinject: How to use delegate pattern with interface segregation (class vs interface)?

I'm working my way into the injection dependency issue. Now the question arises how to use the interface segregation principle in the case of the delegate pattern? I am using the Swinject framework for dependency injection. How can I solve this?
class Client {
private var interface: ParentInterface
...
func execute() {
interface = globalContainer.resolve(ParentInterface.self)
interface?.parentMethod()
}
}
protocol ParentInterface {
func parentMethod()
}
class Parent: ParentInterface, ParentDelegate {
// Dependency
private var child: Child? // 🚨 I WANT TO USE AN PROTOCOL HERE, NOT THE CLASS
init(childReference: Child) {
self.child = childReference
self.child?.delegate = self // 🚨 But if I use the protocol I cant access the delegate property
}
public func parentMethod() {
let result = calcSomething()
// Access child class interface method
child?.childMethod(result)
}
...
}
The child class, nothing unusual so far.
protocol ParentDelegate: class {
func delagteMethod(value: Double)
}
protocol ChildInterface {
func childMethod(_ result: Double)
}
class Child: ChildInterface {
weak var delegate: ParentDelegate?
...
private func delagteMethod() {
delegate?.delagteMethod(value: someValue)
}
}
But to inject the dependencies properly I need a protocol and not a direct class reference, right? Like this:
// 🚨 Would like to
container.register(ParentInterface.self) { r in
Parent(childInterface: r.resolve(ChildInterface.self)!)
}
// 🚨 Only way I get it working without interface
container.register(ParentDelegate.self) { r in
Parent(childClass: Child())
}
container.register(ChildInterface.self) { _ in Child() }
.initCompleted { r, c in
let child = c as! Child
child.delegate = r.resolve(ParentDelegate.self)
}
In short, I'm going around in circles. If I use an interface for the child class I can't access the delegate property, if I use a class reference I can't reasonably to mock/stub the interface methods.
What am I missing? Thanks a lot in advance!
I would recommend removing self.child?.delegate = self from the Parent constructor for two reasons:
Shouldn't the DI framework be responsible for wiring all the dependencies?
In Swinject specifically, this may lead to incorrect dependencies, see the remark in docs
Pattern that I tend to use is more or less what you came up with:
class Parent: ParentInterface, ParentDelegate {
init(child: ChilInterface) { self.child = child }
}
class Child: ChildInterface {
weak var parent: ParentDelegate?
}
container.register(ParentInterface.self) { Parent(child: $0.resolve(ChildInterface.self)!) }
.implements(ParentDelegate.self)
container.register(ChildInterface.self) { _ in Child() }
.initCompleted { ($1 as! Child).delegate = $0.resolve(ParentDelegate.self) }
Additionally, if you wanted to get rid of force casting, you could do something like:
container.register(Child.self) { _ in Child() }
.implements(ChildInterface.self)
.initCompleted { $1.delegate = $0.resolve(ParentDelegate.self) }

Swift doesn't recognize Type variables

In my project I am having an issue with Swift, that it doesn't recognize variables containing protocol types. This means I can't use a variable that stores a type, to check if the type of an instance matches it.
I attached problematic part of the code with some brief context.
Is this some kind of bug or am I overseeing something really badly?
Using XCode 7.3, Swift 2.2
//Context of Issue BEGIN
class TaskValueObject {
//ManyData, VeryComplexity, SoBig, wow..
}
typealias TaskListSorterBlock = (TaskValueObject, TaskValueObject) -> Bool
protocol TaskListSorter : class {
init()
var localizedTitle : String { get }
var sorterBlock : TaskListSorterBlock { get }
}
class PriorityTaskListSorter : TaskListSorter {
// ... Implementation ...
}
// Many other TaskListSorter implementation classes ...
//Context of Issue END
class TaskListContainer {
weak var currentSorter : TaskListSorter?
var sorters : [TaskListSorter]
init() {
self.sorters = [ PriorityTaskListSorter(), /* ... Instances created for <TaskListSorter> implementing class ... */ ]
loadDefaultSorter()
}
static var defaultSorterType : TaskListSorter.Type = PriorityTaskListSorter.self
private func loadDefaultSorter() {
let defaultSorterType = TaskListContainer.defaultSorterType
for sorter in self.sorters {
if sorter is defaultSorterType {
// ^ ERROR HERE : defaultSorterType is not a 'type'
self.currentSorter = sorter
}
}
}
}
Update 1: I get the same error if I replace the problematic line with the following:
if let defaultSorter = sorter as? defaultSorterType {
Update 2: Replacing the problematic line with the one below, makes the code work. However I am using here the 'dynamicType' which is not offered by code completion (must be a reason for that...). Also the question remains, why the first 2 attempts didn't work?
if sorter.dynamicType == defaultSorterType {

Alternative to smelly global variables in Swift?

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.

Inheriting initializer from generic class

I've seen some discussions about this problem, but have not read a satisfactory explanation. Can anybody tell me why this does not work?
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child : Parent<Int> {}
let c = Child(data: 4)
The last line gives the error:
'Child' cannot be constructed because it has no accessible initializers
Do I really need to implement the initializer just to call super?
Edit:
To give a bit of context, the real code looks closer to the below. I have an Action class which uses generics, because I have another bit of code which can chain actions together and I want to use Swift's type safety to ensure that actions can be chained. Then I have a bunch of subclasses classes (e.g. CustomAction). I am looking for a way to avoid overriding the init method in each of the subclasses. Or alternatively, I want to understand why that's not possible.
class Action<Input, Output> {
var cachedOutput:Output?
init(cachedOutput:Output?) {
self.cachedOutput = cachedOutput
}
}
protocol CustomInput {}
protocol CustomOutput {}
class CustomAction : Action<CustomInput, CustomOutput> {
}
yes, you really need to override init method ..
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<T> : Parent<T> {
override init(data: T) {
super.init(data: data)
}
}
let c = Child(data: 4) // Child<Int>
let c2 = Child(data: "alfa") // Child<String>
what are the errors ...
// what is the type T ? it is undeclared!
class Child2: Parent2<T> {}
// how to specialize non-generic type Parent ? how to create it?
// i need an initializer in class Child3 ... Hm ...
class Child3: Parent<Int> {}
// cannot specialize non-generic type 'Parent'
class Child3: Parent<Int> {
override init(data: Int) {
super.init(data: data)
}
}
// So, Child3 must be of the same specialized type as Parent!!
that is terrible, isn't it? so look at my final example !
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<Double> : Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4) // Child<Int> !!!!!
let d = Child(data: true) // Child<Bool> !!!
in your case it works like
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child: Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4)
print(c.dynamicType) // Child :-)
This now works in Swift 3. My original example now compiles. There is no mention of this in the Swift 3 Language changes, so I can only assume that this was a bug.

Resources