Checking for optional protocol methods in Swift gives error? - swift

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.

Related

Can a protocol define subscript(keyPath:) without an explicit implementation in the adopting object?

Since Swift 4, objects have gained subscript(keyPath:) which can be used to retrieve values using AnyKeyPath and its subclasses. According to the Swift book, the subscript is available on all types. For example, an instance of a class TestClass may be subscripted with an AnyKeyPath like so:
class TestClass {
let property = true
}
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = TestClass()[keyPath: anyKeyPath]
This compiles correctly as expected. Use of any other valid subclass would also compile including PartialKeyPath<TestClass>, KeyPath<TestClass, Bool>, etc. This functionality is unavailable in a protocol extension. For example, the following is invalid:
class TestClass {
let property = true
}
protocol KeyPathSubscriptable {
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = self[keyPath: anyKeyPath] // Value of type 'Self' has no subscripts
}
}
If we want to use that keyPath subscript in the protocol, we can include it in the protocol definition. However, the compiler will not resolve it automatically:
protocol KeyPathSubscriptable {
subscript(keyPath: AnyKeyPath) -> Any? { get }
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath // This can be any valid KeyPath
_ = self[keyPath: anyKeyPath]
}
}
class TestClass: KeyPathSubscriptable { // Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
let property = true
}
With this, we get a compile error: Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'. In order to resolve this, we must include a redundant implementation of that subscript in TestClass:
class TestClass: KeyPathSubscriptable {
let property = true
subscript(keyPath: AnyKeyPath) -> Any? {
fatalError() // This is never executed
}
}
This resolves the conformance issue and produces the goal result although it is seemingly unnecessary and illogical. I'm not sure how, but the subscript implementation is never even used. It's finding the expected implementation of subscript(keyPath:) and using that instead, but how? Where is that and is there any way to use it in a protocol? Why is this required by the compiler even though it's never used?
The context of this use case is in a logging module. The goal is that an object should be able to adopt a particular protocol which, with no additional setup on the object, would provide a human readable description of the object, instead of the default for many objects which is a memory address. The protocol would use Mirror to fetch KeyPaths of an object, read the values, and print them to the console. It is intended for debugging purposes and would not run in any production environment.
Please let me know if I can make any clarifications. I may post this to the Swift team if others think that this could potentially be a bug of sorts. All help is appreciated. Thanks in advance.
Full gist located here.

Confused by Ambiguous Reference Member Reference error in Swift

