I'm working my way into the injection dependency issue. Now the question arises how to use the interface segregation principle in the case of the delegate pattern? I am using the Swinject framework for dependency injection. How can I solve this?
class Client {
private var interface: ParentInterface
...
func execute() {
interface = globalContainer.resolve(ParentInterface.self)
interface?.parentMethod()
}
}
protocol ParentInterface {
func parentMethod()
}
class Parent: ParentInterface, ParentDelegate {
// Dependency
private var child: Child? // 🚨 I WANT TO USE AN PROTOCOL HERE, NOT THE CLASS
init(childReference: Child) {
self.child = childReference
self.child?.delegate = self // 🚨 But if I use the protocol I cant access the delegate property
}
public func parentMethod() {
let result = calcSomething()
// Access child class interface method
child?.childMethod(result)
}
...
}
The child class, nothing unusual so far.
protocol ParentDelegate: class {
func delagteMethod(value: Double)
}
protocol ChildInterface {
func childMethod(_ result: Double)
}
class Child: ChildInterface {
weak var delegate: ParentDelegate?
...
private func delagteMethod() {
delegate?.delagteMethod(value: someValue)
}
}
But to inject the dependencies properly I need a protocol and not a direct class reference, right? Like this:
// 🚨 Would like to
container.register(ParentInterface.self) { r in
Parent(childInterface: r.resolve(ChildInterface.self)!)
}
// 🚨 Only way I get it working without interface
container.register(ParentDelegate.self) { r in
Parent(childClass: Child())
}
container.register(ChildInterface.self) { _ in Child() }
.initCompleted { r, c in
let child = c as! Child
child.delegate = r.resolve(ParentDelegate.self)
}
In short, I'm going around in circles. If I use an interface for the child class I can't access the delegate property, if I use a class reference I can't reasonably to mock/stub the interface methods.
What am I missing? Thanks a lot in advance!
I would recommend removing self.child?.delegate = self from the Parent constructor for two reasons:
Shouldn't the DI framework be responsible for wiring all the dependencies?
In Swinject specifically, this may lead to incorrect dependencies, see the remark in docs
Pattern that I tend to use is more or less what you came up with:
class Parent: ParentInterface, ParentDelegate {
init(child: ChilInterface) { self.child = child }
}
class Child: ChildInterface {
weak var parent: ParentDelegate?
}
container.register(ParentInterface.self) { Parent(child: $0.resolve(ChildInterface.self)!) }
.implements(ParentDelegate.self)
container.register(ChildInterface.self) { _ in Child() }
.initCompleted { ($1 as! Child).delegate = $0.resolve(ParentDelegate.self) }
Additionally, if you wanted to get rid of force casting, you could do something like:
container.register(Child.self) { _ in Child() }
.implements(ChildInterface.self)
.initCompleted { $1.delegate = $0.resolve(ParentDelegate.self) }
Related
I would like to make some abstract protocol with generic types
protocol UseCase {
associatedtype P
associatedtype R
func execute(params: P) async throws -> R
}
Then I would like to make some concrete protocol with some types
protocol TestUseCase: UseCase where P == Int, R == Int { }
And then use it to declare some implementation to use it on another side
class TestUseCaseImpl: TestUseCase {
func execute(params: Int) async throws -> Int {
// some impl
}
}
When I try to use it inside of my view model I got such error:
class ViewModel {
private let testUseCase: TestUseCase // error
init(testUseCase: TestUseCase) { // errror
self.testUseCase = testUseCase
}
}
Protocol 'TestUseCase' can only be used as a generic constraint because it has Self or associated type requirements
To fix such problem I can declare generic ViewModel with some use case type like this:
class ViewModel<UseCase: TestUseCase> {
private let testUseCase: UseCase
init(testUseCase: UseCase) {
self.testUseCase = testUseCase
}
}
Problem around ViewModel - it's possible to have many use cases inside of ViewModel.
How to implement such idea without generic ViewModel?
You need to use the any keyword. This is only supported in Swift >= 5.7.
Note: it is very unlikely that you want a type called ViewModel. Nest type Model inside of View if that's what it is.
class ViewModel {
private let testUseCase: any TestUseCase
init(testUseCase: some TestUseCase) {
self.testUseCase = testUseCase
}
}
I am new in swift moved from java. And some implementaion of dessign patterns confuse me.
For example I have presudo pattern observer (callback) in java code (there is example below). Namely UI passed own listener to Manager class and listen callbacks isConnected and isDisconnected. If a callback is executed UI class shows certain message "isConnected" or "isDisconnected"
public class UI{
private Manager mManager;
void createManager(){
mManager = new Manager(mManagerLister);
}
public void showMessage(String aMsg){
print(aMsg)
}
private final IManagerListener mManagerLister = new IManagerListener{
void isConnected(){
this.showMessage("isConnected")
}
void isDisconnected(){
this.showMessage("isConnected")
}
}
}
public class Manager{
interface IManagerListener{
void isConnected();
void isDisconnected();
}
private final mListener;
public Manager(IManagerListener aListener){
mListener = aListener;
}
}
How to correctly port this java code to swift code? I tries to port but error message Value of type 'UI' has no member 'showMessage' is shown
public class UI{
var manager: Manager?
var managerListener: IManagerListener?
func createManager(){
managerListener = ManagerListenerImp(self)
manager = Manager(managerListener)
}
public func showMessage(msg: String){
print(msg)
}
class ManagerListenerImp: IManagerListener{
weak var parent: UI
init(parent : UI ){
self.parent = parent
}
func isConnected(){
parent.showMessage("isConnected")
// Value of type 'UI' has no member 'showMessage'
}
..........
}
}
Perhaps exists more gracefully a way to use callbacks and my way is not correctly?
There are multiple ways to achieve it.
Delegate Pattern (Using Protocols which are nothing but interfaces
in Java)
Using Blocks/Closures
Using KVO
Because you have used interfaces am elaborating on Delegate pattern below.
Modify your code as below
Declare a protocol
#objc protocol ManagerListenerImp {
func isConnected()
}
Declare a variable in Manager class
class Manager {
weak var delegate : ManagerListenerImp? = nil
}
Confirm to ManagerListenerImp in your UI class
extension UI : ManagerListenerImp {
func isConnected () {
//your isConnected implementation here
}
}
Pass UI instance (self in swift and this in JAVA to manager class)
func createManager(){
manager = Manager()
manager?.delegate = self
}
Finally, whenever you wanna trigger isConnected from Manager class simply say
self.delegate?.isConnected()
in your Manager class
Hope it helps
I'm a bit confused by which class has a reference to which, but that shouldn't be too hard to change in the following example.
You might be looking for an Observer Pattern. This can have multiple objects listening to the same changes:
1. ManagerStateListener Protocol
Protocol to be implemented by any class that should react to changes to the state of the Manager
protocol ManagerStateListener: AnyObject {
func stateChanged(to state: Manager.State)
}
2. Manager Class
The Manager class contains:
Its state
A list with listeners
Methods for adding, removing and invoking the listeners
An example class that implements the ManagerStateListener protocol
class Manager {
/// The possible states of the Manager
enum State {
case one
case two
case three
}
/// The variable that stores the current state of the manager
private var _currentState: State = .one
var currentState: State {
get {
return _currentState
}
set {
_currentState = newValue
/// Calls the function that will alert all listeners
/// that the state has changed
invoke()
}
}
/// The list with all listeners
var listeners: [ManagerStateListener] = []
/// A specific listener that gets initialised here
let someListener = SomeListener()
init() {
addListener(someListener) /// Add the listener to the list
}
/// Method that invokes the stateChanged method on all listeners
func invoke() {
for listener in listeners {
listener.stateChanged(to: currentState)
}
}
/// Method for adding a listener to the list of listeners
func addListener(_ listener: ManagerStateListener) {
listeners.append(listener)
}
/// Method for removing a specific listener from the list of listeners
func removeListener(_ listener: ManagerStateListener) {
if let index = listeners.firstIndex(where: { $0 === listener }) {
listeners.remove(at: index)
}
}
}
3. SomeListener Class
An example listener that implements the ManagerStateListener protocol, held by the Manager class
class SomeListener : ManagerStateListener {
func stateChanged(to state: Manager.State) {
/// Do something based on the newly received state
switch state {
case .one:
print("State changed to one")
case .two:
print("State changed to two")
case .three:
print("State changed to three")
}
}
}
I hope this is of any help.
So I'm new to iOS development and have been working on minor changes to an app at my internship that has a relatively large objective-c code base. I've been learning swift from Treehouse(Wow, love them!) and I just learned about protocols. Currently, they should be used in certain instances and the instructor used this example.
Say you have a company with two different types of employees: Salary and Hourly(Pretty common). Now, they both would inherit from a super class called Employee and both would have to call a function called "pay" which would pay the employee. How do you enforce these classes to implement that function? Sure, use a protocol but that would require you to remember to add that to the function declaration. Is there a way to just add the protocol to the super class "Employee" and then whatever inherits from that class would have to follow that protocol that's part of that superclass. Is there another way to do this? Thanks!
What you are looking for is an abstract class. The purpose of an abstract class is to behave as a base class for concrete classes to inherit from, but an abstract class cannot be instantiated directly.
If Employee was an an abstract class then any attempt to actually instantiate an instance of Employee would be reported as an error by the compiler. You would need to instantiate a concrete subclass of Employee, such as SalariedEmployee or HourlyEmployee.
The definition of the Employee class would include that the calculatePay method was required and again a compile time error would occur if a concrete subclass did not implement that method.
Now, the bad news. Neither Objective-C nor Swift supports abstract classes.
You can provide a similar kind of class by providing an implementation of a method that throws an exception if it isn't overridden by a subclass. This gives a runtime error rather than a compile time error.
e.g.
class Employee {
var givenName: String
var surname: String
...
init(givenName: String, surname: String) {
self.givenName = givenName
self.surname = surname
}
func calculatePay() -> Float {
fatalError("Subclasses must override calculatePay")
}
}
class SalariedEmployee: Employee {
var salary: Float
init(givenName: String, surname: String, annualSalary: Float) {
salary = annualSalary
super.init(givenName: givenName, surname: surname)
}
override func calculatePay() -> Float {
return salary/12 // Note: No call to super.calculatePay
}
}
Whether the calculatePay is part of the base class or assigned to the base class through an extension that adds conformance to a protocol, the result is the same;
The Employee class will need a default implementation of the function that generates some sort of error
Failure of a subclass to implement the method will not cause a compile time error
You could assign a protocol, say, Payable to each subclass individually, but then as the protocol was not part of the base class, you couldn't say something like:
var employees[Employee]
for e in employees {
let pay = e.calculatePay()
}
You would have to use the slightly more complicated:
for e in employees {
if e is Payable {
let pay = e.calculatePay()
}
}
Unfortunately abstract functions are not yet supported. A possible workaround is to launch a fatalError when such function is not overridden by a subclass, doing so:
protocol YourProtocol {
func pay()
}
class Employee: YourProtocol {
func pay() {
fatalError("Must Override")
}
}
class SubEmployee: Employee {
func pay() {
print("stuff here")
}
}
My approach to this is to include the delegate as a parameter in the class initializer. See the code below:
protocol ProtocolExample {
func somethingNeedsToHappen()
}
// typical class example with delegate property for the required protocol
class ClassExampleA {
var delegate: ProtocolExample!
init() {
}
func aCriticalMethodWithUpdates() {
delegate.somethingNeedsToHappen()
}
}
// use class example in a view controller. Can easily forget to invoke the delegate and protocol
class MySampleViewControllerA: UIViewController {
var classExampleA : ClassExampleA!
func loadMyData() {
classExampleA = ClassExampleA()
}
}
// an alternative approach for the class is to include the delegate parameter in the initializer.
class ClassExampleB {
var delegate: ProtocolExample!
init(delegateForUpdates: ProtocolExample) {
delegate = delegateForUpdates
}
func doSomething() {
delegate.somethingNeedsToHappen()
}
}
// go to use it and you're reminded that the parameter is required...
class MySampleViewControllerB: UIViewController {
var classExampleB: ClassExampleB!
func loadMyData() {
classExampleB = ClassExampleB() // error: Missing argument for parameter 'delegateForUpdates' in call
}
}
// so to avoid error:
class MySampleViewControllerC: UIViewController {
var classExampleB: ClassExampleB!
func loadMyData() {
classExampleB = ClassExampleB(delegateForUpdates: <#ProtocolExample#>)
}
}
I have a Swift 3 app using Typhoon which successfully injects a NSObject-derived object into a property in several objects deriving from UIViewController. I added code to inject the same object into an object that derives only from NSObject, and the property is always nil upon executing any code in that object.
My assembly looks like this:
class CoreComponents: TyphoonAssembly {
dynamic func appInitializer() -> Any {
return TyphoonDefinition.withClass(AppInitializer.self) {
(definition) in
definition!.injectProperty(Selector(("settings")), with: self.provideSettings())
}
}
dynamic func sideMenu() -> Any {
return TyphoonDefinition.withClass(SideMenuTableView.self) {
(definition) in
definition!.injectProperty(Selector(("settings")), with: self.provideSettings())
}
}
dynamic func mapViewController() -> Any {
return TyphoonDefinition.withClass(MapViewController.self) {
(definition) in
definition!.injectProperty(Selector(("settings")), with: self.provideSettings())
}
}
dynamic func provideSettings() -> Any {
return TyphoonDefinition.withClass(Settings.self) {
(definition) in
definition!.scope = TyphoonScope.singleton
}
}
}
In that, only the injection specified by the first method fails to inject. The object it wants to inject into is declared like this:
class AppInitializer: NSObject {
var settings: Settings? // Injected property
// Other code here
}
Does anybody see anything I'm doing wrong?
Here is the relevant portion of a class where the injection works:
class SideMenuTableView: UITableViewController {
var settings: Settings? // Injected property
// Other code
}
Thanks!
To get this to work, I abandoned the Objective-C compatible version of Typhoon and switched to the "pure swift" version of Typhoon at their website. The setup is more work, but the code ends up simplified:
class CoreComponents: Assembly {
func provideSettings() -> Definition {
return Definition(withClass: Settings.self) { d in
d.setScope(Definition.Scope.Singletone)
}
}
}
class AppInitializer: NSObject {
let settings = CoreComponents.assembly.provideSettings()
// More code here
}
That required instantiating the assembly in my AppDelegate's "didFinishLaunchingWithOptions" method with this line:
Typhoon.activateAssemblies()
I've seen some discussions about this problem, but have not read a satisfactory explanation. Can anybody tell me why this does not work?
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child : Parent<Int> {}
let c = Child(data: 4)
The last line gives the error:
'Child' cannot be constructed because it has no accessible initializers
Do I really need to implement the initializer just to call super?
Edit:
To give a bit of context, the real code looks closer to the below. I have an Action class which uses generics, because I have another bit of code which can chain actions together and I want to use Swift's type safety to ensure that actions can be chained. Then I have a bunch of subclasses classes (e.g. CustomAction). I am looking for a way to avoid overriding the init method in each of the subclasses. Or alternatively, I want to understand why that's not possible.
class Action<Input, Output> {
var cachedOutput:Output?
init(cachedOutput:Output?) {
self.cachedOutput = cachedOutput
}
}
protocol CustomInput {}
protocol CustomOutput {}
class CustomAction : Action<CustomInput, CustomOutput> {
}
yes, you really need to override init method ..
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<T> : Parent<T> {
override init(data: T) {
super.init(data: data)
}
}
let c = Child(data: 4) // Child<Int>
let c2 = Child(data: "alfa") // Child<String>
what are the errors ...
// what is the type T ? it is undeclared!
class Child2: Parent2<T> {}
// how to specialize non-generic type Parent ? how to create it?
// i need an initializer in class Child3 ... Hm ...
class Child3: Parent<Int> {}
// cannot specialize non-generic type 'Parent'
class Child3: Parent<Int> {
override init(data: Int) {
super.init(data: data)
}
}
// So, Child3 must be of the same specialized type as Parent!!
that is terrible, isn't it? so look at my final example !
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<Double> : Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4) // Child<Int> !!!!!
let d = Child(data: true) // Child<Bool> !!!
in your case it works like
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child: Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4)
print(c.dynamicType) // Child :-)
This now works in Swift 3. My original example now compiles. There is no mention of this in the Swift 3 Language changes, so I can only assume that this was a bug.