NSKeyValueObservation not working, Swift 4 - ios

How should I properly set up to receive notifications when the variable changes on the shared instance of the singleton? Currently, the block is never fired.
class MyViewController: UIViewController {
private var observer: NSKeyValueObservation?
func configureKVO() {
observer = MySingleton.shared.observe(\.shouldFetchDataFromServer) { (manager, change) in
print("Changed: \(manager.shouldFetchDataFromServer)")
}
MySingleton.shared.shouldFetchDataFromServer = false
MySingleton.shared.shouldFetchDataFromServer = true
}
}
class MySingleton: NSObject {
static let shared = MySingleton()
#objc var shouldFetchDataFromServer: Bool = false
}

Add the 'dynamic' keyword to the declaration of shouldFetchDataFromServer, and it should work.

Related

How can I assert a delegate is called in my unit test

I would like to asset that the correct delegate method is called depending on the result of a check in my presenter.
Having mocked out my IdentityProvider to return true, how would I write a test to assert delegate?.userIsAuthenticated() is called?
import Foundation
import InjectStory
protocol StartPresenterDelegate: class {
func userIsAuthenticated()
func userNeedsToAuthenticate()
}
class StartPresenter {
weak var delegate: StartPresenterDelegate?
weak var view: StartViewInterface!
private lazy var identityProvider = Dependencies.identityProvider.inject()
init(view: StartViewInterface) {
self.view = view
}
private func checkUserAuthState() {
if identityProvider.isAuthorized() {
delegate?.userIsAuthenticated()
} else {
delegate?.userNeedsToAuthenticate()
}
}
}
extension StartPresenter: StartPresentation {
func onViewDidLoad() {
checkUserAuthState()
}
}
extension StartPresenter {
struct Dependencies {
static let identityProvider = Injection<IdentityProviderProtocol>(IdentityProvider.shared)
}
}
You need to do some trick. Create MockDelegateClass for your protocol StartPresenterDelegate
example:
class MockDelegate: StartPresenterDelegate {
var isUserIsAuthenticatedCalled = false
var isUserNeedsToAuthenticateCalled = false
func userIsAuthenticated() {
isUserIsAuthenticatedCalled = true
}
func userNeedsToAuthenticate() {
isUserNeedsToAuthenticateCalled = true
}
}
then in your test try to do something like that:
func test_MyTest() {
// init your class StartPresenter that you wanna test
var presenter = StartPresenter(...)
var mockDelegate = MockDelegate()
presenter.delegate = mockDelegate
presenter.onViewDidLoad()
XCTAssertTrue(mockDelegate.isUserIsAuthenticatedCalled)
XCTAssertFalse(mockDelegate.isUserNeedsToAuthenticateCalled)
// or change isUserIsAuthenticatedCalled and isUserNeedsToAuthenticateCalled if you expect another states
}
For different states you need different tests, for you it will be the easiest way to test delegate calling.
You have to mock the StartPresenterDelegate as follows too.
class MockStartPresenterDelegate: StartPresenterDelegate {
var userIsAuthenticated_wasCalled = false
func userIsAuthenticated() {
userIsAuthenticated_wasCalled = true
}
}
Inject MockStartPresenterDelegate as the delegate and check that userIsAuthenticated_wasCalled is true
In this way you can confirm the unit test for delegates in Swift apps

Subscribing to a property

