Trying to call a class method from a protocol (Swift) - swift

I am trying to write a fake for MFMailComposeViewController for testing so I made a protocol called MailComposeViewController and a class FakeComposeViewController that is conformed to this protocol. I also made MFMailComposeViewController conform to this protocol as well.
So in my code when I call it I am saying of type MailComposeViewController I want to have a replacement for MFMailComposeViewController.canSendMail(). If I try to replace with my protocol using: MailComposeViewController.canSendMail() I get the error Static member 'canSendMail' cannot be used on protocol metatype 'MailComposeViewController.Protocol' which makes sense. but how would I go about writing around this so I could decide to pass in a FakeComposeViewController.canSendMail() true response when testing and the real MFMailComposeViewController.canSendMail() when running?

You can work with Type of MailComposeViewController itself
func check(for viewControllerType: MailComposeViewController.Type) {
if viewControllerType.canSendMail() { ... }
}

Similar to what Robert suggested you could also get the type of the passed controller via:
func test(controller: MailComposeViewController) {
if type(of: controller).canSendMail() {
// Do stuff
} else {
// Do something different
}
}

Related

How can I make my generic parameter conform to OR in Swift and how can I found the conformation of parameter?

I want build a function which accept to kind of protocols, the function should work if one of protocols passed, I have 2 issue with this function, first I do not know how I can apply OR to protocols, and second i do not know how can I find out my incoming value inside the function conform to which protocol, then I could run right code!
func printFunction<T: CustomStringConvertible OR CustomDebugStringConvertible>(value: T) { // 1: issue with OR!
if value.description { // 2: issue with finding out which protocol conformation is!
print(value.description)
}
else if value.debugDescription { // 3: issue with finding out which protocol conformation is!
print(value.debugDescription)
}
else {
print("not printable!")
}
}
There is no OR operator for protocol conformance. Protocol is like a contract you can't conform to one or another. You need to conform to both. What you need is to implement two methods. One for each. Btw no need to explicitly type description when using CustomStringConvertible:
func printFunction<T: CustomStringConvertible>(value: T) { print(value) }
func printFunction<T: CustomDebugStringConvertible>(value: T) { print(value.debugDescription) }

How do I check the class of an instance that conforms to a protocol in swift?

I'm trying to check the class of an instance which conforms to a protocol.
I have a protocol.
protocol ToolbarProtocol {
func show()
func hide()
}
I have a class which conforms to that protocol.
class GameToolbar: ToolbarProtocol {
...
}
I have a manager class I createed to manage my toolbars.
class ToolbarManager {
var existingToolbars: [Game.rotation: Array<ToolbarProtocol>]
}
In this manager, I have a function that wants to find the first instance of a specific type of toolbar.
func getDebugToolbar() -> ToolbarProtocol? {
return existingToolbars[.east]?.first(where: { (toolbar: ToolbarProtocol) -> Bool in
toolbar.isKind(of: GameToolbar.self) //This line causes an error because .isKind is not a member of ToolbarProtocol
})
}
I can't call isKind(of) on toolbar, which previously worked when my toolbars were a different kind of class provided by an external library (which I'm trying to remove from my codebase because I want different functionality).
I tried making my protocol extend AnyObject, but I think that's implicit anyway, and it had no effect.
How can I check an array of instances which conform to a given protocl, to check for specific class types?
I think you would need to attempt to cast it, like
if let vc = toolbar as? GameToolbar {}
In your case, you might need something like this:
func getDebugToolbar() -> ToolbarProtocol? {
return existingToolbars[.east]?.first(where: { (toolbar: ToolbarProtocol) -> Bool in
let _ = toolbar as? GameToolbar
})
}

Swift protocol to only implemented by specific classes

