Conform to Protocol and Keep Property Private

Is there a way to declare protocol property as private?

Conforming to

protocol P {
var value: String { get }

init(value: String)
}

requires a gettable property value with default access. If write access to the
property in the conforming class should be restricted to the class itself
then you can declare it as in Swift readonly external, readwrite internal property:

class C: P {
private(set) var value: String

required init(value: String) {
self.value = value
}
}

let myObject = C(value: "Hello World")
print(myObject.value) // OK
myObject.value = "New Value" // Error: Cannot assign to property: 'value' setter is inaccessible

And if the property should only be set in initializers then make it
a constant:

class C: P {
let value: String

required init(value: String) {
self.value = value
}
}

let myObject = C(value: "Hello World")
print(myObject.value) // OK
myObject.value = "New Value" // Error: Cannot assign to property: 'value' is a 'let' constant

what is the meaning when a property's type is a protocol in swift?

Property1 is anything that conforms to protocol A. So this could either be another class, a struct, etc. Say you have a protocol Vegetable and a class named Market. You'll want to sell multiple types of Vegetable, however, you want to make sure that the vegetables are for sale. You can do this with protocols.

protocol Vegetable {
var isForSale: Bool { get }
}

// Now let's create some vegetables
class Carrot: Vegetable {
let isForSale = true
}

class Spinach: Vegetable {
let isForSale = false
}

// This is our market.
class Market {
let vegetables: [Vegetable] = [Carrot(), Spinach()]

// Now we can check if the vegetables are for sale, because we know for sure that they conform to Vegetable so must implement this variable.
var forSale: [Vegetable] = {
vegetables.filter { $0.isForSale } // This will return [Spinach()]
}
}

Swift Property that conforms to a Protocol and Class

Update for Swift 4

Swift 4 has added support for representing a type as a class that conforms to a protocol. The syntax is Class & Protocol. Here is some example code using this concept from "What's New in Swift" (session 402 from WWDC 2017):

protocol Shakeable {
func shake()
}
extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }

// Example function to generically shake some control elements
func shakeEm(controls: [UIControl & Shakeable]) {
for control in controls where control.isEnabled {
control.shake()
}
}

As of Swift 3, this method causes problems because you can't pass in the correct types. If you try to pass in [UIControl], it doesn't have the shake method. If you try to pass in [UIButton], then the code compiles, but you can't pass in any UISliders. If you pass in [Shakeable], then you can't check control.state, because Shakeable doesn't have that. Swift 4 finally addressed the topic.

Old Answer

I am getting around this problem for the time being with the following code:

// This class is used to replace the UIViewController<UITableViewDelegate> 
// declaration in Objective-C
class ConformingClass: UIViewController, UITableViewDelegate {}

class AClass: UIViewController {
@IBOutlet weak var anotherThing: UILabel!
var thing: ConformingClass!
}

This seems hackish to me. If any of the delegate methods were required, then I would have to implement those methods in ConformingClass (which I do NOT want to do) and override them in a subclass.

I have posted this answer in case anyone else comes across this problem and my solution helps them, but I am not happy with the solution. If anyone posts a better solution, I will accept their answer.

Implement a public method that rely on a private property in Swift using protocols

Following this discussion from Swift forum, there are two ways to solve the issue.

First one is straightforward:

/// The publically visible capabilities.
public protocol SameModifierProtocol {
func modifier(x: Int) -> SameModifierProtocol
}
/// The internal requirements on which the default implementation relies.
internal protocol SynthesizedSameModifierProtocolConformance:
SameModifierProtocol {
var x: Int { get set }
}
/// The default implementation.
extension SynthesizedSameModifierProtocolConformance {
public func modifier(x: Int) -> SameModifierProtocol{
var s = self
s.x = x
return s
}
}

/// Conforms to the public protocol
/// and requests the default implementation from the internal one.
/// Clients can only see the public protocol.
public struct Component: SynthesizedSameModifierProtocolConformance {
internal var x: Int = 0
public init() {}
}

The second involves an unofficial feature @_spi, that allows to make implementation details unexposed in a public protocol:

public protocol SameModifierProtocol {
@_spi(SameModifier) var x: Int { get set }
}

extension SameModifierProtocol {
public func modifier(x: Int) -> Self {
var s = self
s.x = x
return s
}
}

public struct Component: SameModifierProtocol {
@_spi(SameModifier) public var x: Int = 0
public init() {}
}

Swift - Conform third-party type to my own protocol with conflicting requirement

Inspired by Jonas Maier's comment, I found what I believe to be an architecturally adequate solution to this problem. As Jonas said, function overloading exhibits the behavior that I'm looking for. I'm starting to think that maybe protocol requirements should only ever be functions, and not properties. Following this line of thinking, my protocol will now be:

protocol CursorInput {
func getCursorLocation () -> CGPoint
func setCursorLocation (_ newValue: CGPoint)
}

