Is use of generics valid in XCTestCase subclasses? - ios

I have a XCTestCase subclass that looks something like this. I have removed setup() and tearDown methods for brevity:
class ViewControllerTests <T : UIViewController>: XCTestCase {
var viewController : T!
final func loadControllerWithNibName(string:String) {
viewController = T(nibName: string, bundle: NSBundle(forClass: ViewControllerTests.self))
if #available(iOS 9.0, *) {
viewController.loadViewIfNeeded()
} else {
viewController.view.alpha = 1
}
}
}
And its subclass that looks something like this :
class WelcomeViewControllerTests : ViewControllerTests<WelcomeViewController> {
override func setUp() {
super.setUp()
self.loadControllerWithNibName("welcomeViewController")
// Put setup code here. This method is called before the invocation of each test method in the class.
}
func testName() {
let value = self.viewController.firstNameTextField.text
if value == "" {
XCTFail()
}
}
}
In theory, this should work as expected -- the compiler doesn't complain about anything. But it's just that when I run the test cases, the setup() method doesn't even get called. But, it says the tests have passed when clearly testName() method should fail.
Is the use of generics a problem? I can easily think of many non-generic approaches, but I would very much want to write my test cases like this. Is the XCTest's interoperability between Objective-C and Swift the issue here?

XCTestCase uses the Objective-C runtime to load test classes and find test methods, etc.
Swift generic classes are not compatible with Objective-C. See https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID53:
When you create a Swift class that descends from an Objective-C class, the class and its members—properties, methods, subscripts, and initializers—that are compatible with Objective-C are automatically available from Objective-C. This excludes Swift-only features, such as those listed here:
Generics
...
Ergo your generic XCTestCase subclass can not be used by XCTest.

Well, actually its totally doable. You just have to create a class that will be sort of test runner. For example:
class TestRunner: XCTestCase {
override class func initialize() {
super.initialize()
let case = XCTestSuite(forTestCaseClass: ViewControllerTests<WelcomeViewController>.self)
case.runTest()
}
}
This way you can run all your generic tests.

I got it working in a kind of hacky way with protocols & associated types:
import XCTest
// If your xCode doesn't compile on this line, download the lastest toolchain as of 30 november 2018
// or change to where Self: XCTestCase, and do it also in the protocol extension.
protocol MyGenericTestProtocol: XCTestCase {
associatedtype SomeType
func testCallRunAllTests()
}
extension MyGenericTestProtocol {
// You are free to use the associated type here in any way you want.
// You can constraint the associated type to be of a specific kind
// and than you can run your generic tests in this protocol extension!
func startAllTests() {
for _ in 0...100 {
testPrintType()
}
}
func testPrintType() {
print(SomeType.self)
}
}
class SomeGenericTestInt: XCTestCase, MyGenericTestProtocol {
typealias SomeType = Int
func testCallRunAllTests() {
startAllTests()
}
}
class SomeGenericTestString: XCTestCase, MyGenericTestProtocol {
typealias SomeType = String
func testCallRunAllTests() {
startAllTests()
}
}