I want to create a protocol which is only adopted by a specific class and its subClassses in swift.
I know i can use protocol extensions like this
protocol PeopleProtocol: class {
}
extension PeopleProtocol where Self: People {
}
But the method that will go in my protocol will be an init method which will be implemented by a class or its subClasess and will return only some specific type of objects.
some thing like this.
protocol PeopleProtocol: class {
init() -> People
}
or i can do some thing like this
extension PeopleProtocol where Self : People {
init()
}
But there are two problems,
In the first approach if i put an init method in the protocol it don't allow me to put a return statement there like -> People in the first approach.
In the second approach i have to provide a function body in the protocol extensions, so this thing will be out of question, as i don't know what specific type to return for this general implementation.
So any suggestions how i can call an init method and do either:
Let the protocol (not protocol extension) to be implemented by only specific classe and its subClasses.
Or return an instance of a certain from protocol extension method without giving its body.
You could add a required method that you only extend for the appropriate classes.
for example:
protocol PeopleProtocol
{
var conformsToPeopleProtocol:Bool { get }
}
extension PeopleProtocol where Self:People
{
var conformsToPeopleProtocol:Bool {return true}
}
class People
{}
class Neighbours:People
{}
extension Neighbours:PeopleProtocol // this works
{}
class Doctors:People,PeopleProtocol // this also works
{}
class Dogs:PeopleProtocol // this will not compile
{}
This could easily be circumvented by a programmer who would want to, but at least it will let the compiler warn you if you try to apply the protocol to other classes.

Default Implementation of Objective-C Optional Protocol Methods

How can I provide default implementations for Objective-C optional protocol methods?
Ex.
extension AVSpeechSynthesizerDelegate {
func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
print(">>> did finish")
}
}
Expectation: Whatever class that conforms to AVSpeechSynthesizerDelegate should run the above function whenever a speech utterance finishes.
You do it just exactly as you've implemented it. The difference ends up being in how the method is actually called.
Let's take this very simplified example:
#objc protocol FooProtocol {
optional func bar() -> Int
}
class Omitted: NSObject, FooProtocol {}
class Implemented: NSObject, FooProtocol {
func bar() -> Int {
print("did custom bar")
return 1
}
}
By adding no other code, I'd expect to have to use this code as such:
let o: FooProtocol = Omitted()
let oN = o.bar?()
let i: FooProtocol = Implemented()
let iN = i.bar?()
Where oN and iN both end up having type Int?, oN is nil, iN is 1 and we see the text "did custom bar" print.
Importantly, not the optionally chained method call: bar?(), that question mark between the method name in the parenthesis. This is how we must call optional protocol methods from Swift.
Now let's add an extension for our protocol:
extension FooProtocol {
func bar() -> Int {
print("did bar")
return 0
}
}
If we stick to our original code, where we optionally chain the method calls, there is no change in behavior:
However, with the protocol extension, we no longer have to optionally unwrap. We can take the optional unwrapping out, and the extension is called:
The unfortunate problem here is that this isn't necessarily particularly useful, is it? Now we're just calling the method implemented in the extension every time.
So there's one slightly better option if you're in control of the class making use of the protocol and calling the methods. You can check whether or not the class responds to the selector:
let i: FooProtocol = Implemented()
if i.respondsToSelector("bar") {
i.bar?()
}
else {
i.bar()
}
This also means you have to modify your protocol declaration:
#objc protocol FooProtocol: NSObjectProtocol
Adding NSObjectProtocol allows us to call respondsToSelector, and doesn't really change our protocol at all. We'd already have to be inheriting from NSObject in order to implement a protocol marked as #objc.
Of course, with all this said, any Objective-C code isn't going to be able to perform this logic on your Swift types and presumably won't be able to actually call methods implemented in these protocol extensions it seems. So if you're trying to get something out of Apple's frameworks to call the extension method, it seems you're out of luck. It also seems that even if you're trying to call one or the other in Swift, if it's a protocol method mark as optional, there's not a very great solution.

Object wrapper (surrogate) forwarding methods in Swift

CBPeripheral is a painful object to test as it can not be instanced by itself. Therefore, I'm using a wrapper (HBRPeripheralWrapper) around it (also for other purposes).
I would like to forward most of call on the wrapper
(HBRPeripheralWrapper) to the actual wrapped object CBPeripheral.
It technically works using forwardInvocation but how can I adopt a similar pattern in Swift?
PS: NSInvocation is not available in Swift
class HBRPeripheralWrapper {
let peripheral:CBPeripheral
// I would like to avoid this "manual" forwarding
var identifier: NSUUID {
get {
return peripheral.identifier
}
}
init(peripheral:CBPeripheral) {
self.peripheral = peripheral
}
// target forwarding is great, but how can I make the compiler happy?
override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? {
if(self.peripheral.respondsToSelector(aSelector)) {
return self.peripheral
}
return super.forwardingTargetForSelector(aSelector)
}
}
Instead of making HBRPeripheralWrapper, consider extending CBPeripheral
extension CBPeripheral {
// add whatever else you need here.
}
You don't say exactly what you do in HBRPeripheralWrapper other than forwarding, so I can't provide more help.