(Note that in this answer I'm making it settable as well, unlike in the original post.)

I can now retroactively conform AATrackpad to this protocol without conflict:

extension AATrackpad: CursorInput {
func getCursorLocation () -> CGPoint {
return CGPoint(x: self.cursorLocation.x, y: self.cursorLocation.y)
}
func setCursorLocation (_ newValue: CGPoint) {
self.cursorLocation = AAPoint(newValue)
}
}

Important - This will still compile even if AATrackpad already has a function func getCursorLocation () -> AAPoint, which has the same name but a different type. This behavior is exactly what I was wanting from my property in the original post. Thus:

The major problem with including a property in a protocol is that it can render certain concrete types literally incapable of conforming to that protocol due to namespace collisions.

After solving this in this way, I have a new problem to solve: there was a reason I wanted cursorLocation to be a property and not a function. I definitely do not want to be forced to use the getPropertyName() syntax all across my app. Thankfully, this can be solved, like this:

extension CursorInput {
var cursorLocation: CGPoint {
get { return self.getCursorLocation() }
set { self.setCursorLocation(newValue) }
}
}

This is what is so cool about protocol extensions. Anything declared in a protocol extension behaves analogously to a default argument for a function - only used if nothing else takes precedence. Because of this different mode of behavior, this property does not cause a conflict when I conform AATrackpad to CursorInput. I can now use the property semantics that I originally wanted and I don't have to worry about namespace conflicts. I'm satisfied.


"Wait a second - now that AATrackpad conforms to CursorInput, doesn't it have two versions of cursorLocation? If I were to use trackpad.cursorLocation, would it be a CGPoint or an AAPoint?

The way this works is this - if within this scope the object is known to be an AATrackpad then Alice's original property is used:

let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint

However, if the type is known only to be a CursorInput then the default property that I defined gets used:

let cursorInput: CursorInput = AATrackpad()
type(of: cursorInput.cursorLocation) // This is CGPoint

This means that if I do happen to know that the type is AATrackpad then I can access either version of the property like this:

let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint
type(of: (trackpad as CursorInput).cursorLocation) // This is CGPoint

and it also means that my use case is exactly solved, because I specifically wanted not to know whether my cursorInput happens to be an AATrackpad or a BBMouse - only that it is some kind of CursorInput. Therefore, wherever I am using my cursorInput: CursorInput?, its properties will be of the types which I defined in the protocol extension, not the original types defined in the class.


There is one possibility that a protocol with only functions as requirements could cause a namespace conflict - Jonas pointed this out in his comment. If one of the protocol requirements is a function with no arguments and the conforming type already has a property with that name then the type will not be able to conform to the protocol. This is why I made sure to name my functions including verbs, not just nouns (func getCursorLocation () -> CGPoint) - if any third-party type is using a verb in a property name then I probably don't want to be using it anyway :)

How to properly set a property in a protocol from a conforming class?

Quite simply - declare the property in your protocol as UITableView!. The ! matters - a lot.

! makes a variable an optional, but an explicitly unwrapped one. This basically means, that the unwrapping is done for you, under the hood. You promise the compiler, that although this variable can contain a nil, you will make sure it will be set with a proper value before you try to access it. In case of IBOutlets this is done for you by the SDK.

How to make a struct conforms to a protocol which has a property conforms to another protocol in swift 4?

You are asking to describe the kind of type that data can hold, rather than the actual type. That means it needs to be an associatedtype:

protocol XFNovelApiResponse: Decodable {
associatedtype DataType: Decodable
var data: DataType {get}
var error: NovelApiError {get}
}

Note that protocols with associated types can generate a lot of complexity, so you should carefully consider if this protocol is really necessary, or if XFNovelApiResponse could, for example, be generic instead. It depends on what other types implement this protocol.

For example, another implementation of a similar set of data structures without protocols would be:

struct XFNovelApiResponse<DataType: Decodable>: Decodable {
var data: DataType
var error: NovelApiError
}

struct NovelsData: Decodable {
}

struct NovelApiError: Decodable {

let msg: String
let errorCode: String
}

let novels = XFNovelApiResponse(data: NovelsData(),
error: NovelApiError(msg: "", errorCode: ""))

Alternately, you can implement this with classes and subclasses, which allow inheritance. Structs do not inherit from protocols, they conform to protocols. If you really mean inheritance, classes are the right tool. (But I expect generics are the better solution here.)

Swift Struct with Lazy, private property conforming to Protocol

Because accessing the lazy data variable mutates AStruct, any access to it must be marked as also mutating the struct. So you need to write:

struct AStruct : Example {
// ...
var var1: String {
// must declare get explicitly, and make it mutating:
mutating get {
// act of looking at data mutates AStruct (by possibly initializing data)
return self.data.var1
}
}

var varArray:[String] {
mutating get {
return self.data.varArray
}
}
}

However, you'll find now that Swift complains you aren't conforming to Example, because its get var1 isn't marked as mutating. So you'd have to change it to match:

protocol Example {
var var1:String { mutating get }
var varArray:[String] { mutating get }
}

How to conform to a protocol's variables' set & get?

From the Swift Reference:

Property Requirements

...

The protocol doesn’t specify whether the property should be a stored property or a computed property—it only specifies the required property name and type.

...

Property requirements are always declared as variable properties, prefixed with the var keyword. Gettable and settable properties are indicated by writing { get set } after their type declaration, and gettable properties are indicated by writing { get }.

In your case

var height: Int  {return 5} // error!

is a computed property which can only be get, it is a
shortcut for

var height: Int {
get {
return 5
}
}

But the Human protocol requires a property which is gettable and settable.
You can either conform with a stored variable property (as you noticed):

struct Boy: Human { 
var height = 5
}

or with a computed property which has both getter and setter:

struct Boy: Human { 
var height: Int {
get {
return 5
}
set(newValue) {
// ... do whatever is appropriate ...
}
}
}


Related Topics



Leave a reply



Submit