Injecting a single dependency to a view model - swift

I want the LoginViewModel to have a single dependency of LoginViewModelService.

LoginViewModelService in itself cannot have an implementation.
Its methods will be implemented by both the LoginController and AccountController.
The challenge I’m having right now is, when I instantiate the LoginViewModel which has the single dependency of LoginViewModelService, there is no way I can inject Login Controller and the Account Controller as its implementation class even though they implement the methods of the LoginViewModelService.
To get a better idea I have attached the code from my playground.
What I hope I can do is, there is some way to accomplish the single dependency structure but if thats impossible I would like your recommendation on how I should approach this. Basically my objective is that the service being used by the Login View Model should not have access to method that it does not need, for example Logout() from the Login Controller class or the delete() from the Account controller class.
class AccountController {
func create() {print("Create")}
func get() {print("get")}
func update() {print("update")}
func delete() {print("delete")}
}
// protocol LoginController {
// ...
// }
class LoginController {
func apple() {print("apple")}
func google() {print("google")}
func phone() {print("phone")}
func logout() {print("logout")}
}
protocol LoginViewModelService {
func apple()
func google()
func phone()
func get()
}
extension LoginViewModelService {
func apple(){}
func google(){}
func phone(){}
func get(){}
}
class ViewModel {
init(serivice: LoginViewModelService) {}
}
let accountController = AccountController()
let loginController = LoginController()
extension AccountController: LoginViewModelService {}
extension LoginController: LoginViewModelService {}
// -------------------------This is what I hope I can do-------------
let vm = ViewModel(serivice: accountController & loginController)
// -------------------------------------------------------------------

After playing with it I came up with this solution.
protocol CurrentProfileProvider {
func getMyProfile()
}
protocol ProfileCreator {
func create()
}
protocol ProfileUpdater {
func update(onlyLocally: Bool)
}
protocol ProfileDeleter {
func delete()
}
protocol ConversationCreator {
func create()
}
protocol ListenersProvider {
func listenersList() -> [String]
}
protocol AccountController:CurrentProfileProvider,ProfileCreator,ProfileUpdater,ProfileDeleter {}
class AccountControllerImpl:AccountController {
func update(onlyLocally: Bool) {}
func create() {print("Create")}
func getMyProfile() {print("Get my profile")}
func delete() {print("delete")}
}
protocol LoginProvider {
func apple()
func google()
func phone()
}
protocol Logout {
func logout()
}
protocol AuthenticationController: LoginProvider, Logout {}
class AuthenticationControllerImpl:AuthenticationController {
func apple() {print("apple")}
func google() {print("google")}
func phone() {print("phone")}
func logout() {print("logout")}
}
class ViewModel {
let service: Service
init(service: Service) {
self.service = service
}
}
extension ViewModel {
class Service {
let ac: CurrentProfileProvider
let lc: LoginProvider
init(ac: CurrentProfileProvider, lc: LoginProvider) {
self.ac = ac
self.lc = lc
}
}
}
let ac = AccountControllerImpl()
let lc = AuthenticationControllerImpl()
let service = ViewModel.Service(ac:ac ,lc: lc)
let vm = ViewModel(service: service)

Related

Call delegate function from another class