This approach (https://stackoverflow.com/a/39101121/311889) no longer works. The way I did it in Swift 5.2 is:
class MyFileTestCase: XCTestCase {
override func run() {
let suite = XCTestSuite(forTestCaseClass: FileTestCase<MyFile>.self)
suite.run()
super.run()
}
// At least one func is needed for `run` to be called
func testDummy() { }
}

Related

How to call a protocol's default function declared in a protocol extension?

I'm trying to call a protocol's default function that was declared in an extension:
protocol Tester {
func printTest()
}
extension Tester {
func printTest() {
print("XXXXTestXXXX")
}
}
class TestController: UIViewController, Tester {
let testing = Tester()// error here
override func viewDidLoad() {
super.viewDidLoad()
testing.printTest()
}
}
The error ''Tester' cannot be constructed because it has no accessible initializers' keeps appearing when I try to create an instance of the protocol. Whats the best way to use default functions in protocols?
You have to call the implementer, in your case it's TestController so :
self.printTest() will work
protocol Proto {
// func testPrint() <- comment this out or remove it
}
extension Proto {
func testPrint() {
print("This extension called")
}
}
struct Bar: Proto {
func testPrint() {
print("Call from Structure")
(self as Foo).testPrint()
}
}
Bar().testPrint()
// Output: 'Call from Structure',
// 'This extension call'
Protocols cannot be instantiated - ever - lets think of them Contracts
Classes CAN supposed to confrom to 1..n protocols.
The fact that your protocol provides a default implementation, just enables a class that conforms to it to inherit this default functionality if it doesnt need to provide a custom implementation.
a very cheap way in your case would be:
let testing = self
since your TestController class already conforms to Tester.
(Note I create a retain cycle)
===
another way would be to not use testing but self.printTest()
(This might be what you want)
Id reread the apple swift book and check out protocols (what they are and how they work)

Declarations from extensions cannot be overridden yet in Swift 4

I have recently migrated my code to Swift 4. There is an issue that I am facing with extensions, i.e.:
Declarations from extensions cannot be overridden yet
I have already read multiple posts regrading this issue. But none of them entertains the scenario described below:
class BaseCell: UITableViewCell
{
//Some code here...
}
extension BaseCell
{
func isValid() -> String?
{
//Some code here...
}
}
class SampleCell: BaseCell
{
//Some code here...
override func isValid() -> String? //ERROR..!!!
{
//Some code here...
}
}
According to Apple,
Extensions can add new functionality to a type, but they cannot override existing functionality.
But in the above scenario, I am not overriding the method isValid() in extension. It is overridden in the SampleCell class definition itself. Still, it is giving the error.
But in the above scenario, I am not overriding the method isValid() in an extension.
isValid gets declared in an extension.
The error pretty much says that if a function is declared this way, it cannot be overridden.
The statement is valid for both from an extension and in an extension.
You can override declarations from extensions as long as you #objc the protocol. In Swift 4.2:
class BaseClass {}
class SubclassOfBaseClass: BaseClass {}
#objc protocol IsValidable {
func isValid() -> Bool
}
extension BaseClass: IsValidable {
func isValid() -> Bool { return false }
}
extension SubclassOfBaseClass {
override func isValid() -> Bool { return !super.isValid() }
}
BaseClass().isValid() // -> false
SubclassOfBaseClass().isValid() // -> true
In Swift 3, you were able to override the function of extension if extension was of a class that is getting derived from Objective-C (http://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/), but I guess its not possible now in Swift 4. You can ofcourse do something like this:
protocol Validity {
func isValid() -> String?
}
class BaseCell: UITableViewCell, Validity {
}
extension Validity
{
func isValid() -> String? {
return "false"
}
}
class SampleCell: BaseCell {
func isValid() -> String? {
return "true"
}
}
let base = BaseCell()
base.isValid() // prints false
let sample = SampleCell()
sample.isValid() // prints true
I think this is self-explanatory.
declarations FROM extensions cannot be overridden yet
You are trying to override the function func isValid() -> String? which was declared within an extension of BaseCell, not the BaseCell class itself.
It is clearly saying that you can't override something that was declared inside an extension.
Hope it is helpful.
I too had a huge legacy of Swift 3 code that used this old trick to achieve what I wanted, so when I moved to Swift 4 and started getting these errors, I was somewhat distressed. Fear not, there is a solution.
This error has to do with the way Swift 4 compiles classes and the new way it treats Objective-C classes and functions. Under Swift 3, if a class is derived from NSObject, then all the variables and functions in that class would use Objective-C's dynamic naming and lookup conventions. This approach inhibited Swift's ability to optimise the code and improve code performance and size.
To overcome these penalties, in Swift 4, only variables and functions explicitly tagged with #objc get the Objective-C treatment, everything else uses standard Swift conventions: hence the error.
Armed with this knowledge, the solution to your problem is to tag the functions in the extension you wish to be overridden as #objc, then in the child classes, override the function, but remember to include the #objc tag so your code will get called at runtime.
WARNING The is a little gotcha here: if you forget to include the #objc in the override, the compiler will not complain, but your code lacks the dynamic lookup, so never gets called at runtime.
So your code should look a bit like this:
class BaseCell: UITableViewCell {
//Some code here...
}
extension BaseCell {
#objc func isValid() -> String? {
//Some code here...
}
}
class SampleCell: BaseCell {
//Some code here...
#objc override func isValid() -> String? {
//Some code here...
}
}
It is invalid in Swift, however not in Objective-C. So, if your method signature allows it (no objc forbidden constructs), you can declare it #objc func myMethod() and override it freely in Swift.

