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
How to Prevent Multiple Instances of the Same Window from Opening in MACos
Any or a Trouble with Sequence of Functions
How to Set iOS 13 Glyphs Programmatically
Uibutton Font Size Isn't Changing
Xcode Generated Coredata Files Cannot Be Processed by Copy Bundle Resources Build Phase
Custom Uitabbar Unselected Item's Color
How to Obtain a Swift Type from a String
Swift Any Difference Between Closures and First-Class Functions
Menu Items Disabled in MACos Menubar App
Calling Stop() on Avaudioplayernode After Finished Playing Causes Crash
How to Make a Custom Geometryreader in Swiftui
How to Get Frame Data in Apprtc iOS App for Video Modifications
How to Call a Selector-Based Timer Method on a Swift Struct
How to Make Deinit Take Effect in Swift
Iwatch: Wkinterfacelabel How to Stop Text from Being Cut Off with "..." at The End of a Label
Supportedinterfaceorientationsforwindow in Swift 2.0
Use of Undeclared Type Autoreleasingunsafepointer Xcode 6 Beta 6