I created a custom class with function delegation. Is it possible to call the didPlaneUpdate function in ViewController1 by calling the addToPlane () function from ViewController2 ? Below is my code:
// CustomClass.swift:
protocol PlaneDelegate: class {
func didPlaneUpdate()
}
class Plane {
static let shared = Plane()
weak var delegate: PlaneDelegate?
public init() { }
public func addToPlane() {
updatePlane()
}
public func updatePlane() {
delegate?.didPlaneUpdate()
}
}
// ViewController1.swift:
class ViewControllerPlane: UIViewController, PlaneDelegate {
var plane = Plane()
override func viewDidLoad() {
super.viewDidLoad()
plane.delegate = self
}
func didPlaneUpdate() {
print("test updated")
}
// ViewController2.swift:
var plane = Plane()
plane.addToPlane()
or
// ViewController2.swift:
Plane.shared.addToPlane()
It doesn't work.

Swift 4.3 - protocols passing data between 2 files

I am struggling with understanding how protocols work. I have 2 files and want to use protocol to pass data... Here's what I am doing:
In ViewController.swift
protocol workingProtocol { func myFunc(strValue: String)}
class ViewController: UIViewController {
var interactor = workingProtocol
#objc func doneBtn() {
interactor.myFunc(strValue: "str")
}
}
In Interactor.swift
class Interactor {
func myFunc(strValue: String) {
print(strValue)
}
}
The data is not printing from Interactor.swift
Unfortunately I can't see how you inject interaction class, also your code has some problem with syntax. Here is how it should look:
protocol WorkingProtocol: AnyObject {
func myFunc(strValue: String)
}
final class ViewController: UIViewController {
var interactor: WorkingProtocol
#objc func doneBtn() {
interactor.myFunc(strValue: "str")
}
}
final class Interactor: WorkingProtocol {
func myFunc(strValue: String) {
print(strValue)
}
}
And how to use:
let interactor: WorkingProtocol = Interactor()
let vc = ViewController(interactor: interactor)
vc.doneBtn()
Protocols defines a blueprint of methods, properties and other requirements that suite a piece of functionality.
This is an example about how it works based on your code
protocol ProtocolName {
func functionName(strValue: String)
}
class ViewController {
var interactor: ProtocolName? = nil
#objc
fileprivate func doneBtn() {
interactor?.functionName(strValue: "Passing data to interactor using protocols")
}
}
class Interactor: ProtocolName {
func functionName(strValue: String) {
print("Showing value\n", strValue)
}
}
let interactor = Interactor()
let viewController = ViewController()
viewController.interactor = interactor
viewController.doneBtn()
Another example:
protocol ProtocolName {
func functionName(strValue: String)
}
class ViewController1 {
let interactor = Interactor1()
/// Init or viewDidLoad() if you're using ViewController classes.
init() {
interactor.delegate = self
}
}
extension ViewController1: ProtocolName {
func functionName(strValue: String) {
print("Printing the value: \(strValue)")
}
}
class Interactor1 {
var delegate: ProtocolName?
func someAction() {
delegate?.functionName(strValue: "Executed action in interactor.")
}
}
let vc = ViewController1()
vc.interactor.someAction()

Same protocol objects, but not the same functions

I'm trying to build an API pattern.
I have several different APIs that have the same functions for some, and some have additional functions that other APIs do not have and should not have access.
The problem with the protocol is that they all have access. How to limit access if the function is not override?
apiRequest:
class APIRequest {
static func loginCli(completion: (UserClinic) -> ()) {
}
static func loginFami(completion: (User) -> ()) {
}
static func loginDavid(completion: (UserDavid) -> ()) {
}
}
Protol and API:
protocol API {
func login<T>(completion: (T) -> ())
func saveClient()
}
extension API {
func saveClient() {
saveClient()
}
}
class FirstAPI: API {
func login<T>(completion: (T) -> ()) {
APIRequest.loginFami { (user) in
}
}
}
class SecondAPI: API {
func login<T>(completion: (T) -> ()) {
APIRequest.loginCli { (user) in
}
}
}
class ThreeAPI: API {
func login<T>(completion: (T) -> ()) {
APIRequest.loginDavid { (user) in
}
}
func saveClient() {
// Save client
}
}
View model:
class LoginViewModel {
var apiClient: API
init() {
// Below its good
apiClient = ThreeAPI()
apiClient.saveClient()
// Below its not good
apiClient = FirstAPI()
apiClient.saveClient() // I want this is not accessible
// Below its good
apiClient = SecondAPI()
apiClient.saveClient() // I want this is not accessible
}
}
In my case I need only the third API to have access to the function saveClient()
If you don't want the saveClient method on FirstAPI and SecondAPI, you need to extract that method to a separate protocol. Something like:
protocol APIThatCanLogin {
func login<T>(completion: (T) -> ())
}
protocol APIThatCanSave {
func saveClient()
}
class FirstAPI: APIThatCanLogin { }
class SecondAPI: APIThatCanLogin { }
class ThreeAPI: APIThatCanLogin, APIThatCanSave { }
But perhaps you should reconsider using protocols. At least in this example it doesn't offer any benefits.
Try this.
protocol API {
func login<T>(completion: (T) -> ())
}
extension API where Self: ThreeAPI {
func saveClient() {
}
}
In the above code, protocol API extension is only available to object of type ThreeAPI.
I've used ThreeAPI directly here. You can make a common type for all the classes that need saveClient() and use that type in place of ThreeAPI
Usage:
apiClient = ThreeAPI()
(apiClient as? ThreeAPI)?.saveClient()
apiClient = FirstAPI() //saveClient() not available
apiClient = SecondAPI() //saveClient() not available
Edit-1:
Create a CommonAPI parent class for all the classes that need saveClient() method.
class CommonAPI {
func saveClient() {
print("CommonAPI saveClient()")
}
}
Inherit ThreeAPI and FourAPI from CommonAPI
class ThreeAPI: CommonAPI, API {
func login<T>(completion: (T) -> ()) {
}
override func saveClient() {
print("ThreeAPI saveClient()")
}
}
class FourAPI: CommonAPI, API {
func login<T>(completion: (T) -> ()) {
}
override func saveClient() {
print("FourAPI saveClient()")
}
}
Simply use a switch statement to call saveClient() on all the objects of type CommonAPI.
apiClient = FourAPI()
apiClient = ThreeAPI()
apiClient = FirstAPI()
apiClient = SecondAPI()
switch apiClient {
case is CommonAPI:
(apiClient as? CommonAPI)?.saveClient()
default:
break
}
In this case, you won't need to parse each and every type.
There is not going to be a solution to this as described. The type API means "has the following methods and properties." You cannot assign something to a variable of type API and then say "but it doesn't have some of the methods." That violates LSP, which is the foundation of these kinds of types. Consider the following code:
func save(api: API) {
api.saveClient()
}
If the feature you're describing could be implemented, should this compile or not? If it did compile, and then it were called with FirstAPI(), what should happen then?
If you want saveClient to be a no-op (to do nothing), then that's very straightforward. Provide an empty default implementation:
extension API {
func saveClient() {}
}
If you want some APIs to be savable, and some not to be savable, then that's straightforward too:
protocol API {
func login<T>(completion: (T) -> ())
}
protocol SavableAPI: API {
func saveClient()
}
But the init method you've written cannot be made to work. It violates the underlying goal of having a type.

