Delegate of a class + conforms to a protocol?

I want to write a "helper" class to handle a MFMailComposeViewController transaction instead of bulking up my main class.


What the helper class needs is a reference to a UIViewController (to present the mail composer view) and a delegate to handle a "I'm done now, you can release me". [I probably could get away with not using the protocol, but I'd prefer to use it).


I have this little protocol:


protocol MailProtocol {
  func mailDone();
}


Try as I might, I cannot seem to craft a delegate that is a UIViewController that conforms to 'MailProtocol':


weak var delegate: UIViewController!, MailProtocol


Is this possible? I guess I can have my class init take two objects (both 'self' from the caller), one which meets the protocol and the other that is a UIViewController, but this sure seems like a silly solution.

I think you would include the "parts" of the UIViewController that you want to use in your protocol definition. For example, if you want your helper to have access to the view property, you would do something like this:

protocol MailProtocol {
    var view: UIView! { get set }
    func mailDone()
}

class MyVC: UIViewController, MailProtocol {
    var mailHelper: MyHelper!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.mailHelper = MyHelper(delegate: self)
    }

    func mailDone() {
        print("done")
    }
}

class MyHelper {
    let delegate: MailProtocol!
    init(delegate: MailProtocol) {
        self.delegate = delegate
       print(delegate.view)    // for testing only
       delegate.mailDone()     // for testing only
    }
}

However, I tried this in Xcode 7 beta 2, and when I type anything the REPL (or something) keeps crashing (i.e. code goes all black for a few seconds, get error message at top of of the source window). Compiling gives this error:

SIL verification failed: return value type does not match return type of function: functionResultType == instResultType
Verifying instruction:
     %7 = apply %6(%0, %1, %5) : $@convention(method) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @guaranteed UIViewController) -> (Builtin.RawPointer, Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout UIViewController, @thick UIViewController.Type) -> ()>) // user: %9
->   return %7 : $(Builtin.RawPointer, Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout UIViewController, @thick UIViewController.Type) -> ()>) // id: %9
In function:
// protocol witness for TryHelper.MailProtocol.view.materializeForSet : Swift.ImplicitlyUnwrappedOptional<ObjectiveC.UIView> in conformance TryHelper.ViewController : TryHelper.MailProtocol in TryHelper
sil [transparent] [thunk] @_TTWC9TryHelper14ViewControllerS_12MailProtocolS_FS1_m4viewGSQCSo6UIView_ : $@convention(witness_method) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @inout ViewController) -> (Builtin.RawPointer, Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout ViewController, @thick ViewController.Type) -> ()>) {
bb0(%0 : $Builtin.RawPointer, %1 : $*Builtin.UnsafeValueBuffer, %2 : $*ViewController):
  %3 = load %2 : $*ViewController                 // users: %4, %5, %8
  strong_retain %3 : $ViewController              // id: %4
  %5 = upcast %3 : $ViewController to $UIViewController // user: %7
  // function_ref ObjectiveC.UIViewController.view.materializeForSet : Swift.ImplicitlyUnwrappedOptional<ObjectiveC.UIView>
  %6 = function_ref @_TFCSo16UIViewControllerm4viewGSQCSo6UIView_ : $@convention(method) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @guaranteed UIViewController) -> (Builtin.RawPointer, Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout UIViewController, @thick UIViewController.Type) -> ()>) // user: %7
  %7 = apply %6(%0, %1, %5) : $@convention(method) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @guaranteed UIViewController) -> (Builtin.RawPointer, Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout UIViewController, @thick UIViewController.Type) -> ()>) // user: %9
  strong_release %3 : $ViewController             // id: %8
  return %7 : $(Builtin.RawPointer, Optional<@convention(thin) (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, inout UIViewController, @thick UIViewController.Type) -> ()>) // id: %9
}


