Search code examples
iosobjective-cswiftdependency-injectionnsarray

How to define a Swift protocol with a getter and setter for an Objective-C NSArray


I'm doing this for testing purposes. It helps with dependency injection.

I have an Objective-C library that I've imported into my Swift 3 project. One of the classes in the Objective-C library is defined this way:

@interface Auth : NSObject
@property (strong, readwrite) NSString *clientId;
@property (strong, readwrite) NSArray *scopes;
@end

In my Swift project, I want to define a protocol. The protocol should define 4 methods: 2 setters and 2 getters.

I want the protocol to be defined in a way so that I can reopen the Auth class in Swift with an extension and declare that Auth conforms to my protocol without putting anything in the body of the extension because the Objective-C class already conforms.

Here's my protocol:

protocol AuthProtocol {
    var clientID: String! { get set }
}

Here's the extension:

extension Auth: AuthProtocol {}

This works fine. Now I can treat Auth objects as AuthProtocols and have access to setting and getting their clientID property.

The problem arises when I try to make the protocol define the setter and getter for the scopes array. I believe NSArray is Array<AnyObject> in Swift. Here's what I tried:

protocol AuthProtocol {
    var clientID: String! { get set }
    var scopes: Array<AnyObject> { get set }
}

Now the line which reopens Auth for extension complains that

Type Auth does not conform to protocol AuthProtocol

Xcode suggests a solution when I click on the error, which adds this code to the body of the exention:

extension Auth: AuthProtocol {
    internal var scopes: Array<AnyObject> {
        get {
            <#code#>
        }
        set {
            <#code#>
        }
    }
}

This code now has an error on the internal line:

scopes used within its own type

How do I define AuthProtocol with a getter and setter for the scopes array so that this line:

extension Auth: AuthProtocol {}

Doesn't complain?

All this indicates to me that the problem has to do with the NSArray type.


Solution

  • you can use Xcode to show you exactly what the protocol should be by using the "Generated Interface" command:

    Generated Interface Prompt

    this jumps you to the generated code:

    open class Auth : NSObject {
        open var clientId: String!
        open var scopes: [Any]!
    }
    

    transform this generated class definition into the correct protocol definition:

    protocol AuthProtocol {
        var clientId: String! { get set }
        var scopes: [Any]! { get set }
    }
    
    // doesn't complain anymore
    extension Auth: AuthProtocol {}