Ambiguous functions in multiple protocol extensions?

I have multiple protocols that have the same function name. Some protocols have associated types, where I can't figure out how to call the functions as I do in non-generic protocols. I get the error: Protocol 'MyProtocol1' can only be used as a generic contraint because it has Self or associated type requirements
Here's what I'm trying to do:
protocol Serviceable {
associatedtype DataType
func get(handler: ([DataType] -> Void)?)
}
struct PostService: Serviceable {
func get(handler: ([String] -> Void)? = nil) {
print("Do something...")
}
}
protocol MyProtocol1: class {
associatedtype ServiceType: Serviceable
var service: ServiceType { get }
}
extension MyProtocol1 {
func didLoad(delegate: Self) {
print("MyProtocol1.didLoad()")
}
}
protocol MyProtocol2: class {
}
extension MyProtocol2 {
func didLoad(delegate: MyProtocol2) {
print("MyProtocol2.didLoad()")
}
}
class MyViewController: UIViewController, MyProtocol1, MyProtocol2 {
let service = PostService()
override func viewDidLoad() {
super.viewDidLoad()
didLoad(self as MyProtocol1) // Error here: Protocol 'MyProtocol1' can only be used as a generic contraint because it has Self or associated type requirements
didLoad(self as MyProtocol2)
}
}
How can I specifically call the function from a generic protocol extension?
It's simple to achieve by turning the protocol into a generic (see below), or by creating a type eraser for these protocols, but this very strongly suggests that you have a design problem and you should redesign your classes and/or extensions. A collision like this suggests strongly that MyStruct is doing too many things itself because it's being pulled in multiple directions by MyProtocol1 and MyProtocol2. There should likely be two objects here instead. (Composition rather than inheritance.)
class MyStruct: MyProtocol1, MyProtocol2 {
let service = PostService()
func prot1Load<T: MyProtocol1>(t: T) {
t.didLoad()
}
func prot2Load<T: MyProtocol2>(t: T) {
t.didLoad()
}
init() {
prot1Load(self)
prot2Load(self)
}
}
To your particular example in the comments, I would use composition rather than inheritance. You're treating protocols like multiple-inheritance, which is almost never right. Instead compose out of things that conform to a protocol.
protocol LoadProviding {
func load()
}
struct MyLoader1: LoadProviding {
func load() {
print("MyLoader1.didLoad()")
}
}
struct MyLoader2: LoadProviding {
func load() {
print("MyLoader2.didLoad()")
}
}
protocol Loader {
var loaders: [LoadProviding] { get }
}
extension Loader {
func loadAll() {
for loader in loaders {
loader.load()
}
}
}
class MyStruct: Loader {
let service = PostService()
let loaders: [LoadProviding] = [MyLoader1(), MyLoader2()]
init() {
loadAll()
}
}
Of course you don't really have to have LoadProviding be a full struct. It could just be a function if that's all you need:
typealias LoadProviding = () -> Void
func myLoader1() {
print("MyLoader1.didLoad()")
}
func myLoader2() {
print("MyLoader2.didLoad()")
}
protocol Loader {
var loaders: [LoadProviding] { get }
}
extension Loader {
func loadAll() {
for loader in loaders {
loader()
}
}
}
class MyStruct: Loader {
let service = PostService()
let loaders: [LoadProviding] = [myLoader1, myLoader2]
init() {
loadAll()
}
}
If you have time to wade through a video on the subject, you may be interested in the Beyond Crusty: Real World Protocols talk from dotSwift. It's about this and similar problems.

