Typhoon parameter injection with initalizer crashing app - ios

i want to use Typhoon (GitHub & WebSite) for dependency injection in my app. I use Swift Version 3 and Typhoon 3.6. Unfortunately my app is crashing when I try to initalize an object. I have the following protocol:
Protocol
import Foundation
#objc public protocol Client {
func method()
}
Protocol implementation
import Foundation
public class ClientWhateverImpl : NSObject, Client{
let name : String
init(name: name) {
self.name = name
}
public func method(){
//make something
}
}
Assembly
import Foundation
import Typhoon
public class MyAssembly: TyphoonAssembly {
public dynamic func client() -> AnyObject {
return TyphoonDefinition.withClass(ClientWhateverImpl.self) {
(definition) in
definition!.useInitializer("initWithName:") {
(initializer) in
initializer!.injectParameter(with: "name")
}
} as AnyObject
}
}
Call it somewhere
let myAssembly : MyAssembly = MyAssembly()
myAssembly.activate()
let client = myAssembly.client()
Unfortunately I got the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Method 'initWithName:' not found on 'MyApp.ClientWhateverImpl'. Did you include the required ':' characters to signify arguments?'
I read some posts on stackoverflow about this error but on their side they forget to use the objectice-c method syntax. But in my case I use the objc method "initWithName". Is there something different in swift 3? Has someone the same problem?

Ok. I found the issue. It has something to do with an object which i wanted to inject. It doesn't inherit from NSObject and Typhoon makes something with it and fails:
definition!.useInitializer("initWithObject:") {
(initializer) in
initializer!.injectParameter(with: MyObject())
}
Before:
public class MyObject{
}
Solution:
public class MyObject: NSObject{
}
The documentation even says it:
Every class you want to inject has to be a subclass of NSObject in
some way (either by subclassing or adding #objc modifier).
I just thought the ClientWhateverImpl in my case has to inherit from NSObject. My fault. This is question closed

Related

Testing protocol extension with default implementations

In past projects, I've had an object use constructor injection for the objects it needs to get some other information. For example:
class Foo {
let appInfo: AppInfoType
init(appInfo: AppInfoType) {
self.appInfo = appInfo
}
}
protocol AppInfoType {
func build(bundle: Bundle) -> String?
}
And then if within Foo, information about the app like build is needed, it can use AppInfoType to get that info. I thought I would see what this looked like with protocol extensions.
extension AppInfoType {
func build(bundle: Bundle) -> String? {
return bundle.infoDictionary?[kCFBundleVersionKey as String] as? String
}
}
class Foo: AppInfoType {
}
So now I can achieve the same thing within Foo by just calling build(bundle: Bundle.main). But is there any easy way to test this now? With the first way, I could still create a MockAppInfoType and provide an implementation for build(bundle: Bundle), but now I don't really see a way to do this unless the protocol extension maybe depended on another protocol where I could inject a mock for that protocol.

Generic protocol Swift 4 error

The following code use to work for me at Swift 3.2, but with the latest release of Swift 4 I am getting an strange error I can not get to understand.
I am trying to create a generic protocol like so:
public protocol FactoryComponent {
associatedtype Argument
associatedtype Service
static var factory: (Resolver) -> (Argument) -> Service { get }
}
public extension FactoryComponent {
public typealias Factory = (Argument) -> Service
}
And using it here:
public extension Container {
#discardableResult
public func register<Component: FactoryComponent>(
factory componentType: Component.Type
) -> ServiceEntry<Component.Factory> { // On this line the error shows
return self.register(componentType.Factory.self) { resolver in
componentType.factory(resolver)
}
}
}
Error:
'Component' does not have a member type named 'Factory'; did you mean 'Factory'?
And of course, the auto-fix does not help since the error is useless...
I checked out Swift 4 breaking changes and did not see anything involving generic protocols.
Can somone please help me understand what does this mean?
It seems that a concrete class of FactoryComponentis missing. The Factory type alias can be called only by protocol's concrete classes.
Try create an FactoryComponent concrete class that implements its protocol
The Argument and Service still generic on your implementation and needs an concrete type. My english is very bad, but I hope I've been help you. Check the code bellow if I'm wasn't clear on my answer.
```
class Resolver {}
protocol FactoryComponent {
associatedtype Argument
associatedtype Service
static var factory: (Resolver) -> (Argument) -> Service { get }
}
extension FactoryComponent {
typealias Factory = (Argument) -> Service
}
class ConcreteFactoryComponent: FactoryComponent {
static var factory: (Resolver) -> Factory {
return foo
}
static func foo(_ resolver: Resolver) -> Factory {
let factory: Factory = { argument in return "" }
return factory
}
typealias Argument = String
typealias Service = String
}
let factory: ConcreteFactoryComponent.Factory = { argument in return "" }
```
I believe the error is not really on the line with the return type. The processing of generic signature resolution has changed slightly in Swift 4 due to the new default for #escaping. The fact that the function is calling a self.register function with an identical signature as the function itself is likely the source of the issue.
From other similar instances, I would suggest looking at the signature of the Container.register. If it happens to have an #escaping closure, then the signature of the generic function in the extension should also have one so that it is recognized as an overload.
See this post: https://stackoverflow.com/a/43081214/5237560
[EDIT] I just realized this change was between Swift 2 and 3. Leaving the answer here in case it can provide some inspiration.

Add protocol to super class which will force other classes that inherit from it to implement protocol

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#>)
}
}

