I can't use private property in extension. My extension is in another file.
How can I use private property in extension?
Update
Starting with Swift 4, extensions can access the private properties of a type declared in the same file. See Access Levels.
If a property is declared private, its access is restricted to the enclosing declaration, and to extensions of that declaration that are in the same file.
If the property is declared fileprivate, it can only be used within the file it is declared (and by anything in that file).
If the property is declared internal (the default), it can only be used within the module it is declared (and by anything in that file).
If the property is declared public or open, it can be used by anything within the module as well as outside of the module by files that import the module it is declared in.
While #nhgrif is correct about how painful is that protected is missing in Swift, There is a way around.
Wrap your object with protocol, and declare only about the methods and properties that you wish to expose.
For Example
MyUtiltyClass.swift
protocol IMyUtiltyClass {
func doSomething()
}
class MyUtiltyClass : IMyUtiltyClass {
static let shared : IMyUtiltyClass = MyUtiltyClass()
/*private*/
func doSomethingPrivately() {
}
}
MyUtiltyClass+DoSomething.swift
extension MyUtiltyClass {
func doSomething() {
self.doSomethingPrivately()
print("doing something")
}
}
And then you treat the object as the interface type and not the class/struct type directly.
MyViewController.swift
class MyViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
MyUtiltyClass.shared.doSomething()
/*
//⚠️ compiler error
MyUtiltyClass.shared.doSomethingPrivately()
*/
}
}
Happy Coding 👨💻
It's not a good practice to write a private variable in extensions (even if we can). It will introduce unexpected bugs.
If you want to access the variable, you can use private(set) for that variable to make it read only for outside world.
Ex.
private(set) var myPrivateVariable:Int
Related
Consider code like this:
protocol SomeProtocol {
var something: Bool { get set }
}
class SomeProtocolImplementation: SomeProtocol {
var something: Bool = false {
didSet {
print("something changed!")
}
}
}
protocol MyProtocol {
var myProperty: SomeProtocol { get }
}
class MyClass: MyProtocol {
var myProperty: SomeProtocol = SomeProtocolImplementation() {
didSet {
print("myProperty has changed")
}
}
}
var o: MyProtocol = MyClass()
o.myProperty.something = true
This code doesn't compile with error:
error: cannot assign to property: 'myProperty' is a get-only property
o.myProperty.something = true
~~~~~~~~~~~~ ^
Why? My property is of type of SomeProtocolImplementation, which is class type so it should be possible to modify it's inner property using reference to myProperty.
Going further, after modifying myProperty definition so that it looks like that:
var myProperty: SomeProtocol { get set }
something weird happens. Now the code compile (not a surprise), but the output is:
something changed!
myProperty has changed
So at this point SomeProtocolImplementation starts behaving like a value type - modyifing it's internal state causes that the "didSet" callback for myProperty is triggered. Just as SomeProtocolImplementation would be struct...
I actually find the solution, but I want also understand what's going on. The solution is to modify SomeProtocol definition to:
protocol SomeProtocol: class {
var something: Bool { get set }
}
It works fine, but I'm trying to understand why it behaves like this. Anybody able to explain?
First read what Class Only Protocol is. Concentrate on the note section that says:
Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.
Above quote should get you the idea.
You are trying to get the behavior of reference type for your SomeProtocol's conforming class (i.e. SomeProtocolImplementation). You want to be able to change the value of something in future. So basically you are directing to the above quoted sentence.
If you need more clarification please consider the following more meaningful design where I changed the naming for convenience:
protocol Base: class {
var referenceTypeProperty: Bool { get set }
// By now you are assuming: this property should be modifiable from any reference.
// So, instantly make the protocol `Class-only`
}
class BaseImplementation: Base {
var referenceTypeProperty: Bool = false {
didSet {
print("referenceTypeProperty did set")
}
}
}
protocol Child {
var valueTypeProperty: Base { get }
// This property shouldn't be modifiable from anywhere.
// So, you don't need to declare the protocol as Class-only
}
class ChildImplementation: Child {
var valueTypeProperty: Base = BaseImplementation() {
didSet {
print("valueTypeProperty did set")
}
}
}
let object: Child = ChildImplementation()
object.valueTypeProperty.referenceTypeProperty = true
Any class that can provide behavior useful to other classes may declare a programmatic interface for vending that behavior anonymously. Any other class may choose to adopt the protocol and implement one or more of its methods, thereby making use of the behavior. The class that declares a protocol is expected to call the methods in the protocol if they are implemented by the protocol adopter.
Protocol Apple Documentation
When you try to 'set' value to a variable that is read-only - you are trying to change the protocol's implementation. Conforming classes can only consume information from protocol. In Swift we can write protocol extensions where we can have alternative methods for the protocol.
In short think of computed variables as functions. You are technically trying to change a function in this case.
I actually find the solution, but I want also understand what's going on.
I was just about to tell you to make SomeProtocol a class protocol, but you already figured that out. — So I'm a little confused as to what you don't understand.
You understand about reference types and value types, and you understand about class protocols and nonclass protocols.
Well, as long as SomeProtocol might be adopted by a struct (it's a nonclass protocol), then if you are typing something as a SomeProtocol, it is a value type. The runtime isn't going to switch on reference type behavior just because the adopter turns out to be a class instance; all the decisions must be made at compile time. And at compile time, all the compiler knows is that this thing is a SomeProtocol, whose adopter might be a struct.
I have a problem with Typhoon dependency injection framework.
My viewcontroller MainViewController depends on dataProvider property that I want to declare as AnyObject corresponding to protocol DataProviderProtocol
class MainViewController: UIViewController {
// Compiler error here
var dataProvider : DataProviderProtocol!
// something more
}
protocol DataProviderProtocol {
func fetchAllBanks(closure : ([BankObject]) -> Void)
}
class TestDataProvider: NSObject, CEDataProviderProtocol {
func fetchAllBanks(closure : ([CEBankObject]) -> Void) {
var resultBanks = ///////code that creates test data
closure(resultBanks);
}
I want this dataProvider property to be injected by the Typhoon and initialized to the corresponding instance of class TestDataProvider, that implements this protocol. But I also have RealDataProvider that also corresponds to the DataProviderProtocol and might be used sometimes
But this code crashes with the message
Can't inject property 'dataProvider' for object
''. Setter selector not
found. Make sure that property exists and writable'
I can inject this property without crashes if I use the property class of TestDataProvider, but this disables the ability to inject different DataProviderProtocol implementations.
I understand this this crash happens because DataProviderProtocol property type is not NSObject successor. But I just can't find a way to declare property as NSObject<DataProviderProtocol> in Swift
I would appreciate any help
P.S. My Assembly class
public class CEAppAssembly:TyphoonAssembly {
//uiviewcontrollers' components assembly
var mainComponentsAssembly : CEMainComponentsAssembly!
/**
UI Dependencies
**/
public dynamic func mainViewController() -> AnyObject {
return TyphoonDefinition.withClass(MainViewController.self) {
(definition) in
definition.injectProperty("dataProvider", with: self.mainComponentsAssembly.dataProvider())
}
}
Typhoon uses the Objective-C run-time introspection and dynamic features. Therefore:
Protocols must be marked with the '#objc' directive.
Types incompatible with Objective-C (ie 'pure swift') can't be injected.
There's more information about this in the Quick Start guide. If you're still having trouble after reviewing and making those changes, let us know.
We plan to release a Pure Swift version of Typhoon in the near future.
I have class:
class InformationTableViewController: UITableViewController {
private var cos: Int!
}
And I'm trying to inject property:
public dynamic func informationTableViewController() -> AnyObject {
return TyphoonDefinition.withClass(InformationTableViewController.self) {
(definition) in
definition.injectProperty("cos", with: 3)
}
}
When it's a simple class it works normal. But when I use InformationTableViewController on Storyboard (as some view class) I'm getting error:
'Can't inject property 'cos' for object 'Blah.InformationTableViewController: 0x7fca3300afe0'. Setter selector not found. Make sure that property exists and writable'
What's the problem?
Private access modifier restricts the use of an entity to its own defining source file.
So one problem is that you are trying to set your property from outside of it private scope. Remove private keyword from property declaration.
Another problem here is that you are trying to inject primitive type.
In Obj-C Typhoon has support of injecting primitive types but not in Swift yet.
Every class you want to inject has to be a subclass of NSObject in some way (either by subclassing or adding #objc modifier).
As a workaround you may use NSNumber instead of an Int type for your property.
class InformationTableViewController: UITableViewController {
var cos: NSNumber!
}
Assembly:
public dynamic func informationTableViewController() -> AnyObject {
return TyphoonDefinition.withClass(InformationTableViewController.self) {
(definition) in
definition.injectProperty("cos", with: NSNumber.init(int: 3))
}
}
Why need to set public before override viewDidload in a public access control viewController
public class customViewController: UIViewController {
override public func viewDidLoad() {
super.viewDidLoad()
}
}
if I remove the public, Xcode will give an error warning!
The error message is fairly explicit:
Overriding instance method must be as accessible as the declaration it
overrides.
This means that a method must not have a lower access level than the method it overrides.
For example given this class:
public class Superclass {
internal func doSomething() {
...
}
}
You cannot then override doSomething with a method that is less accessible than interal. e.g.
public class Subclass : Superclass {
// error
private override func doSomething() {
}
}
You can however override a method and make it more accessible:
public class Subclass : Superclass {
public override func doSomething() {
// You can even call the internal method in the superclass
super.doSomething()
}
}
The reference documentation has lots of detail on this, but seems to leave this relationship to implication.
Took from here
Public access enables entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use public access when specifying the public interface to a framework.
Internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.
Private access restricts the use of an entity to the enclosing declaration. Use private access to hide the implementation details of a specific piece of functionality.
File-private access restricts the use of an entity to its own defining source file.
Do you need public modifier? You can write it like this:
class customViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
No warnings
You class declaration is:
public class customViewController: UIViewController
This opens the customViewController up to public access (publicly inherited)
Writing this gives you error:
override func viewDidLoad() {
super.viewDidLoad()
}
What happened here was an unpleasant interaction between the default access level for a method and how inheritance handles access levels.
A subclass must preserve the accessibility of its superclass’s methods
wherever the subclass might be used directly. Otherwise, we would
violate the substitution principle that allows us to treat all kinds
of UIViewController as just another UIViewController.
This is my inheritance structure
Protocols
protocol BaseProtocol {
}
protocol ChildProtocol: BaseProtocol {
}
Classes
class BaseClass: NSObject {
var myVar: BaseProtocol!
}
class ChildClass: BaseClass {
override var myVar: ChildProtocol!
}
I'm receiving a compiler error:
Property 'myVar' with type 'ChildProtocol!' cannot override a property with type 'BaseProtocol!'
What is the best approach to achieve this?
UPDATE
I updated the question trying to implement the solution with generics but it does not work :( This is my code (now the real one, without examples)
Protocols
protocol TPLPileInteractorOutput {
}
protocol TPLAddInteractorOutput: TPLPileInteractorOutput {
func errorReceived(error: String)
}
Classes
class TPLPileInteractor<T: TPLPileInteractorOutput>: NSObject, TPLPileInteractorInput {
var output: T!
}
And my children
class TPLAddInteractor<T: TPLAddInteractorOutput>: TPLPileInteractor<TPLPileInteractorOutput>, TPLAddInteractorInput {
}
Well, inside my TPLAddInteractor I can't access self.output, it throws a compiler error, for example
'TPLPileInteractorOutput' does not have a member named 'errorReceived'
Besides that, when I create the instance of TPLAddInteractor
let addInteractor: TPLAddInteractor<TPLAddInteractorOutput> = TPLAddInteractor()
I receive this other error
Generic parameter 'T' cannot be bound to non-#objc protocol type 'TPLAddInteractorOutput'
Any thoughts?
#tskulbru is correct: it can't be done, and this has nothing to do with your protocols. Consider the example below, which also fails…this time with Cannot override with a stored property 'myVar':
class Foo {
}
class Goo: Foo {
}
class BaseClass: NSObject {
var myVar: Foo!
}
class ChildClass: BaseClass {
override var myVar: Foo!
}
To understand why, let's reexamine the docs:
Overriding Properties
You can override an inherited instance or class property to provide
your own custom getter and setter for that property, or to add
property observers to enable the overriding property to observe when
the underlying property value changes.
The implication is that if you are going to override a property, you must write your own getter/setter, or else you must add property observers. Simply replacing one variable type with another is not allowed.
Now for some rampant speculation: why is this the case? Well, consider on the one hand that Swift is intended to be optimized for speed. Having to do runtime type checks in order to determine whether your var is in fact a Foo or a Bar slows things down. Then consider that the language designers likely have a preference for composition over inheritance. If both of these are true, it's not surprising that you cannot override a property's type.
All that said, if you needed to get an equivalent behavior, #tskulbru's solution looks quite elegant, assuming you can get it to compile. :)
I don't think you can do that with protocols
The way i would solve the problem you are having is with the use of generics. This means that you essentially have the classes like this (Updated to a working example).
Protocols
protocol BaseProtocol {
func didSomething()
}
protocol ChildProtocol: BaseProtocol {
func didSomethingElse()
}
Classes
class BaseClass<T: BaseProtocol> {
var myProtocol: T?
func doCallBack() {
myProtocol?.didSomething()
}
}
class ChildClass<T: ChildProtocol> : BaseClass<T> {
override func doCallBack() {
super.doCallBack()
myProtocol?.didSomethingElse()
}
}
Implementation/Example use
class DoesSomethingClass : ChildProtocol {
func doSomething() {
var s = ChildClass<DoesSomethingClass>()
s.myProtocol = self
s.doCallBack()
}
func didSomething() {
println("doSomething()")
}
func didSomethingElse() {
println("doSomethingElse()")
}
}
let foo = DoesSomethingClass()
foo.doSomething()
Remember, you need a class which actually implements the protocol, and its THAT class you actually define as the generic type to the BaseClass/ChildClass. Since the code expects the type to be a type which conforms to the protocol.
There are two ways you can go with your code, depending what you want to achieve with your code (you didn't tell us).
The simple case: you just want to be able to assign an object that confirms to ChildProtocol to myVar.
Solution: don't override myVar. Just use it in ChildClass. You can do this by design of the language Swift. It is one of the basics of object oriented languages.
Second case: you not only want to enable assigning instances of ChildProtocol, you also want to disable to be able to assign instances of BaseProtocol.
If you want to do this, use the Generics solution, provided here in the answers section.
If you are unsure, the simple case is correct for you.
Gerd