Given this workspace example:
import Foundation
import CoreBluetooth
class Something: NSObject, CBPeripheralDelegate {
var peripheral:CBPeripheral!
func peripheral(peripheral: CBPeripheral, didUpdateValueForDescriptor descriptor: CBDescriptor, error: NSError?) {
}
func foobar() {
self.peripheral.writeValue([], forDescriptor: 0) // I use a real value instead of 0 in real code
}
}
I get errors that look like:
Playground execution failed: Playground2.playground:6:3: error: ambiguous reference to member 'peripheral'
self.peripheral.writeValue([], forDescriptor: 0)
^~~~
Playground2.playground:5:6: note: found this candidate
var peripheral:CBPeripheral!
^
Playground2.playground:1:7: note: found this candidate
func peripheral(peripheral: CBPeripheral, didUpdateValueForDescriptor descriptor: CBDescriptor, error: NSError?) {
So, it can't seem to decide if my self.peripheral code is a reference to my variable named peripheral or one of the delegate functions I've chosen to implement? I guess I could rename my peripheral variable to be something else...
But what surprises me is that if I construct what seems like a similar example, it has no issues disambiguating:
import Foundation
extension Int {
func frobnicate() { }
func barf() { }
}
class YakAttack: NSObject {
var something:Int!
func something(something:Int, else:Int) {
}
func foobar() {
self.something.frobnicate()
}
}
The foobar() function should have the same issue with the self.something reference, but it has no such problem. What's the difference?
(I am using XCode 7.3 Beta 5, which has the latest Swift version in it)
My best guess is it's a message passing confusion due to dealing with Objective-C protocols/datatypes. I'm not really qualified to analyze the specific, but thus far I've learned there are still some weird bugs like this that come up when dealing with both languges.
I'm guessing this will be fixed down the road, but for now you'll probably just have the change the variable name.

Want to avoid copy-pasting method names for a Swift 2 delegate dispatcher

I’m trying to implement a dispatcher to notify multiple targets for a delegate pattern protocol.
What’s a better way of doing the following pattern, without copy-pasting for every method name? Reflection, aspect-oriented programming, meta-programming all come to mind:
public class AirBatteryClientDelegateDispatcher: AirBatteryClientDelegate {
private var targets = [AirBatteryClientDelegate]()
public func clientDidStartScan(client: AirBatteryClient) {
for target in targets {
target.clientDidStartScan?(client)
}
}
. . .
}
For reference, I’m using the following protocol with a dozen more similar methods:
#objc public protocol AirBatteryClientDelegate: class {
optional func clientDidStartScan(client: AirBatteryClient)
optional func clientDidStopScan(client: AirBatteryClient)
optional func clientDidUpdateState(client: AirBatteryClient)
. . .
}
You will have to implement all the delegate methods explicitly, but you can at least reduce the amount of repetition by implementing them all the same way. Effectively, since you're using an Objective-C feature (optional protocol methods), you are writing this code in Objective-C:
#objc protocol AirBatteryClientDelegate: NSObjectProtocol { // *
optional func clientDidStartScan(client: AirBatteryClient)
optional func clientDidStopScan(client: AirBatteryClient)
optional func clientDidUpdateState(client: AirBatteryClient)
}
class AirBatteryClientDelegateDispatcher {
private var targets = [AirBatteryClientDelegate]()
private func call(f:String, client:AnyObject) {
for target in targets {
let f = f + ":" // work around Swift bug
if target.respondsToSelector(Selector(f)) {
target.performSelector(Selector(f), withObject:client)
}
}
}
func clientDidStartScan(client: AirBatteryClient) {
call(#function, client:client) // or __FUNCTION__ before Swift 2.2
}
// ... and the others are identical
}
However, it would be even better if you really were writing this code in Objective-C, which is made for this kind of dynamic forwarding. You'd be able to be even more elegant. But I won't describe how you'd do that, as it would take us too far afield from what you asked.

EXC_BAD_ACCESS using protocol composition

I want to configure an object with multiple presentables; I've created protocols for them; so that I can combine them into a concrete presentable.
protocol Presentable {
}
protocol TextPresentable: Presentable {
var text:String { get }
}
protocol ImagePresentable: Presentable {
var image:String { get }
var images:[String] { get }
}
The concrete struct:
struct ConcretePresentable: TextPresentable, ImagePresentable {
var text:String { return "Text" }
var image:String { return "Image" }
var images:[String] { return ["A", "B"] }
}
The next thing would be to have a presenter, and I would check if the passed in presenter is actually valid:
typealias TextAndImagePresentable = protocol<TextPresentable, ImagePresentable>
struct ConcretePresenter {
func configureWithPresentable(presentable: Presentable) {
guard let textAndImagePresentable = presentable as? TextAndImagePresentable else {
return
}
print(textAndImagePresentable.text)
print(textAndImagePresentable.image)
print(textAndImagePresentable.images)
}
}
To configure:
let concretePresentable = ConcretePresentable()
let concretePresenter = ConcretePresenter()
concretePresenter.configureWithPresentable(concretePresentable)
It goes fine if I run this in a playground, with all of the code in the same place. However, once I put this in a project and split it up into multiple files (ConcretePresenter.swift, ConcretePresentable.swift holding the concrete structs and Presentable.swift which holds the protocols), I get a EXC_BAD_ACCESS.
Why does that happen?
As a disclaimer, I don't necessarily find this answer very satisfying, but it does work.
So, once I noticed that the text & image properties were being returned in each others places (the value for text is being returned by the image property and vice versa), I figured the problem had something to do with what Swift is doing with managing pointers here.
So, out of curiosity, I wanted to add a truly scalar value to the protocols. I added a value property as an Int to the TextPresentable protocol:
protocol Presentable {}
protocol TextPresentable: Presentable {
var text:String { get }
var value: Int { get }
}
protocol ImagePresentable: Presentable {
var image:String { get }
var images:[String] { get }
}
And then I set up the concrete implementation to return some known value. Here, we're returning 0.
struct ConcretePresentable: TextPresentable, ImagePresentable {
var text:String { return "SomeText" }
var value: Int { return 0 }
var image:String { return "SomeImage" }
var images:[String] { return ["A", "B"] }
}
After running this code, we still get the same crash, but I notice that value, which really shouldn't have a problem printing 0 is instead printing some very large number: 4331676336. This isn't right at all.
I also changed images from an array to a dictionary to see if the error persists--it does. It seems the crash is related to collections and not specific to arrays.
From here, I tried some other things.
I tried making ConcretePresentable a class rather than a struct.
class ConcretePresentable: TextPresentable, ImagePresentable
That resulted in the same behavior.
I tried making ConcretePresentable conform to the typealias rather than the protocols independently:
struct ConcretePresentable: TextAndImagePresentable
That resulted in the same behavior.
I tried doing both of the aforementioned at once:
class ConcretePresentable: TextAndImagePresentable
Yet still the same behavior.
I did come up with one way to make it work though. Make a protocol that conforms to the two protocols in your typealias and make ConcretePresentable conform to that:
protocol TextAndImagePresentable: TextPresentable, ImagePresentable {}
struct ConcretePresentable: TextAndImagePresentable {
// ...
}
The problem here is that if you don't explicitly make ConcretePresentable conform to the protocol, it will fail the guard let even if it does conform to TextPresentable and ImagePresentable.
I asked about this on Swift Users and filled a bug.
Confirmed to be a bug in the compiler:
https://bugs.swift.org/browse/SR-4477
Fixed by Joe Groff now:
Joe Groff added a comment - 2 hours ago
Merged. Should be fixed in future snapshots.
struct uses value semantics and so properties are copied. Swift should have reported this as an error since you are trying to inherit from two protocols which derive from the same base protocol. In classes this will work but in struct it wont because of value semantics for struct. In case you decide to add a variable to Presentable protocol Swift would be confused which ones to bring into the struct. From TextPresentable or ImagePresentable
You should use #protocol Presentable : class and then convert ConcretePresentable to class to fix this.
protocol Presentable : class {
}
class ConcretePresenter {...
The problem is in the cast of the Presentable to TextAndImagePresentable. The guard let succeed, but creates an invalid value (I don't know exactly why).
One way to check it, is look to the console on the execution of the commands:
print(textAndImagePresentable.text)
print(textAndImagePresentable.image)
print(textAndImagePresentable.images)
That will print:
Image
Text
Program ended with exit code: 9
One way to avoid it is to change your method signature to avoid the casting:
func configureWithPresentable(presentable: TextAndImagePresentable) {
print(presentable.text)
print(presentable.image)
print(presentable.images)
}
And, in my opinion, since nothing will happen if the presentable do not conform to both protocols, makes more sense to delimiter it on the method signature.

Replacement of #protocol(<protocol name>) in swift

I am trying to use NSXPCConnection in swift.
So, this line:
_connectionToService = [[NSXPCConnection alloc] initWithServiceName:#"SampleXPC"];
can be replaced by this line:
_connectionToService = NSXPCConnection(serviceName: "SampleXPC")
And, this line:
_connectionToService.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:#protocol(StringModifing)];
can be replaced by this line:
_connectionToService.remoteObjectInterface = NSXPCInterface(protocol: <#Protocol#>)
Now I am confused about using the correct replacement for: <#Protocol#> in swift, in objective c I would have used: #protocol(StringModifing), but in swift I am clueless :(
That's a tricky one.
First of all protocol is a reserved keyword and cannot be used as parameter label. A quick look into Apples official doc helped me here. use `protocol`` instead. That means the parameter name contains single quotes.
"[obj class]" is being replaced by "obj.self" in swift. The same syntax is used for protocols. That means in your case "#protocol(StringModifing)" becomes "StringModifing.self".
Unfortunately this still will not work. The problem now is behind the scenes. The xpc mechanism is some kind of low-level stuff and wants ObjC style protocols. That means that you need the keyword #objc in front of your protocol declaration.
All together the solution is:
#objc protocol StringModifing {
func yourProtocolFunction()
}
#objc protocol StringModifingResponse {
func yourProtocolFunctionWhichIsBeingCalledHere()
}
#objc class YourXPCClass: NSObject, StringModifingResponse, NSXPCListenerDelegate {
var xpcConnection:NSXPCConnection!
private func initXpcComponent() {
// Create a connection to our fetch-service and ask it to download for us.
let fetchServiceConnection = NSXPCConnection(serviceName: "com.company.product.xpcservicename")
// The fetch-service will implement the 'remote' protocol.
fetchServiceConnection.remoteObjectInterface = NSXPCInterface(`protocol`: StringModifing.self)
// This object will implement the 'StringModifingResponse' protocol, so the Fetcher can report progress back and we can display it to the user.
fetchServiceConnection.exportedInterface = NSXPCInterface(`protocol`: StringModifingResponse.self)
fetchServiceConnection.exportedObject = self
self.xpcConnection = fetchServiceConnection
fetchServiceConnection.resume()
// and now start the service by calling the first function
fetchServiceConnection.remoteObjectProxy.yourProtocolFunction()
}
func yourProtocolFunctionWhichIsBeingCalledHere() {
// This function is being called remotely
}
}
Swift 4
_connectionToService.remoteObjectInterface = NSXPCInterface(with: StringModifing.self)