Optional field type doesn't conform protocol in Swift 3
I do not believe there's a simple way to do this, given that we currently cannot talk in terms of generic types without their placeholders – therefore we cannot simply cast to Optional.Type
.
Nor can we cast to Optional<Any>.Type
, because the compiler doesn't provide the same kinds of automatic conversions for metatype values that it provides for instances (e.g An Optional<Int>
is convertible to an Optional<Any>
, but an Optional<Int>.Type
is not convertible to a Optional<Any>.Type
).
However one solution, albeit a somewhat hacky one, would be to define a 'dummy protocol' to represent an 'any Optional
instance', regardless of the Wrapped
type. We can then have this protocol define a wrappedType
requirement in order to get the Wrapped
metatype value for the given Optional
type.
For example:
protocol OptionalProtocol {
// the metatype value for the wrapped type.
static var wrappedType: Any.Type { get }
}
extension Optional : OptionalProtocol {
static var wrappedType: Any.Type { return Wrapped.self }
}
Now if fieldMirror.subjectType
is an Optional<Wrapped>.Type
, we can cast it to OptionalProtocol.Type
, and from there get the wrappedType
metatype value. This then lets us check for CustomProtocol
conformance.
for field in Mirror(reflecting: CustomClass()).children {
let fieldMirror = Mirror(reflecting: field.value)
// if fieldMirror.subjectType returns an optional metatype value
// (i.e an Optional<Wrapped>.Type), we can cast to OptionalProtocol.Type,
// and then get the Wrapped type, otherwise default to fieldMirror.subjectType
let wrappedType = (fieldMirror.subjectType as? OptionalProtocol.Type)?.wrappedType
?? fieldMirror.subjectType
// check for CustomProtocol conformance.
if wrappedType is CustomProtocol.Type {
print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol")
} else {
print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol")
}
}
// nonoptionalField is AnotherClass and conforms CustomProtocol
// optionalField is Optional<AnotherClass> and conforms CustomProtocol
This only deals with a single level of optional nesting, but could easily be adapted to apply to an arbitrary optional nesting level through simply repeatedly attempting to cast the resultant metatype value to OptionalProtocol.Type
and getting the wrappedType
, and then checking for CustomProtocol
conformance.
class CustomClass : CustomProtocol {
var nonoptionalField: AnotherClass = AnotherClass()
var optionalField: AnotherClass??
var str: String = ""
}
/// If `type` is an `Optional<T>` metatype, returns the metatype for `T`
/// (repeating the unwrapping if `T` is an `Optional`), along with the number of
/// times an unwrap was performed. Otherwise just `type` will be returned.
func seeThroughOptionalType(
_ type: Any.Type
) -> (wrappedType: Any.Type, layerCount: Int) {
var type = type
var layerCount = 0
while let optionalType = type as? OptionalProtocol.Type {
type = optionalType.wrappedType
layerCount += 1
}
return (type, layerCount)
}
for field in Mirror(reflecting: CustomClass()).children {
let fieldMirror = Mirror(reflecting: field.value)
let (wrappedType, _) = seeThroughOptionalType(fieldMirror.subjectType)
if wrappedType is CustomProtocol.Type {
print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol")
} else {
print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol")
}
}
// nonoptionalField is AnotherClass and conforms CustomProtocol
// optionalField is Optional<Optional<AnotherClass>> and conforms CustomProtocol
// str is String and DOES NOT conform CustomProtocol
How can you check if a type is Optional in Swift?
This is a hacky but working solution:
func isOptional(_ type: Any.Type) -> Bool {
let typeName = String(describing: type)
return typeName.hasPrefix("Optional<")
}
Test:
let t1 = Int?.self
let t2 = Bool.self
print(isOptional(t1))
// true
print(isOptional(t2))
// false
Optional chaining not working for optional protocol requirements
It turns out that the website version of the iBook content for The Swift Programming Language (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html) shows the ThreeSource declared with @objc. Either doing this or deriving ThreeSource from NSObject
works.
Why does the type not conform to protocol?
UIImageView
does not declare that it conforms to ItemView
. Because swift is a strictly typed language, it doesn't infer conformance to protocols, as some types will only incidentally conform. You could however say something like:
protocol ItemView {
var image: UIImage? { get set }
}
class BaseController<T: UIView where T: ItemView>: UIViewController {
var itemView = T()
}
extension UIImageView: ItemView {}
class ConcreteItemController: BaseController<UIImageView> {
}
Not conforming to a protocol
The protocol definition doesn't match your re-declaration, which is what the error message is trying to say. Just add @escaping
before (String)
in your protocol definition and that should fix the error
How to make protocol conformed by only required classes?
I am not sure if below code is what you need, do let me know if that’s what you were looking for, else I will be happy to remove my answer.
protocol A:BaseViewController {
func execute()
}
protocol B:A {
func confirm()
}
class BaseViewController: UIViewController {
}
class AnotherVC: B {
}
In above code compiler will give error saying-:
'A' requires that 'AnotherVC' inherit from ‘BaseViewController'
Once you inherit AnotherVC
from BaseViewController
, it will give another error saying-:
Type 'AnotherVC' does not conform to protocol ‘A'
Once you confirm the implementations errors will be resolved-:
class AnotherVC:BaseViewController, B {
func confirm() {
}
func execute() {
}
}
How to define optional methods in Swift protocol?
1. Using default implementations (preferred).
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
Advantages
No Objective-C runtime is involved (well, no explicitly at least). This means you can conform structs, enums and non-
NSObject
classes to it. Also, this means you can take advantage of powerful generics system.You can always be sure that all requirements are met when encountering types that conform to such protocol. It's always either concrete implementation or default one. This is how "interfaces" or "contracts" behave in other languages.
Disadvantages
For non-
Void
requirements, you need to have a reasonable default value, which is not always possible. However, when you encounter this problem, it means that either such requirement should really have no default implementation, or that your you made a mistake during API design.You can't distinguish between a default implementation and no implementation at all, at least without addressing that problem with special return values. Consider the following example:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}If you provide a default implementation which just returns
true
— it's fine at the first glance. Now, consider the following pseudo code:final class SomeParser {
func parse(data: Data) -> [Any] {
if /* delegate.validate(value:) is not implemented */ {
/* parse very fast without validating */
} else {
/* parse and validate every value */
}
}
}There's no way to implement such an optimization — you can't know if your delegate implements a method or not.
Although there's a number of different ways to overcome this problem (using optional closures, different delegate objects for different operations to name a few), that example presents the problem clearly.
2. Using @objc optional
.
@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
Advantages
- No default implementation is needed. You just declare an optional method or a variable and you're ready to go.
Disadvantages
It severely limits your protocol's capabilities by requiring all conforming types to be Objective-C compatible. This means, only classes that inherit from
NSObject
can conform to such protocol. No structs, no enums, no associated types.You must always check if an optional method is implemented by either optionally calling or checking if the conforming type implements it. This might introduce a lot of boilerplate if you're calling optional methods often.
Related Topics
Implicitly Import Specific Swift Module
Swift 4.2 Setter Getter, All Paths Through This Function Will Call Itself
Can the Conversion of a String to Data with Utf-8 Encoding Ever Fail
Autolayout Contraints for a View from Xib
Swift Enum Loses Initialized Values When Set as a Property
Implementing a Drag-And-Drop Zone in Swift
Get the Last Character of a String Without Using Array
Write and Read a Plist in Swift with Simple Data
Uicollectionview Compositionallayout Not Calling Uiscrolldelegate
How to Get the Height of a Uilabel in Swift
Ios8 Custom Keyboard - Copy & Paste to Uipasteboard
How to Handle Closure Recursivity
Uicollectionviewlayout Not Working with Uiimage in Swift 5 and Xcode 11
Create Codable Struct with Generic Type