I have a singleton service class which maintains a value of heading it gets from the compass. I have a UIView which draws some custom graphics based on this. I'm trying to do something like an Observable in javascript, where my code gets executed when there's a change in the value.
final class LocationService: NSObject {
static let shared = LocationService()
public var heading:Int
public func getHeading() -> Int {
return self.heading
}
Then in my UIView subclass:
var ls:LocationService = LocationService.shared
var heading: Int = ls.getHeading() {
didSet {
setNeedsDisplay()
}
}
I tried also just directly accessing the property via ls.heading but this doesn't get accepted either. It's telling me I cannot use the instance member within the property initialiser. What's a proper swift method of doing this?
Edit:
I've been working with Christian's answer below and some other documentation and now got to here where it all compiles nicely, but doesn't actually work. Here's my delegator and protocol:
final class LocationService: NSObject {
static let shared = LocationService()
weak var delegate: CompassView?
var heading:Int
func headingUpdate(request:HeadingRequest, updateHeading:CLHeading) {
print ("New heading found: \(updateHeading)")
self.heading = Int(updateHeading.magneticHeading)
self.delegate?.setHeading(newHeading: Int(updateHeading.magneticHeading))
}
public func getHeading() -> Int {
return self.heading
}
}
protocol LSDelegate: class {
func setHeading(newHeading:Int)
}
Then in the delegate:
class CompassView: UIView, LSDelegate {
func setHeading(newHeading:Int) {
self.heading = newHeading
print("heading updated in compass view to \(self.heading)")
setNeedsDisplay()
}
}
So I get the print message that the heading has been updated in the headingUpdate function. The print message in the setHeading function in the delegate CompassView never gets displayed.
You can use the delegation pattern and have that class that wants to consume your events implement the functions in your protocol.
protocol MyDelegate {
func setNeedsDisplay()
}
class LocationService: NSObject {
var myDelegate : MyDelegate?
var heading: Int = ls.getHeading() {
didSet {
myDelegate?.setNeedsDisplay()
}
}
...
func assignDelegate() {
self.myDelegate = MyConsumer()
}
}
class MyConsumer : MyDelegate {
func setNeedsDisplay()
{
}
}

How do i make singleton class as delegator in swift3

I have a singleton class as shown in below code snippet .
protocol EmpLoginDelegate {
func empLoginSuccess()
func empLoginFailed()
}
class CommunicationModule {
static let sharedInstance = CommunicationModule()
private init() {
}
var empLoginDelegate:EmpLoginDelegate?
func test(){
self.empLoginDelegate?.empLoginSuccess()
}
}
My delegate class is shown in below code snippet.
extension LoginViewController: EmpLoginDelegate{
func empLoginSuccess(){
wrongAttempts = 0
loginSuccess = true
print("EmpLoginIsSuccess")
performSegue(withIdentifier: "attendanceView", sender: self)
}
func empLoginFailed(){
wrongAttempts = wrongAttempts + 1
userNameTextField.shake(count: 3, for: 0.3, withTranslation: 10)
passwordTextField.shake(count: 3, for: 0.3, withTranslation: 10)
loginSuccess = false
loginAlert(alertTitle: "Invalid Credentials", alertMsg: "Your employee id or password is not correct")
}
}
When i call test function emploginSuccess() method does not get called. Test function is executed successfully with out any error.
I thought that problem is empLoginDelegate is not initialised in my code, so i had tried possible ways to initialise it as self but nothing worked for me. Is there any other way to use delegation pattern in singleton class in swift3(iOS 10.3.1).
Make sure you are communicating with the singleton properly. To set your instance of LoginViewController as the empLoginDelegate, call:
CommunicationModule.sharedInstance.empLoginDelegate = self
from a method inside of LoginViewController.
I think This method is good, for me that is best way
final class Singleton {
// Can't init is singleton
private init() { }
//MARK: Shared Instance
static let shared: Singleton = Singleton()
}

Swift Delegate from Singleton not working

I'm trying to implement SharedInstanceDelegate in App class. I have no idea why the functions under the protocol are not being called.
This is my Protocol and class.
class App {
let sharedInstance = SharedInstance.shared
init() {
self.sharedInstance.delegate = self
}
}
extension App: SharedInstanceDelegate {
func1() { } // this is not executed
func2() { }
}
protocol SharedInstanceDelegate: class {
func1()
func2()
}
class SharedInstance {
static let shared = SharedInstance()
weak var delegate: SharedInstanceDelegate?
private init() { }
func method1() {
self.delegate?.func1() // this is executed
}
}
I believe you meant to make SharedInstanceDelegate a protocol, but you've made it a class. In either case, App does not conform/inherit SharedInstanceDelegate, so it's not clear how this would even compile.
Here is how I would implement your code to work with the delegate:
class App {
let sharedInstance = SharedInstance.shared
init() {
self.sharedInstance.delegate = self
}
}
extension App: SharedInstanceDelegate {
func func1() { } // this will run now
func func2() { }
}
protocol SharedInstanceDelegate {
func func1()
func func2()
}
class SharedInstance {
static let shared = SharedInstance()
var delegate: SharedInstanceDelegate?
private init() { }
func method1() {
self.delegate?.func1() // this is executed
}
}
Still no idea why this was happening, but cleaning the project fixed this. This is very strange. I have other delegates that call successfully.
Your code could work but it depends on how you are calling func1(). Calling it like this:
let testinstance = App().sharedInstance
testinstance.delegate?.func1()
will not work because you are not holding on to the App object. In this case the App object is the delegate, but because its a weak member and no one is retaining it, it gets released right away.
If you call it like this:
let testapp = App()
testapp.sharedInstance.delegate?.func1()
it works. In this case the App object is being retained and is still around when func1() is called.
Either way the way these classes are related is confusing to me. Why have a separate SharedInstance class at all?

Delegate method property to being triggered on conforming class

I have the following protocol
protocol SentenceDelegate: class{
func sentenceDidFinish()
}
I have my SentenceMarkov class conform to the protocol:
class SentenceMarkov : SentenceDelegate{
// foo
// bar
}
I implement the protocol method in my conforming class:
class SentenceMarkov : SentenceDelegate{
//...
func sentenceDidFinish{
//Do something
}
//...
}
I create a property on the class that calls the protocol method called sentenceDelegate :
class otherClass{
//..
weak var sentenceDelegate: SentenceDelegate?
//..
}
I set this property in my first class to self
class SentenceMarkov{
var FirstOne:OtherClass {
didSet { FirstOne.sentenceDelegate = self}
}
var SecondOne:OtherClass {
didSet{ SecondOne.sentenceDelegate = self}
}
init(Ult:OtherClass, Penult:OtherClass){
self.FirstOne= Ult
self.SecondOne = Penult
self.FirstOne.sentenceDelegate = self
self.SecondOne.sentenceDelegate = self
}
//..
}
Finally I call the sentenceDelegate method in OtherClass after its init()
class OtherClass{
func sentenceDone(){
sentenceDelegate?.sentenceDidFinish()
}
}
The problem is that, when I set breakpoints on the above method sentenceDelegate is nil. I am not sure why because I set it, although I could be setting it wrong, I am not sure how to ameliorate that though. Here is the three Swift files include the ViewController where SentenceMarkov is initialized:
https://gist.github.com/ebbnormal/e1cb791dd165a6866e11
https://gist.github.com/ebbnormal/263c9343e403c3a7ac40
https://gist.github.com/ebbnormal/1400e7da024d78ba5ed0
The following works for me:
protocol Greeter: class {
func greet()
}
class Dog {
weak var greeter: Greeter?
}
class DogPound: Greeter {
func greet() {
print("Hi. Welcome to the DogPound!")
}
var dog: Dog {
didSet {
dog.greeter = self
}
}
init(dog: Dog) {
self.dog = dog
self.dog.greeter = self
}
}
let myDog = Dog()
myDog.greeter?.greet() //=>nil
DogPound(dog: myDog)
myDog.greeter?.greet()
--output:--
Hi. Welcome to the DogPound!

Resources