How to Mock an object with protected init - ios

I am trying to Mock an amazon services object to perform UnitTesting on a related code. I have done it as follows but each time the init is hit it crashes with error failed:
caught "NSInternalInconsistencyException", "- init is not a valid
initializer
Normally the same object will be created using there factory method, so it seems the initializer is made private or something. How can such object be mocked ?
class MyAWSiOTDataManager : AWSIoTDataManager {
override func publishString(_ string: String, onTopic topic: String, qoS qos: AWSIoTMQTTQoS) -> Bool {
print("publish string called")
return true
}
override init() {
}
}
let manager = MyAWSiOTDataManager()

You must call the designated initializer of AWSIoTDataManager inside your init call and remove the override decorator. That is why you got the error.
You can not mock something by subclassing it. With Swift, mocks are usually provided through a shared protocol. Define a protocol for the interface that you use from the third party library. Create an empty extension on the library with your protocol (there should be no code needed). Then implement a mock object against the protocol for use in your tests.

Related

instance methods from class methods swift

I have a class TestClass and a class & instance method inside it
class TestClass {
class func classMethod(){
print("how do i call instance method from here")
}
func instanceMethod(){
print("call this instance method")
}
}
My question is how do i call that instanceMethod from classMethod??
One way i have noticed is
class func classMethod(){
TestClass().instanceMethod()
}
But,Is this the good way to do?
What you are trying to do rarely makes any sense from a design perspective.
By definition, an instance method operates on an instance of an object.
For example, it might require access to some instance members, or somehow meddle with the state of the object you call the method on.
class methods on the other hand do not require an instance to be able to call them - and should in general only operate on the given parameters, not be dependant on shared state.
If you need to call instanceMethod() in classMethod(), and instanceMethod() does not require any state - why is it not also a class method, or a (global) pure function?
You can pass the instance object as a parameter to the class method, and then call the instance method of the object:
class TestClass {
class func classMethod(obj:TestClass){
print("how do i call instance method from here")
obj.instanceMethod()
}
func instanceMethod(){
print("call this instance method")
}
}
To call the instance method, you need an instance of TestClass. That's what TestClass() is getting you when you call TestClass().instanceMethod().
If you want to call it from a specific instance, you could pass it in as a parameter to the class function: class func classMethodUsingInstance(instance: TestClass)
If you don't need a specific instance for instanceMethod(), maybe consider making it a class method as well.

Typhoon: Inject subclass property into definition from withFactory:selector: injection style

I am using Typhoon to inject dependencies into a subclass of UIViewController. I've found a potential solution to a different question i had which involves doing instantiation of the view controller using the "factory" injection method:
return TyphoonDefinition.withFactory(self.storyboard(), selector:"instantiateViewControllerWithIdentifier:", parameters: { (method) in
method.injectParameterWith("camera-mode-controller")
}, configuration: { (definition) in
definition.injectProperty("cameraProvider", with: self.systemComponents.systemCameraProvider())
})
However, the UIStoryboard signature func instantiateViewControllerWithIdentifier(identifier: String) -> UIViewController indicates that this factory method initializer will create a UIViewController, which does not have the property of my subclass (dynamic var cameraProvider). Therefore, at runtime, the property injection fails with setter not found.
Is there a way to say something like, "create definition with class: and factory: with method: and properties", so that the definition knows the class it is producing is not a UIViewController but in fact, in my case, a CameraModeViewController: UIViewController? Digging in the API docs I see, TyphoonDefinition.withParent:class:configuration: might chainable with the withFactory:selector:parameters: method to produce this effect? However, my attempt:
public dynamic func viewControllerFromID(id: String) -> AnyObject {
return TyphoonDefinition.withFactory(self.storyboard(), selector: "instantiateViewControllerWithIdentifier:") {
(method) in
method.injectParameterWith(id)
}
}
public dynamic func cameraModeViewController() -> AnyObject {
return TyphoonDefinition.withParent(self.viewControllerFromID("camera-mode-controller"), `class`: CameraModeViewController.self) {
(definition) in
definition.injectProperty("cameraProvider", with: self.systemComponents.systemCameraProvider())
}
}
produces the error: 'You can't call a method on the runtime argument being passed in. It has to be passed in as-is' in -[TyphoonInjectionByRuntimeArgument forwardingTargetForSelector:] with selector argument "length". Any thoughts?
I solved this problem for myself: due to a version control problem, the storyboard member with that identifier had forgotten its UIViewController subclass assignment, and as such was NOT in fact castable, and did not have that property. As it turns out, Typhoon works just fine with injecting properties declared in view controller subclasses.

Swift: Unable to override operationDidFinish from GroupOperation class

