When should I use class-only protocol rathar than #objc? - swift

When we want to limit protocol adoption to class types, we can use :class protocol or #objc protocol.
But, I fail to see the advantage of "class-only protocol" over #objc protocol.
The differences I know:
Size
#objc is more space efficient.
#objc protocol ProtocolObjC {}
protocol ProtocolClass: class {}
sizeof(ProtocolObjC) // -> 8
sizeof(ProtocolClass) // -> 16
Checking for Protocol Conformance
Only for #objc protocol
#objc protocol ProtocolObjC {}
protocol ProtocolClass: class {}
let obj:AnyObject = NSObject()
obj is ProtocolObjC // -> false
obj is ProtocolClass // < [!] error: cannot downcast from 'AnyObject' to non-#objc protocol type 'ProtocolClass'
Optional Protocol Requirements
Only for #objc protocol
#objc protocol ProtocolObjC {
optional func foo()
}
protocol ProtocolClass: class {
optional func foo() // < [!] error: 'optional' can only be applied to members of an #objc protocol
}
So, is there any use case where we should use : class protocol? or someone know any disadvantages of #objc?
ADDED: Thanks #Antonio !
enum MyEnum { case A,B,C }
#objc protocol ProtoObjC {
// Generics
typealias FooType
var foo:FooType {get set}
// Tuple
var bar:(Int,Int) {get set}
// Enum
var baz:MyEnum {get set}
}
All of them causes compile error.

I think they are 2 different things not to be compared to each other.
My rule of thumb is that native swift protocols should always be used, unless objc compatibility is needed, or unless a certain feature available through #objc only is needed..
However the advantage of using swift protocols is that all swift related features, non available in objc, can be used, such as:
generics
tuples
swift enums
But regardless of that, I'd still stick with swift protocols for a pure stylistic choice.

Related

Can swift function return different types conforming to same protocol? [duplicate]

I am trying to create a Dictionary (actually a HashSet) keyed on a custom protocol in Swift, but it is giving me the error in the title:
Protocol 'myProtocol' can only be used as a generic constraint because it has Self or associated type requirements
and I can't make heads nor tails of it.
protocol Observing: Hashable { }
var observers = HashSet<Observing>()
Protocol Observing inherits from protocol Hashable, which in turn inherits from protocol Equatable. Protocol Equatable has the following requirement:
func ==(lhs: Self, rhs: Self) -> Bool
And a protocol that contains Self somewhere inside it cannot be used anywhere except in a type constraint.
Here is a similar question.
To solve this you could use generics. Consider this example:
class GenericClass<T: Observing> {
var observers = HashSet<T>()
}

Error "Redundant constraint 'Self' : 'AnyObject'" - where is the `AnyObject`?

I found a strange Swift compiler message while developing (I'm using Swift 4.1):
protocol Foo: class where Self: NSObject { // (1)
// Redundant constraint 'Self' : 'AnyObject'
}
What is happening here?
First, this is not redundant. When I write
protocol Foo: class { } // (2)
I have a protocol that any object could potentially conform to, even objects that don't derive from NSObject. But I can create weak references: weak var f: Foo? is okay.
On the other hand, when I write
protocol Foo where Self: NSObject { } // (3)
I have a protocol where I cannot produce weak references: weak var f: Foo? is a compile time error.
Second, where does the AnyObject come from? I'm asking for NSObject. The NSObject is respected though: I cannot declare a class MyFoo: Foo { } because it rightly complains that it must inherit from NSObject?
Is this a bug in Swift? Or am I missing something? If it is a bug: is it a bug because snippet (3) does not let me take weak references? Or because of the compiler warning? Or both? And if I am missing something: what is it?
It is not possible to constrain a protocol to be subclasses of a specific class in Swift 4.1. You can inherit Foo from NSObjectProtocol, which probably matches your intent.
protocol Foo: NSObjectProtocol {
// ....
}
In Swift 4.2, what you've written is legal Swift and does what you expect.
From the Swift public API:
public typealias AnyObject
/// The protocol to which all class types implicitly conform.
So by declaring your protocol to conform to class it automatically conforms to AnyObject

Class-only protocol as typealias for associatedtype with AnyObject constraints

In Swift 4.0 I could write something like this
protocol ObserversHolder {
///Compiling Error in Swift 4.1
///note: possibly intended match 'StringManager.ObserverValue' (aka 'StringObserver') does not conform to 'AnyObject'
///note: protocol requires nested type 'ObserverValue'; do you want to add it?
associatedtype ObserverValue: AnyObject
var observers: [ObserverValue] {get set}
}
protocol StringObserver: class {
func showString()
}
class StringManager: ObserversHolder {
typealias ObserverValue = StringObserver
var observers = [ObserverValue]()
}
But in Swift 4.1 I receive the error Type 'StringManager' does not conform to protocol 'ObserversHolder'.
Is it possible to resolve this?
Change AnyObject to Any
protocol ObserversHolder {
///Compiling Error in Swift 4.1
///note: possibly intended match 'StringManager.ObserverValue' (aka 'StringObserver') does not conform to 'AnyObject'
///note: protocol requires nested type 'ObserverValue'; do you want to add it?
associatedtype ObserverValue: Any
var observers: [ObserverValue] {get set}
}
protocol StringObserver: class {
func showString()
}
class StringManager: ObserversHolder {
typealias ObserverValue = StringObserver
var observers = [ObserverValue]()
}

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.

Object doesn't conforms to protocol

struct IsProtocol<Pr> {
static func implementedInObject<T>(object: T) -> Bool {
return object is Pr
}
}
protocol A : class {}
class B : A {}
let b = B()
println(IsProtocol<A>.implementedInObject(b))
Returns false. What should i do to it returns true?
Checking conformance to protocols works only for protocols that are Objective-C compatible, i.e. marked with #objc directive. This is a limitation of Swift language and as much as I'd love to see it gone, we have to run with it for now.
Here comes the extract from documentation:
You can check for protocol conformance only if your protocol is marked
with the #objc attribute, as seen for the HasArea protocol above. This
attribute indicates that the protocol should be exposed to Objective-C
code and is described in Using Swift with Cocoa and Objective-C. Even
if you are not interoperating with Objective-C, you need to mark your
protocols with the #objc attribute if you want to be able to check for
protocol conformance.
Note also that #objc protocols can be adopted only by classes, and not
by structures or enumerations. If you mark your protocol as #objc in
order to check for conformance, you will be able to apply that
protocol only to class types.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
After you add #objc, it works properly:
struct IsProtocol<Pr> {
static func implementedInObject<T>(object: T) -> Bool {
return object is Pr
}
}
#objc protocol A {}
class B : A {}
let b = B()
println(IsProtocol<A>.implementedInObject(b))