Override controlAlternatingRowBackgroundColors in Swift - swift

I'm trying to figure out how to override controlAlternatingRowBackgroundColors in Swift but whatever I do I seem to get an error.
extension NSColor {
class var controlAlternatingRowBackgroundColors: [NSColor] {
return [NSColor.red, NSColor.blue]
}
}
This gives me "Getter for 'controlAlteratingRowBackgroundColors' with Objective-C selector 'controlAlteratingRowBackgroundColors' conflicts with previous declaration with the same Objective-C selector.
If I use override then I get an error saying that it doesn't override anything.
If I create a category on NSColor in objective c it all works as expected!

Related

Ambiguous reference when using selectors

Ready to use example to test in Playgrounds:
import Foundation
class Demo {
let selector = #selector(someFunc(_:))
func someFunc(_ word: String) {
// Do something
}
func someFunc(_ number: Int) {
// Do something
}
}
I'm getting an Ambiguous use of 'someFunc' error.
I have tried this answer and getting something like:
let selector = #selector(someFunc(_:) as (String) -> Void)
But still getting the same result.
Any hints on how to solve this?
Short answer: it can't be done.
Long explanation: when you want to use a #selector, the methods need to be exposed to Objective-C, so your code becomes:
#objc func someFunc(_ word: String) {
// Do something
}
#objc func someFunc(_ number: Int) {
// Do something
}
Thanks to the #objc annotation, they are now exposed to Objective-C and an compatible thunk is generated. Objective-C can use it to call the Swift methods.
In Objective-C we don't call a method directly but instead we try to send a message using objc_msgSend: the compiler is not able to understand that those method are different, since the generated signature is the same, so it won't compile. You will face the error:
Method 'someFunc' with Objective-C selector 'someFunc:' conflicts with previous declaration with the same Objective-C selector.
The only way to fix it is to have different signatures, changing one or both of them.
The selector is obviously ambiguous, neither methods have external parameter names. Remove the empty external parameters to solve this:
#objc func someFunc(word: String) {
// Do something
}
#objc func someFunc(number: Int) {
// Do something
}
After that, you should specify a selector with the parameter name:
#selector(someFunc(word:))
or
#selector(someFunc(number:))

Can I override a Swift method that has been overridden by an extension?

I'm trying to create an extension for debugging a class to print some output when you enter certain methods. I want to be able to reuse this code in many different classes, and I want to keep those classes clean from this debugging code while also not repeating code (keeping DRY). That's why I thought of using an extension like this:
class A: ... {
override func myMethod() {
super.myMethod()
print("hello A")
}
}
extension A {
override func myMethod() {
print("hello extension")
}
}
And I would like that when myMethod() is called, to see this
hello extension
hello A
(or the other way around, I don't care)
But the problem is that the compiler is saying "myMethod() has already been overridden here"
Any ideas?
Extensions can add new functionality to a type, but they cannot
override existing functionality.
Answer is here Swift override function in extension

Swift 3 UIActivity Subclass problems with override

I just upgraded from swift 2 to swift 3 and I am running into some problems with overriding a property.
In swift 2 you could set the activity type like this
override func activityType() -> String? {
return "com.a.string"
}
However in swift 3 (in Xcode 8 beta 6) they introduced NS_STRING_ENUM and now we override activityType like this
override var activityType() -> UIActivityType? {
return UIActivityType.customActivityType
}
The problem is that Xcode will complain with this:
Property cannot be an #objc override because its type cannot be represented in Objective-C
One solution i found is to add the annotation #nonobjc to it like so:
#nonobjc override var activityType() -> UIActivityType? {
return UIActivityType.customActivityType
}
While this helps make the error go away, this property never gets called...
This is a problem because when the user completes the activity and completionWithItemsHandler() gets called, the activity type is considered nil.
One work around I found is to use an objective-c extension. It works; it gives me type "horse" like I wanted.
#interface Custom_UIActivity (custom)
- (UIActivityType)activityType;
#end
#implementation Custom_UIActivity (custom)
- (UIActivityType)activityType {
return #"horses";
}
My question is how to do it in a pure swift.
In pure Swift3 I am able to compile it using
override var activityType: UIActivityType {
return UIActivityType(rawValue: self.customActivityType.rawValue)
}

Checking for optional protocol methods in Swift gives error?

After reading the Apple docs on optional protocol requirements it says you can use optional chaining to check for the implementation. I tried this out and I keep getting an error. It seems like this is no longer a valid way of doing this and I am trying to find out if there is a new way to do this now.
Here is a example so you can see the error: http://swiftstub.com/743693493/
Here is my code:
#objc protocol Bearable {
func growl()
optional func cough() -> String //Apparently bears cough when they are scared.
}
#objc class Bear:Bearable {
var name = "Black Bear"
func growl() {
println("Growllll!!!")
}
}
#objc class Forest {
var bear:Bear?
func scareBears() {
if let cough = bear?.cough?() {
println(cough)
} else {
println("bear was scared")
}
}
}
I get the error: error: 'Bear' does not have a member named 'cough'
if let cough = bear?.cough?() {
The error you're getting makes sense because Swift can know at compile time that Bear doesn't implement cough() (whereas Objective-C wouldn't necessarily be able to know that).
To make your code compile, you need to define bear using the Bearable protocol instead of the Bear class.
var bear: Bearable?
Which is probably what you'd want anyway. Otherwise, there's not much point in creating that protocol.

Swift: overriding an initializer that takes an NSInvocation

I'm trying to create a reusable test harness in Swift with the idea that subclasses will extend the test harness to provide the instance under test, and can add their own subclass-specific test methods, something like this:
class FooTestHarness: XCTestCase {
let instance: Foo
init(instance: Foo) {
self.instance = instance
}
func testFooBehavior() {
XCTAssert(instance.doesFoo())
}
}
class FooPrime: Foo {
func doesFooPrime(): Bool { /* ... */ }
}
class FooPrimeTests: XCTestCase {
init() {
super.init(FooPrime())
}
func myInstance(): FooPrime {
return instance as FooPrime
}
func testFooPrimeBehavior() {
XCTAssert(myInstance().doesFooPrime())
}
}
However, when XCode's testrunner tries to run FooPrimeTests, it doesn't call the no-arg init(), it calls init(invocation: NSInvocation!) (and fails because there isn't one). I tried to override this in FooTestHarness:
init(invocation: NSInvocation!, instance: Foo) {
self.instance = instance
super.init(invocation)
}
and in FooPrimeTests:
init(invocation: NSInvocation!) {
super.init(invocation, FooPrime())
}
but this fails with the message 'NSInvocation' is unavailable.
Is there a workaround?
I'm not os sure if I got it right, but checking the code you suggested you should get a compiler Error like:
Which actually I reckon is quite normal since your FooPrimeTests is just subclassing XCTestCase which has got different init like:
init!(invocation: NSInvocation!)
init!(selector: Selector)
init()
Probably when you posted you're question you're running on an older version of Swift, (I'm currently running it on the Xcode Beta 6.2) that's why you can't see the error. But, and I say again if I got your point right, your class FooPrimeTests can't see you custom initializer just because is sublcassing XCTestCase, rather then FooTestHarness. Which is the class where the init(instance: Foo) is defined.
So you might probably want to define FooPrimeTests as subclass of FooTestHarness. That way you should be able to correctly see your initializer. Hope this help.