In a custom subclass of GroupOperation, I'm trying to override operationDidFinish(). When I attempt to implement the function in my subclass, I get this error message:
Method does not override any method from its superclass
If I remove the override keyword, I get
Method 'operationDidFinish(:withErrors:)' with Objective-C selector
'operationDidFinish:withErrors:' conflicts with method
'operationDidFinish(:withErrors:)' from superclass 'GroupOperation'
with the same Objective-C selector
Weirdness. I'm pretty sure my method signature is spot on, and I'm not trying to overload an obj-c method, so all should be well. What gives?
For reference, my class looks like this:
class ServerAuthenticationOperation: GroupOperation {
// properties... initializer stuff...
override func operationDidFinish(operation: NSOperation, withErrors errors: [NSError]) {
print("I finished!")
}
}
I assume you're using Swift 2.
Objective-C does not support method overloading, so you have to select a different name for your method. Or, you can try these options:
Rename the method using the #objc(newMethodName:)
Use #nonobjc
Edit:
It seems working for the repo you provided, you can check it here. https://www.dropbox.com/s/hb07u3hyjhjuews/OverrideTest.zip?dl=0

How to inject dependency via protocol in Typhoon using Swift?

I have a problem with Typhoon dependency injection framework.
My viewcontroller MainViewController depends on dataProvider property that I want to declare as AnyObject corresponding to protocol DataProviderProtocol
class MainViewController: UIViewController {
// Compiler error here
var dataProvider : DataProviderProtocol!
// something more
}
protocol DataProviderProtocol {
func fetchAllBanks(closure : ([BankObject]) -> Void)
}
class TestDataProvider: NSObject, CEDataProviderProtocol {
func fetchAllBanks(closure : ([CEBankObject]) -> Void) {
var resultBanks = ///////code that creates test data
closure(resultBanks);
}
I want this dataProvider property to be injected by the Typhoon and initialized to the corresponding instance of class TestDataProvider, that implements this protocol. But I also have RealDataProvider that also corresponds to the DataProviderProtocol and might be used sometimes
But this code crashes with the message
Can't inject property 'dataProvider' for object
''. Setter selector not
found. Make sure that property exists and writable'
I can inject this property without crashes if I use the property class of TestDataProvider, but this disables the ability to inject different DataProviderProtocol implementations.
I understand this this crash happens because DataProviderProtocol property type is not NSObject successor. But I just can't find a way to declare property as NSObject<DataProviderProtocol> in Swift
I would appreciate any help
P.S. My Assembly class
public class CEAppAssembly:TyphoonAssembly {
//uiviewcontrollers' components assembly
var mainComponentsAssembly : CEMainComponentsAssembly!
/**
UI Dependencies
**/
public dynamic func mainViewController() -> AnyObject {
return TyphoonDefinition.withClass(MainViewController.self) {
(definition) in
definition.injectProperty("dataProvider", with: self.mainComponentsAssembly.dataProvider())
}
}
Typhoon uses the Objective-C run-time introspection and dynamic features. Therefore:
Protocols must be marked with the '#objc' directive.
Types incompatible with Objective-C (ie 'pure swift') can't be injected.
There's more information about this in the Quick Start guide. If you're still having trouble after reviewing and making those changes, let us know.
We plan to release a Pure Swift version of Typhoon in the near future.

Swift init method from JSONJoy protocol not available as Objective-C selector

I'm having an issue with getting the Objective-C selector for a Swift init method that is required to be implemented by the JSONJoy protocol. The reason I want to be able to get the selector for the init method is so that I can instantiate the Swift class programatically based on the class name which doesn't seem possible in the Swift API's so need to go down to Objective-C to do it. I am using the routine at https://gist.github.com/BennettSmith/1613121 to print out all of the methods for each class and their parent classes and the selector is always showing up as "init", but from my understanding it should be showing up as "init:" because it has a parameter.
The JSONJoy protocol is:
public protocol JSONJoy {
init(_ decoder: JSONDecoder)
}
To test, I tried creating my own protocol named "TestProtocol". Below are my results.
init(str: String) {} shows up with a selector of "initWithStr:"
required public init(str: String) {} still shows up with a selector of "initWithStr:"
Taking away the protocol and just implementing the methods directly in my class using a method signature "init(str: String)", it still shows up as "initWithStr:".
All the above tests with and without the "TestProtocol" are generating selectors as expected.
However, no matter how I manipulate the JSONJoy protocol signature it always shows up as plain "init" without any parameters when I list the selectors for the class. I tried removing the "_" and adding additional parameters to the protocol, however it still comes out as "init"
To make a Swift protocol available in Objective-C you need to add the #objc attribute to the protocol declaration.
You can test the Objective-C method signature by using respondsToSelector(), as in the following Playground example:
#objc protocol Foo
{
init(_ someNumber: Int)
}
#objc(MYBar) class Bar : NSObject, Foo
{
required init(_ someNumber: Int)
{
println("Got \(someNumber)")
}
}
var bar = Bar(123)
bar.respondsToSelector("init:") // true
NSClassFromString("MYBar")?.alloc() // `Bar` instance
An initializer with a first parameter with an external parameter name of foo (i.e. init(foo bar:Int)), will by default become initWithFoo: in Objective-C. When you do not explicitly specify an external name for an initializer parameter, the external name will be implicitly made to be the same as the internal name. So when you write init(str: String), it is implicitly init(str str: String), you have to use it like MyClass(str: something), and the Objective-C selector is initWithStr:.
You can always explicitly suppress the external name by using _ where you would specify the external name. This is what they do with the first parameter of the initializer in JSONJoy (init(_ decoder: JSONDecoder)). You can do the same thing:
init(_ str: String)
and then you would use it like MyClass(something), and the Objective-C selector would be init:

Resources