0  swift                    0x000000010e092b9b llvm::sys::PrintStackTrace(__sFILE*) + 43
1  swift                    0x000000010e0932db SignalHandler(int) + 379
2  libsystem_platform.dylib 0x00007fff93b7ff1a _sigtramp + 26
3  swift                    0x000000010e68da26 FirstTarget + 60126
4  swift                    0x000000010e0930d6 abort + 22
5  swift                    0x000000010c259d69 (anonymous namespace)::SILVerifier::_require(bool, llvm::Twine const&, std::__1::function<void ()> const&) + 425
6  swift                    0x000000010c26a122 swift::SILVisitor<(anonymous namespace)::SILVerifier, void>::visit(swift::ValueBase*) + 65810
7  swift                    0x000000010c257c8b swift::SILFunction::verify() const + 6059
8  swift                    0x000000010c2ccabb swift::Lowering::SILGenModule::emitProtocolWitness(swift::ProtocolConformance*, swift::SILLinkage, swift::SILDeclRef, swift::SILDeclRef, swift::Lowering::IsFreeFunctionWitness_t, llvm::ArrayRef<swift::Substitution>) + 4203
9  swift                    0x000000010c2ceb38 (anonymous namespace)::SILGenConformance::addMethod(swift::FuncDecl*, swift::ValueDecl*, llvm::ArrayRef<swift::Substitution>) + 328
10 swift                    0x000000010c2ce10a swift::SILWitnessVisitor<(anonymous namespace)::SILGenConformance>::visitProtocolDecl(swift::ProtocolDecl*) + 538
11 swift                    0x000000010c2cb08d swift::Lowering::SILGenModule::getWitnessTable(swift::ProtocolConformance*) + 301
12 swift                    0x000000010c330b7a (anonymous namespace)::SILGenType::emitType() + 1290
13 swift                    0x000000010c3305ce swift::Lowering::SILGenModule::visitNominalTypeDecl(swift::NominalTypeDecl*) + 30
14 swift                    0x000000010c298ddb swift::Lowering::SILGenModule::emitSourceFile(swift::SourceFile*, unsigned int) + 571
15 swift                    0x000000010c299bbf swift::SILModule::constructSIL(swift::ModuleDecl*, swift::SILOptions&, swift::FileUnit*, llvm::Optional<unsigned int>, bool, bool) + 703
16 swift                    0x000000010c299ddb swift::performSILGeneration(swift::FileUnit&, swift::SILOptions&, llvm::Optional<unsigned int>, bool) + 123
17 swift                    0x000000010c09aef1 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&) + 9153
18 swift                    0x000000010c098913 frontend_main(llvm::ArrayRef<char const*>, char const*, void*) + 2515
19 swift                    0x000000010c094a9f main + 1983
20 libdyld.dylib            0x00007fff9741e5c9 start + 1

Maybe it is a bug?


Edit: changed to indicate I tested in a project instead of just playground.

I'm not sure why you need to use a single variable for both. That is, with the arrangement you want (with no type-based connection between UIViewController and MailProtocol), it's merely accidental that the roles are filled by the same object.


Also, remember that in Swift, you might not need a helper class at all. A class extension (which can be in a different file( might be a better choice.

Can you please file a bug report for this crash and let us know what the bug number is?


Thanks!

I sort of rushed the comment about two variables. My thoughts were that the init method would be:


init(delegate: MailDone, vc: UIViewController)


Then I'd set two weak vars, both which would be the same thing - and I could then message the UIViewController when needed, and the delegate for the special delegate methods.


I personally prefer protocols to extensions, since it makes it quite clear to anyone reading the code what is going on. Extensions can be harder to discover (for me anyway). Call me old school.


Anyway, what I ended up with is this protocol:


@objc protocol MailProtocol {
  var viewController: UIViewController { get }
  func mailDone();
}


I added a var to my view controller to return 'self' for 'viewController'.


If you google around on StackOverflow, its filled with questions just like mine - how to I typealias something that is a class and also conforms to some protocol.

Sure, filed bug # 21568805. The editor crash / compile error does not occur if you remove the set requirement from the protocol:

protocol MailProtocol {
    var view: UIView! { get }
    func mailDone() -> Void
}

I added this info to the bug after it was originally submitted.

Delegate of a class &#43; conforms to a protocol?
 
 
Q