Protocol subclass implementation

I have the following code in which I'm trying to force abstraction (abstract class/conformance):
PlayerProtocol:
protocol PlayerProtocol {
func play();
func stop();
func pause();
func getVolume() -> UInt32;
func setVolume(level: UInt32);
func isPaused() -> Bool;
func isStopped() -> Bool;
func onSessionResume();
func onSessionInterupt();
}
BasicPlayer:
class BasicPlayer : PlayerProtocol {
//some variables here..
init() {
//init some variables here..
}
func play() {
fatalError("play() - pure virtual function called.");
}
func stop() {
fatalError("stop() - pure virtual function called.");
}
func pause() {
fatalError("stop() - pure virtual function called.");
}
func getVolume() -> UInt32 {
fatalError("getVolume() - pure virtual function called.");
}
func setVolume(level: UInt32) {
fatalError("setVolume() - pure virtual function called.");
}
func isPaused() -> Bool {
fatalError("isPaused() - pure virtual function called.");
}
func isStopped() -> Bool {
fatalError("isStopped() - pure virtual function called.");
}
func onSessionInterupt() {
fatalError("onSessionInterupt() - pure virtual function called.");
}
func onSessionResume() {
fatalError("onSessionResume() - pure virtual function called.");
}
}
AudioPlayer:
class AudioPlayer : BasicPlayer, PlayerProtocol {
private var device: COpaquePointer = nil;
private var context: COpaquePointer = nil;
private var source: ALuint = 0;
private var buffer: ALuint = 0;
private var interrupted: Bool = false;
private var volume: Float = 50;
override init() {
super.init();
//..
}
deinit {
//..
}
override func play() {
//..
}
override func stop() {
//..
}
override func pause() {
//..
}
override func setVolume(volume: UInt32) {
//..
}
override func getVolume() -> UInt32 {
//..
}
func isPlaying() -> Bool {
//..
}
override func isPaused() -> Bool {
//..
}
override func isStopped() -> Bool {
//..
}
func isAudioPlaying() -> Bool {
return AVAudioSession.sharedInstance().otherAudioPlaying;
}
override func onSessionInterupt() {
self.pause();
}
override func onSessionResume() {
self.play();
}
func setData(buffer: ALuint, source: ALuint) {
self.buffer = buffer;
self.source = source;
}
}
But even though I specified that AudioPlayer implements the PlayerProtocol, it doesn't force me to implement all the member functions like play, stop, etc.. I can remove them and it doesn't complain. It's probably because the super class implements it but I can't figure out how to leave it un-implemented in the super class and allow the derived classes to do the implementation instead.
Basically, BasicPlayer is supposed to be abstract and any class inheriting it must implement "certain" members (not all). OnSessionInterrupt isn't implemented in the derived class. I need it to error.
How can I do this? How can I get it to error at compile time on un-implemented members in the derived classes but not the abstract class?
AudioPlayer is a subclass of a class that already conforms to PlayerProtocol. Because you've implemented all of the methods in the superclass, those implementations are available in your subclass, so you aren't obliged to redeclare them.
It looks to me like you are conceptually abstracting your interface in two separate ways: through a protocol and through an abstract superclass. This might be superfluous? One or the other should be able to serve your purposes.