Class with Non-Optional Property Conforming to Protocol with Optional Property

Class With Non-Optional Property Conforming To Protocol With Optional Property

Unfortunately, you can't redeclare the same variable in MyClass with a different type.

What Dennis suggests will work, but if you want to keep your variable in MyClass non-Optional, then you could use a computed property to wrap around your stored variable:

protocol MyProtocol {
var a: String? { get set }
}

class MyClass {
// Must use a different identifier than 'a'
// Otherwise, "Invalid redeclaration of 'a'" error
var b: String
}

extension MyClass: MyProtocol {
var a: String? {
get {
return b
}
set {
if let newValue = newValue {
b = newValue
}
}
}
}

Swift Protocol Optional conformance via Non-Optional

If the protocol provides a default implementation that returns an optional:

protocol SomeProtocol {
var foo: Int? { get }
}

extension SomeProtocol {
var foo: Int? { return nil }
}

protocol-conforming types can then provide an overriding non-optional version of the variable/function:

struct StructB: SomeProtocol {
let foo: Int
}

I found this discussed on the Swift Evolution forum:

At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.

https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448

This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:

Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)

Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns

which is tracked on Stack Overflow here:

Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?

swift protocol conformance when same property name is optional

There is no elegant solution to your problem other than renaming the conflicting property on the conforming type.

Swift doesn't allow 2 properties of the same name to exist on a type even if their types are different. On the other hand, Int? and Int are completely different types, so you cannot have trackNumber: Int fulfil the protocol requirement of trackNumber: Int?.

The only solution (other than changing the type in either the protocol or the struct) is to rename the non-Optional property in SpotifyTrack and make an optional computed property of the same name returning the non-optional one.

protocol Track {
var trackNumber: Int? { get }
}
struct SpotifyTrack {
private let _trackNumber: Int
}
extension SpotifyTrack: Track {
var trackNumber: Int? { _trackNumber }
}

Swift protocol with properties that are not always used

Unlike in Objective-C, you cannot define optional protocol requirements in pure Swift. Types that conform to protocols must adopt all the requirements specified.

One potential way of allowing for optional property requirements is defining them as optionals, with a default implementation of a computed property that just returns nil.

protocol Nameable {
var name : String? { get }
var fullName : String? { get }
var nickname : String? { get }
}

extension Nameable {
var name : String? { return nil }
var fullName : String? { return nil }
var nickname : String? { return nil }
}

struct Person : Nameable {

// Person now has the option not to implement the protocol requirements,
// as they have default implementations that return nil

// What's cool is you can implement the optional typed property requirements with
// non-optional properties – as this doesn't break the contract with the protocol.
var name : String
}

let p = Person(name: "Allan")
print(p.name) // Allan

However the downside to this approach is that you potentially pollute conforming types with properties that they don't implement (fullName & nickName in this case).

Therefore if it makes no logical sense for a type to have these properties (say you wanted to conform City to Nameable – but cities don't (really) have nicknames), you shouldn't be conforming it to Nameable.

A much more flexible solution, as you say, would be to define multiple protocols in order to define these requirements. That way, types can choose exactly what requirements they want to implement.

protocol Nameable {
var name : String { get }
}

protocol FullNameable {
var fullName : String { get }
}

protocol NickNameable {
// Even types that conform to NickNameable may have instances without nicknames.
var nickname : String? { get }
}

// A City only needs a name, not a fullname or nickname
struct City : Nameable {
var name : String
}

let london = City(name: "London")

// Person can have a name, full-name or nickname
struct Person : Nameable, FullNameable, NickNameable {
var name : String
var fullName: String
var nickname: String?
}

let allan = Person(name: "Allan", fullName: "Allan Doe", nickname: "Al")

You could even use protocol composition in order to define a typealias to represent all three of these protocols for convenience, for example:

typealias CombinedNameable = Nameable & FullNameable & NickNameable

struct Person : CombinedNameable {
var name : String
var fullName: String
var nickname: String?
}

Swift: setting an optional property of a protocol

It's impossible in Swift (yet?). Referenced from an ADF thread:

Optional property requirements, and optional method requirements that return a value, will always return an optional value of the appropriate type when they are accessed or called, to reflect the fact that the optional requirement may not have been implemented.

So it's no surprise to get optional values easily. However, setting a property requires implementation to be guaranteed.

Invalid redeclaraciton of property for protocol when base class has optional property of same name

You cannot have several properties with the same name, but different types, so you cannot achieve what you are trying to achieve. Even if you changed the type of name of a completely unrelated type (let's say Int), you'd get the same error, this has nothing to do with one of the two declarations being Optional.

Optional Variables in protocol is possible?

protocol TestProtocol {
var name : String {set get}
var age : Int {set get}
}

Provide a default extension for the protocol. Provide the default implementation for all the variables set and get which u want them to be optional.

In below protocol, name and age are optional.

 extension TestProtocol {

var name: String {
get { return "Any default Name" } set {}
}
var age : Int { get{ return 23 } set{} }
}

Now if I am conforming above protocol to any other class, like

class TestViewController: UIViewController, TestProtocol{
var itemName: String = ""

**I can implement the name only, and my objective is achieved here, that the controller will not give a warning that "TestViewController does not conform to protocol TestProtocol"**

var name: String {
get {
return itemName ?? ""
} set {}
}
}

How to conform custom class with optional properties to hashable protocol

The calculation of the hash value does not have to be based on all properties. In fact, it doesn't need to be based on any. You could simply return a hard coded number though you shouldn't.

Simply return a hash value of one or more of the non-optional properties.

The only rule is that two objects that compare as equal must also return the same hash value. But there is no requirement that two objects with the same hash value much compare as equal.

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 to resolve collision between protocol and class fields types?

Property names and types declared in a protocol must exactly be matched by the conforming classes.

So you cannot resolve the error without changing the property type in either the protocol or the conforming type. You could also rename one of the properties and add the matching property to the conforming type as a new field.

So either do:

protocol Named {
var name: String { get }
}

class Person {
var name: String

init(_ name:String) {
self.name = name
}
}

extension Person: Named {

}

Or

protocol Named {
var name: String { get }
}

class Person {
var _name: String!
}

extension Person: Named {
var name: String {
return _name
}
}

As @user28434 pointed out, there's a(n ugly) workaround. You can create a wrapper protocol that matches the optionality of the Person class, make that protocol inherit from the original protocol, declare the non-optional variable in an extension on the new protocol and make Person conform to the new protocol instead of the original Named.

protocol Named {
var name: String { get }
}

class Person {
var name: String!
}

protocol Namedd: Named {
var name: String! { get }
}

extension Namedd {
var name: String {
return name!
}
}

extension Person: Namedd {

}


Related Topics



Leave a reply



Submit