Subscribing to a property - ios

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

Related

Swift class throwing immutable self

I have a method in a protocol extension that plays music files. Since the protocol doesn't know if its conformer will be a class or struct and the methods in it change an ivar, it requires them to be marked as mutating.
When I conform a class to that protocol and try to call the method I'm getting the below error even though a class should always be muteable...
Cannot use mutating member on immutable value: 'self' is immutable
Here's the protocol...
import AVFoundation
/// Conformers are required to implement `player` property to gain access to `playMusic:fileLloops`
/// method.
protocol CanPlayMusic {
/// This instance variable stores the audio player for `playMusic:file:loops` method.
var player: AVAudioPlayer! { get set }
}
extension CanPlayMusic {
/// This method creates a new audio player from url and then plays for a number of given.
///
/// - Parameter file: The url where sound file is stored.
/// - Parameter loops: The number of loops to play (-1 is infinite).
mutating func playMusic(file url: URL, loops: Int = -1) {
player = try! AVAudioPlayer(contentsOf: url)
player.numberOfLoops = loops
player.play()
}
/// This method starts playing intro music.
mutating func playIntroMusic() {
let file = Assets.Music.chargeDeBurkel
let ext = Assets.Music.ext
guard let url = Bundle.main.url(forResource: file,
withExtension: ext) else { return }
playMusic(file: url)
}
/// This method starts playing game over music based on win/loss condition.
mutating func playGameOverMusic(isWinning: Bool) {
guard let lose = Bundle.main.url(forResource: Assets.Music.taps,
withExtension: Assets.Music.ext),
let win = Bundle.main.url(forResource: Assets.Music.reveille,
withExtension: Assets.Music.ext)
else { return }
playMusic(file: isWinning ? win : lose, loops: 1)
}
}
And here's how I call it in a class...
import UIKit
import AVFoundation
class EntranceViewController: UIViewController, CanGetCurrency, CanPlayMusic {
...
// MARK: - Properties: CanPlayMusic
var player: AVAudioPlayer!
...
// MARK: - Functions: UIViewController
override func viewDidLoad() {
playIntroMusic() // <-- Error thrown here
startButton.setTitle("", for: .normal)
getCurrency()
}
UPDATE
I use these methods in multiple places, both in UIKit scenes and SwiftUI views; moving it into the protocol extension was an attempt to reduce duplication of code.
For now, I'm using a class wrapper that I can call in both contexts; but I still haven't seen an explanation for the error triggering on a class (since they're pass by ref and all of their functions are considered mutating by default).
To clarify, my question is "Why is this class having issues with a mutating function?"
The error is a bit misleading, but I believe the reason of it is that you call a method marked with mutating (defined in your protocol extension) from a class – it's illegal.
Consider this simplified example:
protocol SomeProtocol {
var state: Int { get set }
}
extension SomeProtocol {
mutating func doSomething() {
state += 1
}
}
struct SomeValueType: SomeProtocol {
var state = 0
init() {
doSomething()
}
}
final class SomeReferenceType: SomeProtocol {
var state = 0
init() {
doSomething() // Cannot use mutating member on immutable value: 'self' is immutable
}
}
One way to get rid of the error is not using the same implementation for both structs and classes and defining their own implementations:
protocol SomeProtocol {
var state: Int { get set }
mutating func doSomething()
}
struct SomeValueType: SomeProtocol {
var state = 0
init() {
doSomething()
}
mutating func doSomething() {
state += 1
}
}
final class SomeReferenceType: SomeProtocol {
var state = 0
init() {
doSomething()
}
func doSomething() {
state += 1
}
}
Another way is to, at least, defining an own implementation for classes, which will shade the default implementation from the protocol extension:
protocol SomeProtocol {
var state: Int { get set }
}
extension SomeProtocol {
mutating func doSomething() {
state += 1
}
}
struct SomeValueType: SomeProtocol {
var state = 0
init() {
doSomething()
}
}
final class SomeReferenceType: SomeProtocol {
var state = 0
init() {
doSomething()
}
func doSomething() {
state += 1
}
}

How to notify a change from a class to multiple ViewControllers?

I'm writing because I'd like to know what's the best method to notify a change from a class to multiple ViewControllers. At the moment I'm using delegate method but I'm sure It's not the best one for this purpose. I created a class where I receive data and after a bit of processing I need to send the processed message to some ViewControllers (any one shows a piece of that message.). At the moment I have a singleton for the class and I assign its delegate to a different ViewController when I load it through a menu. What's your suggestion to do this job?
Here is an example of my actual code:
import Foundation
protocol MyClassDelegate {
func receivedData(_ sender: MyClass)
}
class MyClass: NSObject {
// create the var for delegate
var delegate: MyClassDelegate?
// save the single instance
static private var instance: MyClass {
return sharedInstance
}
private let sharedInstance = MyClass()
static func getInstance() -> MyClass {
return instance
}
func processData() {
// at the end of the process
delegate?.receivedData(self)
}
}
class Menu: UIViewController {
private var containerView: UIView!
private let myClass = Myclass.getInstance()
private var vcOne = VcOne()
private var vcTwo = VcTwo()
override func viewDidLoad() {
super.viewDidLoad()
containerView = UIVIew()
// set containerView position and dimensions
}
func selectViewController(previous: UIViewController, next: UIViewController) {
// remove actual loaded ViewController
previous.willMove(toParent: nil)
previous.view.removeFromSuperView()
previous.removeFromParent()
// assign the delegate
myClass.delegate = next
// add the new ViewController
self.addChild(next)
slef.addSubView(next.view)
next.didMove(toParent: self)
}
}
class VcOne: UIViewController, MyClassDelegate {
func receivedData(_ sender: MyClass) {
// data received
}
}
class VcTwo: UIViewController, MyClassDelegate {
func receivedData(_ sender: MyClass) {
// data received
}
}
You can use NotificationCenter https://developer.apple.com/documentation/foundation/notificationcenter to broadcast a message. or use KVO. Generally I consider notification center much easier.
simple example:
https://www.hackingwithswift.com/example-code/system/how-to-post-messages-using-notificationcenter

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!

Swift - How can I override an extension method in a concrete subclass

I have an extension on UIView implementing a protocol
protocol SomeProtocol {
var property : Int
}
extension UIView : SomeProtocol {
var property : Int {
get {
return 0
}
set {
// do nothing
}
}
}
in a concrete subclass I want to override this extension method:
class Subclass : UIView, SomeProtocol {
var _property : Int = 1
var property : Int {
get { return _property}
set(val) {_property = val}
}
}
I set breakpoints and see that the extension method is called and not the concrete subclass method:
var subclassObject = Subclass()
someObject.doSomethingWithConcreteSubclassObject(subclassObject)
// other code;
fun doSomethingWithConcreteSuclassObject(object : UIView) {
var value = object.property // always goes to extension class get/set
}
As others have noted, Swift does not (yet) allow you to override a method declared in a class extension. However, I'm not sure whether you'll ever get the behavior you want even if/when Swift someday allows you to override these methods.
Consider how Swift deals with protocols and protocol extensions. Given a protocol to print some metasyntactic variable names:
protocol Metasyntactic {
func foo() -> String
func bar() -> String
}
An extension to provide default implementations:
extension Metasyntactic {
func foo() -> String {
return "foo"
}
func bar() -> String {
return "bar"
}
}
And a class that conforms to the protocol:
class FooBar : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
}
Swift will use dynamic dispatch to call the appropriate implementations of foo() and bar() based on each variable's runtime type rather than on the type inferred by the compiler:
let a = FooBar()
a.foo() // Prints "FOO"
a.bar() // Prints "BAR"
let b: Metasyntactic = FooBar()
b.foo() // Prints "FOO"
b.bar() // Prints "BAR"
If, however, we extend the protocol further to add a new method:
extension Metasyntactic {
func baz() -> String {
return "baz"
}
}
And if we override our new method in a class that conforms to the protocol:
class FooBarBaz : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
func baz() -> String {
return "BAZ"
}
}
Swift will now use static dispatch to call the appropriate implementation of baz() based on the type inferred by the compiler:
let a = FooBarBaz()
a.baz() // Prints "BAZ"
let b: Metasyntactic = FooBarBaz()
b.baz() // Prints "baz"
Alexandros Salazar has a fantastic blog post explaining this behavior in depth, but suffice it to say that Swift only uses dynamic dispatch for methods declared in the original protocol, not for methods declared in protocol extensions. I imagine the same would be true of class extensions, as well.
I know this question has been asked a while ago. But this will be handy for someone who looking for an easier way. There is a way of overriding an extension methods. I know its bit hacky but it does the job beautifully.
If you declare your protocol with #objc
#objc protocol MethodOverridable {
func overrideMe()
}
In Extension
extension MainClass: MethodOverridable {
func overrideMe() {
print("Something useful")
}
}
Subclass - You can able to override it in your subclass. It works like a magic. Well, not really when adding #objc it exposes your protocol to Objective-C and its Runtime. That allows your subclass to override.
class SubClass: MainClass {
override func overrideMe() {
print("Something more useful")
}
}
Swift 5
class Class
{
#objc dynamic func make() { print("make from class") }
}
class SubClass: Class {}
extension SubClass {
override func make() {
print("override")
}
}
It looks like you can override property for 2nd super class property. For example, you can access UIView property by making extension to the UILabel wanting to override frame property of UIView. This sample works for me in Xcode 6.3.2
extension UILabel {
override public var frame: CGRect {
didSet {
println("\(frame)")
}
}
}
You can't do this through normal means.
It's in Apple's docs that you can't override a method in an extension in a subclass.
Also, extensions can add new functionality to a type, but they cannot override existing functionality.
https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
I think you forgot to override the superclass property in your subclass:
class Subclass : UIView {
var _property : Int = 1
override var property : Int {
get { return _property}
set(val) {_property = val}
}
}

Resources