Issue with Protocols with Associated Types in Swift

I have one framework ProviderFramework with the following contents:
public class Provider {
public func fun(some: Model) {
}
}
public class Model {
public let id: Int
init(id: Int) {
self.id = id
}
}
and another UserFramework with the following contents:
public protocol ProviderProtocol {
func fun(some: ModelProtocol)
}
public protocol ModelProtocol {
var id: Int {get}
}
What I want is to make the Provider class conform to the ProviderProtocol class. So in a framework that imports both of the previously mentioned frameworks I have this:
extension ProviderFramework.Model: UserFramework.ModelProtocol {}
extension ProviderFramework.Provider: UserFramework.ProviderProtocol {}
Unfortunately, this results in an error for the second conformance.
So, I tried using an associated types and my ProviderProtocol turned into this:
public protocol ProviderProtocol {
associatedtype T: ModelProtocol
func fun(some: T)
}
and the problematic conformance to this:
extension ProviderFramework.Provider: UserFramework.ProviderProtocol {
public typealias T = ProviderFramework.Model
}
Now there aren't any compile errors, but if I want to use the Protocol as a type like this:
class Consumer {
var provider: ProviderProtocol?
}
I again get an error: 'Protocol 'ProviderProtocol' can only be used as a generic constraint because it has Self or associated type requirements'
I would want to be able to do the last thing. Do I have some bug in my code or if not is there some alternative solution for this problem?
Thanks a lot in advance.
According to the second approach, why not use Provider instead of ProviderProtocol? Since you confirmed typealias T as ProviderFramework.Model in the extension of class Provider.
class Consumer {
var provider: Provider?
}

Set of protocols in Swift

Having a Set of a specific interface in java is very useful:
HashSet<MyInterface> mySet;
Is it possible to do something similar in Swift?
I tried unsuccessfully the following:
public protocol DialogDelegate : class, Hashable
{
func myFunction()
}
public final class DialogManager: NSObject
{
private var _dialogDelegates: Set<DialogDelegate>
private override init()
{
_dialogDelegates = Set<DialogDelegate>()
super.init();
}
}
I get the compiler error:
Protocol 'DialogDelegate' can only be used as a generic constraint
because it has Self or associated type requirements
The problem you're having is explained in: What does "Protocol ... can only be used as a generic constraint because it has Self or associated type requirements" mean?
To solve this you could use DialogDelegate as a generic type constraint:
public final class DialogManager<T: DialogDelegate>: NSObject {
private var _dialogDelegates: Set<T>
private override init()
{
_dialogDelegates = Set<T>()
super.init();
}
}
Bear in mind, this won't allow you allowed to mix the types of objects stored in _dialogDelegates; that may or may not be a problem for you.

Resources