I'm trying to make a singleton class from a protocol, my intentions with this is to have multiple xxxxxHandlers, eg. SpotifyHandler, AppleMusicHandler and so on... and create the correct one when a user logs in with their music-account, then make the handler accessible all throughout the program.
But when i try to make SpotifyHandler i run into this error:
Property 'shared' in non-final class 'SpotifyHandler' must specify type 'Self' to conform to protocol 'MusicHandler'
On this line: static var shared: SpotifyHandler = SpotifyHandler()
Here's the code for the singleton class and protocol (separate files)
import Foundation
import SwiftUI
class SpotifyHandler : MusicHandler {
static var shared: SpotifyHandler = SpotifyHandler()
var is_logged_in: Bool = false;
func set_is_logged_in(is_logged_in: Bool) {
self.is_logged_in = is_logged_in
}
func print_status() {
print("--- Status ---")
print("Is logged in: \(self.is_logged_in)")
}
}
public protocol MusicHandler {
static var shared: Self { get }
var is_logged_in: Bool { get }
func set_is_logged_in(is_logged_in: Bool)
func print_status()
}
I am quite new to swift but have experience in other languages, i am open to suggestions on alternative ways to solve this if you have any. :)
The problem arises because SpotifyHandler
being a class can have subclasses. If you create a sub class that inherits from SpotifyHandler
it will not conform to the protocol because Self
has got to simultaneously have type SpotifyHandler
and the type of the subclass e.g.
class AdFreeSpotifyHandler: SpotifyHandler {}
let foo = AdFreeSpotifyHandler.shared
What type would you expect foo
to have?
There are two solution: you can do what @lorum ipsum suggests and make the shared instance of type MusicHandler
or you can stop people from subclassing SpotifyHandler
by making it final
i.e.
final class SpotifyHandler : MusicHandler {
static var shared: SpotifyHandler = SpotifyHandler() // No error
// rest of the implementation
}
As a general rule, as soon as your protocol has any Self
requirements or an associated type, you can't make non final classes conform to it.
I don't recommend @lorum ipsum's approach on the whole because if I have
class AdFreeSpotifyHandler: SpotifyHandler
{
}
let foo = AdFreeSpotifyHandler.shared
We know foo
is a MusicHandler
, but it's not an AdFreeSpotifyHandler
which is what you'd really want in the above fragment.