How to write a test that expects a method call using swift and XCTest?
I could use OCMock, but they don't officially support swift, so not much of an option.
As you said OCMock does not support Swift(nor OCMockito), so for now the only way I see is to create a hand rolled mock. In swift this is a little bit less painful since you can create inner classes within a method, but still is not as handy as a mocking framework.
Here you have an example. The code is self explanatory, the only thing I had to do(see EDIT 1 below) to make it work is to declare the classes and methods I want to use from the test as public(seems that the test classes do not belong to the same application's code module - will try to find a solution to this).
EDIT 1 2016/4/27:
Declaring the classes you want test as public is not necessary anymore since you can use the "#testable import ModuleName" feature.
The test:
import XCTest
import SwiftMockingPoC
class MyClassTests: XCTestCase {
func test__myMethod() {
// prepare
class MyServiceMock : MyService {
var doSomethingWasCalled = false
override func doSomething(){
doSomethingWasCalled = true
}
}
let myServiceMock = MyServiceMock()
let sut = MyClass(myService: myServiceMock)
// test
sut.myMethod()
// verify
XCTAssertTrue(myServiceMock.doSomethingWasCalled)
}
}
MyClass.swift
public class MyClass {
let myService: MyService
public init(myService: MyService) {
self.myService = myService
}
public func myMethod() {
myService.doSomething()
}
}
MyService.swift
public class MyService {
public init() {
}
public func doSomething() {
}
}
Related
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 am using Typhoon for dependencies injection in Swift for iOS.
I have created an assembly to inject dependencies in a class called BaseRequest like this:
public class NetworkAssembly: TyphoonAssembly {
public dynamic func baseRequest() -> AnyObject {
return TyphoonDefinition.withClass(BaseRequest.self){
(definition) in
definition.useInitializer("initWithRetryCount:userUmbrella:networkQueueManager:"){
(initializer) in
initializer.injectParameterWith((TyphoonConfig("network.request.retry.count") as! NSNumber).integerValue)
initializer.injectParameterWith(self.coreComponents.userUmbrella())
initializer.injectParameterWith(self.networkQueueManager())
}
}
}
}
Now, I am trying to create a subclass of BaseRequest with a factory method like this:
class DownloadLibrariesRequest: BaseRequest {
var libraries:Array<String> = []
class func downloadLibraries(libraries:Array<String>)->Void{
let request: DownloadLibrariesRequest = DownloadLibrariesRequest(.....)
request.libraries = libraries
}
}
I need to be able to create an instance of DownloadLibrariesRequest and call the NetworkAssembly for BaseRequest since I need to use another init in the subclasses.
Also, I need to mention that I will have around 50 such subclasses, so creating assemblies for all of them doesn't sound too great at the moment.
To create a definition with shared configuration, in order to avoid repetition, use the parent feature.
When I create two classes like so:
class Example
{
}
class OtherExample
{
init() {
println("created")
}
}
var instance = Example()
var otherInstance = OtherExample()
Both seem to create usable instances, so I'm wondering what the difference is in Swift if you don't provide an init method, and yet you initialise as above?
I did think it probably called the superclass init automatically, however since both of these objects don't inherit from NSObject, they don't have super classes do they?!
Also is there a need to class super.init() in the otherExample?
You only need super.init() if your class inherits from another class.
class Example {
func sayHi() {
print("hi")
}
}
class OtherExample: Example {
override init() {
super.init()
print("created")
}
}
var instance = Example()
instance.sayHi()
// hi
var otherInstance = OtherExample()
otherInstance.sayHi()
// created
// hi
class example{
var example:Int = 0
}
class anotherExample{
init(example:Int){
self.example = example
}
var example:Int
}
example()
anotherExample(0)
You can have not initialized variables in a class with an initializer, if you don’t have any initializer you will need to set a value in the class
Both will be able to change their example value but only anotherExample will have a value that can be set.
In my test playground I was unable to use super.init() in the classes (since they don’t have any superclasses to init to/from)
In Java I can do the following:
interface SomeCallback {
void onDone();
}
then I can create a function like this:
void test(SomeCallback callback) {
...
}
To call this function I do:
test(new SomeCallback() {
#Override
void done() {
...
}
});
I want to do something similar in Swift. I could create a protocol
protocol SomeCallback : class {
func done()
}
and a function like this
func test(callback: SomeCallback) {
...
}
I am still struggling with the call of this function.
Edit: Since I use an external API which requires a delegate I cannot use a Closure.
Is it possible to create some kind of anonymous inner class like I did it in the Java example to call test()?
Update: If you can't use a closure/function, the direct answer is: no, there are no anonymous inner classes. However, as Mike M points out, you'll have to use a class, and this class may be nested/inner to prevent polluting the global namespace. This class may well have a closure for every method it needs to implement and just call through to those closures.
The Swift-y way of doing this as long as you just need one method is to just use a lambda/closure.
For example, see NSComparator, which is typealiased since it is used all over the place and you are meant to recognize it.
In your example, specifying a function type inline will do fine. So for example:
func test(callback: () -> Void) {
...
callback()
}
// called as:
test({ in
...
})
// or even (since it's the last parameter)
test { in
...
}
Just to clarify the syntax because what you wrote is a little confusing.
#objc protocol SomeCallback {
func done() -> Void
}
No need to inherit from class as you wrote. Also, don't forget the #objc even if you do not want to bridge the protocol to that language. It helps with compiler complaints later on (might be a bug at the moment)
You cannot instantiate a protocol in Swift. You can however have an internal class that inherits from NSObject (root object) and implements this.
class External: NSObject {
class Internal : SomeCallback {
func done() {
// does something
}
}
let int = Internal()
func test(callback : SomeCallback) {
// additional work
callback.done()
}
}
I'm looking for behavior similar to Objective-C's +(void)initialize class method, in that the method is called once when the class is initialized, and never again thereafter.
A simple class init () {} in a class closure would be really sleek! And obviously when we get to use "class vars" instead of "static vars in a struct closure", this will all match really well!
If you have an Objective-C class, it's easiest to just override +initialize. However, make sure subclasses of your class also override +initialize or else your class's +initialize may get called more than once! If you want, you can use dispatch_once() (mentioned below) to safeguard against multiple calls.
class MyView : UIView {
override class func initialize () {
// Do stuff
}
}
If you have a Swift class, the best you can get is dispatch_once() inside the init() statement.
private var once = dispatch_once_t()
class MyObject {
init () {
dispatch_once(&once) {
// Do stuff
}
}
}
This solution differs from +initialize (which is called the first time an Objective-C class is messaged) and thus isn't a true answer to the question. But it works good enough, IMO.
There is no type initializer in Swift.
“Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
You could use a type property which default value is a closure. So the code in the closure would be executed when the type property (or class variable) is set.
class FirstClass {
class var someProperty = {
// you can init the class member with anything you like or perform any code
return SomeType
}()
}
But class stored properties not yet supported (tested in Xcode 8).
One answer is to use static, it is the same as class final.
Good link for that is
Setting a Default Property Value with a Closure or Function
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
Code example:
class FirstClass {
static let someProperty = {
() -> [Bool] in
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
print("setting default property value with a closure")
return temporaryBoard
}()
}
print("start")
FirstClass.someProperty
Prints
start
setting default property value with a closure
So it is lazy evaluated.
For #objc classes, class func initialize() definitely works, since +initialize is implemented by the Objective-C runtime. But for "native" Swift classes, you'll have to see the other answers.
You can use stored type properties instead of initialize method.
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
}
But since stored types properties are actually lazily initialized on their first access, you will need refer them somewhere. You can do this with ordinary stored property:
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
private let initializer: Void = SomeClass.initializer
}
#aleclarson nailed it, but as of recent Swift 4 you cannot directly override initialize. You still can achieve it with Objective-C and categories for classes inheriting from NSObject with a class / static swiftyInitialize method, which gets invoked from Objective-C in MyClass.m, which you include in compile sources alongside MyClass.swift:
# MyView.swift
import Foundation
public class MyView: UIView
{
#objc public static func swiftyInitialize() {
Swift.print("Rock 'n' roll!")
}
}
# MyView.m
#import "MyProject-Swift.h"
#implementation MyView (private)
+ (void)initialize { [self swiftyInitialize]; }
#end
If your class cannot inherit from NSObject and using +load instead of +initialize is a suitable fit, you can do something like this:
# MyClass.swift
import Foundation
public class MyClass
{
public static func load() {
Swift.print("Rock 'n' roll!")
}
}
public class MyClassObjC: NSObject
{
#objc public static func swiftyLoad() {
MyClass.load()
}
}
# MyClass.m
#import "MyProject-Swift.h"
#implementation MyClassObjC (private)
+ (void)load { [self swiftyLoad]; }
#end
There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️
I can't find any valid use case to have something like +[initialize] in Swift. Maybe this explains way it does not exist
Why do we need +[initialize] in ObjC?
To initialize some global variable
static NSArray *array;
+ (void)initialize {
array = #[1,2,3];
}
which in Swift
struct Foo {
static let array = [1,2,3]
}
To do some hack
+ (void)initialize {
swizzle_methodImplementation()
}
which is not supported by Swift (I can't figure out how to do it for pure Swift class/struct/enum)