Swift 3.1 deprecates initialize(). How can I achieve the same thing?

Objective-C declares a class function, initialize(), that is run once for each class, before it is used. It is often used as an entry point for exchanging method implementations (swizzling), among other things.
Swift 3.1 deprecates this function with a warning:
Method 'initialize()' defines Objective-C class method 'initialize',
which is not guaranteed to be invoked by Swift and will be disallowed
in future versions
How can this be resolved, while still maintaining the same behaviour and features that I currently implement using the initialize() entry point?
Easy/Simple Solution
A common app entry point is an application delegate's applicationDidFinishLaunching. We could simply add a static function to each class that we want to notify on initialization, and call it from here.
This first solution is simple and easy to understand. For most cases, this is what I'd recommend. Although the next solution provides results that are more similar to the original initialize() function, it also results in slightly longer app start up times. I no longer think
it is worth the effort, performance degradation, or code complexity in most cases. Simple code is good code.
Read on for another option. You may have reason to need it (or perhaps parts of it).
Not So Simple Solution
The first solution doesn't necessarily scale so well. And what if you are building a framework, where you'd like your code to run without anyone needing to call it from the application delegate?
Step One
Define the following Swift code. The purpose is to provide a simple entry point for any class that you would like to imbue with behavior akin to initialize() - this can now be done simply by conforming to SelfAware. It also provides a single function to run this behavior for every conforming class.
protocol SelfAware: class {
static func awake()
}
class NothingToSeeHere {
static func harmlessFunction() {
let typeCount = Int(objc_getClassList(nil, 0))
let types = UnsafeMutablePointer<AnyClass>.allocate(capacity: typeCount)
let autoreleasingTypes = AutoreleasingUnsafeMutablePointer<AnyClass>(types)
objc_getClassList(autoreleasingTypes, Int32(typeCount))
for index in 0 ..< typeCount { (types[index] as? SelfAware.Type)?.awake() }
types.deallocate(capacity: typeCount)
}
}
Step Two
That's all good and well, but we still need a way to actually run the function we defined, i.e. NothingToSeeHere.harmlessFunction(), on application startup. Previously, this answer suggested using the Objective-C code to do this. However, it seems that we can do what we need using only Swift. For macOS or other platforms where UIApplication is not available, a variation of the following will be needed.
extension UIApplication {
private static let runOnce: Void = {
NothingToSeeHere.harmlessFunction()
}()
override open var next: UIResponder? {
// Called before applicationDidFinishLaunching
UIApplication.runOnce
return super.next
}
}
Step Three
We now have an entry point at application startup, and a way to hook into this from classes of your choice. All that is left to do: instead of implementing initialize(), conform to SelfAware and implement the defined method, awake().
My approach is essentially the same as adib's. Here's an example from a desktop application that uses Core Data; the goal here is to register our custom transformer before any code mentions it:
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
override init() {
super.init()
AppDelegate.doInitialize
}
static let doInitialize : Void = {
// set up transformer
ValueTransformer.setValueTransformer(DateToDayOfWeekTransformer(), forName: .DateToDayOfWeekTransformer)
}()
// ...
}
The nice thing is that this works for any class, just as initialize did, provided you cover all your bases — that is, you must implement every initializer. Here's an example of a text view that configures its own appearance proxy once before any instances have a chance to appear onscreen; the example is artificial but the encapsulation is extremely nice:
class CustomTextView : UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame:frame, textContainer: textContainer)
CustomTextView.doInitialize
}
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
CustomTextView.doInitialize
}
static let doInitialize : Void = {
CustomTextView.appearance().backgroundColor = .green
}()
}
That demonstrates the advantage of this approach much better than the app delegate does. There is only one app delegate instance, so the problem isn't very interesting; but there can be many CustomTextView instances. Nevertheless, the line CustomTextView.appearance().backgroundColor = .green will be executed only once, as the first instance is created, because it is part of the initializer for a static property. That is very similar to the behavior of the class method initialize.
If you want to fix your Method Swizzling in Pure Swift way:
public protocol SwizzlingInjection: class {
static func inject()
}
class SwizzlingHelper {
private static let doOnce: Any? = {
UILabel.inject()
return nil
}()
static func enableInjection() {
_ = SwizzlingHelper.doOnce
}
}
extension UIApplication {
override open var next: UIResponder? {
// Called before applicationDidFinishLaunching
SwizzlingHelper.enableInjection()
return super.next
}
}
extension UILabel: SwizzlingInjection
{
public static func inject() {
// make sure this isn't a subclass
guard self === UILabel.self else { return }
// Do your own method_exchangeImplementations(originalMethod, swizzledMethod) here
}
}
Since the objc_getClassList is Objective-C and it cannot get the superclass (e.g. UILabel) but all the subclasses only, but for UIKit related swizzling we just want to run it once on the superclass. Just run inject() on each target class instead of for-looping your whole project classes.
A slight addition to #JordanSmith's excellent class which ensures that each awake() is only called once:
protocol SelfAware: class {
static func awake()
}
#objc class NothingToSeeHere: NSObject {
private static let doOnce: Any? = {
_harmlessFunction()
}()
static func harmlessFunction() {
_ = NothingToSeeHere.doOnce
}
private static func _harmlessFunction() {
let typeCount = Int(objc_getClassList(nil, 0))
let types = UnsafeMutablePointer<AnyClass>.allocate(capacity: typeCount)
let autoreleasingTypes = AutoreleasingUnsafeMutablePointer<AnyClass>(types)
objc_getClassList(autoreleasingTypes, Int32(typeCount))
for index in 0 ..< typeCount { (types[index] as? SelfAware.Type)?.awake() }
types.deallocate(capacity: typeCount)
}
}
You can also use static variables since those are already lazy and refer them in your top-level objects' initializers. This would be useful for app extensions and the like which doesn't have an application delegate.
class Foo {
static let classInit : () = {
// do your global initialization here
}()
init() {
// just reference it so that the variable is initialized
Foo.classInit
}
}
If you prefer Pure Swift™! then my solution to this kind of thing is running at _UIApplicationMainPreparations time to kick things off:
#UIApplicationMain
private final class OurAppDelegate: FunctionalApplicationDelegate {
// OurAppDelegate() constructs these at _UIApplicationMainPreparations time
private let allHandlers: [ApplicationDelegateHandler] = [
WindowHandler(),
FeedbackHandler(),
...
Pattern here is I'm avoiding the Massive Application Delegate problem by decomposing UIApplicationDelegate into various protocols that individual handlers can adopt, in case you're wondering. But the important point is that a pure-Swift way to get to work as early as possible is dispatch your +initialize type tasks in the initialization of your #UIApplicationMain class, like the construction of allHandlers here. _UIApplicationMainPreparations time ought to be early enough for pretty much anybody!
Mark your class as #objc
Inherit it from NSObject
Add ObjC category to your class
Implement initialize in category
Example
Swift files:
//MyClass.swift
#objc class MyClass : NSObject
{
}
Objc files:
//MyClass+ObjC.h
#import "MyClass-Swift.h"
#interface MyClass (ObjC)
#end
//MyClass+ObjC.m
#import "MyClass+ObjC.h"
#implement MyClass (ObjC)
+ (void)initialize {
[super initialize];
}
#end
Here is a solution that does work on swift 3.1+
#objc func newViewWillAppear(_ animated: Bool) {
self.newViewWillAppear(animated) //Incase we need to override this method
let viewControllerName = String(describing: type(of: self)).replacingOccurrences(of: "ViewController", with: "", options: .literal, range: nil)
print("VIEW APPEAR", viewControllerName)
}
static func swizzleViewWillAppear() {
//Make sure This isn't a subclass of UIViewController, So that It applies to all UIViewController childs
if self != UIViewController.self {
return
}
let _: () = {
let originalSelector = #selector(UIViewController.viewWillAppear(_:))
let swizzledSelector = #selector(UIViewController.newViewWillAppear(_:))
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
method_exchangeImplementations(originalMethod!, swizzledMethod!);
}()
}
Then on AppDelegate:
UIViewController.swizzleViewWillAppear()
Taking from the following post
Init static stored property with closure
[static stored property with closure]
One more example to execute something once using
extension MyClass {
static let shared: MyClass = {
//create an instance and setup it
let myClass = MyClass(parameter: "parameter")
myClass.initialize()//setup
return myClass
}()
//() to execute the closure.
func initialize() {
//is called once
}
}
//using
let myClass = MyClass.shared
I think that is a workaround way.
Also we can write initialize() function in objective-c code, then use it by bridge reference
Hope the best way.....

Override function error in swift

I got a struct :
struct ErrorResultType: ErrorType {
var description: String
var code: Int
}
and a protocol:
protocol XProtocol {
func dealError(error: ErrorResultType)
}
Now I want to make an extention of UIViewController:
extension UIViewController: XProtocol {
func dealError(error: ErrorResultType) {
// do something
}
}
So I can subclass from this and override the function like:
class ABCViewController: UIViewController {
--->override func dealError(error: ErrorResultType) {
super.dealError(error)
// do something custom
}
}
But it goes wrong with: Declarations from extensions cannot be overridden yet
It doesn't make any sense to me. When I replace all ErrorResultType with AnyObject, the error won't appear any more.
Anything I missed?
For now the method in the extension must be marked with #objc to allow overriding it in subclasses.
extension UIViewController: XProtocol {
#objc
func dealError(error: ErrorResultType) {
// do something
}
}
But that requires all types in the method signature to be Objective-C compatible which your ErrorResultType is not.
Making your ErrorResultType a class instead of a struct should work though.
If i am not making mistake this is connected with Swift official extension mechanism for adding methods to classes.
Conclusion :
At the moment, it's not possible to override entities declared in
extension by subclassing, like so:
class Base { }
extension Base {
var foo: String { return "foo" }
}
class Sub: Base {
override var foo: String { return "FOO" } // This is an error
}
Please check this resource for more information : https://github.com/ksm/SwiftInFlux/blob/master/README.md#overriding-declarations-from-extensions

How to use dynamic types conforming to protocols

I'm just trying to get a grasp as to why this isn't possible. Why can I not use a dynamic type with this setup. (I put this in playgrounds):
protocol SomeProtocol {
init()
}
class SomeClass : SomeProtocol {
required init() { }
}
let x: SomeProtocol.Type = SomeClass.self
x()
When this runs, playgrounds will crash, and if you try to put code like this in Xcode it will throw a
Command failed due to signal: Segmentation fault: 11
If I, however, never call x(), I can print out x to be of the correct SomeClass.Type.
I realize this is a weird setup, so I understand the confusion of "why would you ever want to do that?". But that aside; is it possible? not possible? Is it a bug? Am I not understanding how protocols really work?
It works fine. I tried your code in Xcode 7 and it compiles and runs. Of course, in real code you can't say x() at top level. And in Swift 2 you have to say x.init(), not x(). But when you do, it's fine.
protocol SomeProtocol {
init()
}
class SomeClass : SomeProtocol {
required init() { }
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let x: SomeProtocol.Type = SomeClass.self
x.init()
}
}

Resources