Same protocol objects, but not the same functions - swift

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.

Related

Mocking a generic class in Swift

I have a class for that I want to write unit test. This class has a dependency, which is a generic class, that I want to mock.
This is the class I want to test.
class ClassToTest: SomeProtocol {
private let dependency = DependencyClass<AnotherClass>()
init() {
//Some Code Here
}
}
class DependencyClass<General: AnotherClassProtocol> {
func getData(from general: General) -> AnyPublisher<Data, Error> {
//Some Code Here
}
}
What I tried
class ClassToTest: SomeProtocol {
private let dependency: DependencyClassProtocol
init(_ dependency: DependencyClassProtocol = DependencyClass<AnotherClass>()) {
//Some Code Here
self. dependency = dependency
}
}
class DependencyClass<General: AnotherClassProtocol>: DependencyClassProtocol {
func getData(from general: General) -> AnyPublisher<Data, Error> {
//Some Code Here
}
}
protocol DependencyClassProtocol {
associatedtype General
func getData(from general: General) -> AnyPublisher<Data, Error>
}
This approach gives me error "Protocol can an only be used as a generic constraint because it has Self or associated type requirements".
How can I Mock the DependencyClass to test the exact behaviour of ClassToTest.

Implement Decorator Pattern with generics in Swift

I am new to Swift, but I have plenty of experience in other languages like Java, Kotlin, Javascript, etc. It's possible that what I want to do is not supported by the language, and I've poured over the Swift Language Guide looking for the answer.
I want to implement the decorator pattern, using generics. I easily did this in Kotlin, and I'm porting the library to Swift.
class Result<T> {
let result: T?
let error: NSError?
init(result: T?, error: NSError?) {
self.result = result
self.error = error
}
}
protocol DoSomething {
associatedtype T
func doSomething() -> Result<T>
}
protocol StoreSomething {
associatedtype T
func storeSomething(thing: Result<T>)
}
/*
* DOES NOT COMPILE
*/
class StoringSomething<T> {
private let delegate: DoSomething
private let store: StoreSomething
init(delegate: DoSomething, store: StoreSomething) {
self.delegate = delegate
self.store = store
}
func doSomething() -> Result<T> {
let result = delegate.doSomething()
store.storeSomething(thing: result)
return result
}
}
I get a Protocol 'DoSomething' can only be used as a generic constraint because it has Self or associated type requirements error from the compiler. I've tried using a typealias and other ideas from SO and the Swift manual.
Thanks to #Sweeper's suggestion on associatedtype erasure you can implement the Decorator pattern with generics like so:
class AnyDoSomething<T>: DoSomething {
func doSomething() -> Result<T> {
fatalError("Must implement")
}
}
class AnyStoreSomething<T>: StoreSomething {
func storeSomething(thing: Result<T>) {
fatalError("Must implement")
}
}
class StoringSomething<T>: DoSomething {
private let delegate: AnyDoSomething<T>
private let store: AnyStoreSomething<T>
init(delegate: AnyDoSomething<T>, store: AnyStoreSomething<T>) {
self.delegate = delegate
self.store = store
}
func doSomething() -> Result<T> {
let result = delegate.doSomething()
store.storeSomething(thing: result)
return result
}
}
class DoSomethingNice<T>: AnyDoSomething<T> {
override func doSomething() -> Result<T> {
}
}

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.

Why can't I provide default implementations for Type functions returning Self in protocol when using inheritance?

Why doesn't this work:
protocol Work {
init()
static func make() -> Self
}
extension Work {
static func make() -> Self {
return self.init()
}
}
class Foo : Work {
required init() {}
}
I can make inheritance possible by adding the factory to the object itself:
class Foo : Work {
required init() {}
static func make() -> Self {
return self.init()
}
}
I could also use a non-class or mark the class final, but I'd prefer/am required to use inheritance.
Is it possible to implement a default factory on a protocol so that an inheritable type can conform without implementing it again.
If you want a factory that will initialize objects adhering to a protocol, you should use the always awesome generics!
E.g.
protocol Work {
init()
static func make<T: Work>(type: T.Type) -> T
}
extension Work {
static func make<T: Work>(type: T.Type) -> T {
return T.init()
}
static func make() -> Self {
return make(Self)
}
}
class Foo : Work {
required init() {}
}

Grouping Many Functions in Swift

I've got a conceptual Animal API client class that will interface with a Rest Api below (it may have syntax errors, I'm typing it from my head).
class AnimalApi {
let connectionInfo: ApiConnectionInfo
init(connectionInfo: ApiConnectionInfo) {
self.connectionInfo = connectionInfo
}
func login(username: String, password: String) {
// login stuff
}
func logout() {
// logout stuff
}
func get(url: String) {
}
func post(url: String) {
}
func delete(url: String) {
}
}
// Dogs
extension AnimalApi {
func getAllDogs() -> Dogs {
return get("dogResourceUrl")
}
func deleteDog() { }
func updateDog() { }
}
// Cats
extension AnimalApi {
func getAllCats() { }
func deleteCat() { }
func updateCat() { }
}
Is there a better way to group code in Swift instead of using extensions? There will be dozens of API resources I have to call that are all located on the same API server. I am trying to avoid the following...
let api = AnimalApi()
let dogs = api. // bombarded with all functions here, ideally something like api.Dogs.getAll would be more manageable
I realize that Apple uses extensions to group their code in their Swift API, but is there a better way? Sub classes maybe?
EDIT: I'd like to avoid sub classes if possible. This is because I am planning on having a single global instance of the AnimalApi since it will be accessed throughout the app constantly. Maybe make AnimalAPi members static and have separate classes with static members that contain functions that call the static AnimalApi.
class DogApi {
class func all() { return AnimalApi.get("dogResourceUri") }
}
let dogs = DogApi.all()
The below sample code is an attempt to accomplish your requirement.
Hope it helps.
typealias Task = () -> ()
typealias Api = (getAll: Task, delete: Task, update: Task)
class AnimalApi {
let dogs: Api = {
func getAll() { }
func delete() { }
func update() { }
return (getAll, delete, update)
}()
let cats: Api = {
func getAll() { }
func delete() { }
func update() { }
return (getAll, delete, update)
}()
}
Now you can use it anywhere in the app as follows without getting bombarded with all different functions:
let api = AnimalApi()
let dogs = api.dogs.getAll