Access variable in different class - Swift - class

i got two swift files :
main.swift and view.swift
In main.swift i have a variable (Int) initially set to 0.
With an IBACtion I set that variable to be 10, and everything is ok.
However, if I try access that variable from view.swift, with a simple call like main().getValue(), i get always 0 and not 10 even if the variable has changed it's value in main.swift.
The method getValue() in main.swift looks like this:
func getValue() -> Int {
return variable
}
EDIT
Here is the code (Translated from Italian :D )
import Cocoa
class Main: NSObject {
var variable: Int = 0
func getValue() -> Int {
return variable
}
#IBAction func updateVar(sender: AnyObject!) {
variable = 10
}
}
class View: NSView {
override func drawRect(dirtyRect: NSRect) {
println(Main().getValue()) //Returns always 0
}
}
Thanks in advance
Alberto

I have solved this by creating a generic main class which is accessible to all views. Create an empty swift file, name it 'global.swift' and include it in your project:
global.swift:
class Main {
var name:String
init(name:String) {
self.name = name
}
}
var mainInstance = Main(name:"My Global Class")
You can now access this mainInstance from all your view controllers and the name will always be "My Global Class". Example from a viewController:
viewController:
override func viewDidLoad() {
super.viewDidLoad()
println("global class is " + mainInstance.name)
}

There is an important distinction to be made between "files" in Swift and "classes". Files do not have anything to do with classes. You can define 1000 classes in one file or 1 class in 1000 files (using extensions). Data is held in instances of classes, not in files themselves.
So now to the problem. By calling Main() you are creating a completely new instance of the Main class that has nothing to do with the instance that you have hooked up to your Xib file. That is why the value comes out as the default.
What you need to do, is find a way to get a reference to the same instance as the one in your Xib. Without knowing more of the architecture of your app, it is hard for me to make a suggestion as to do that.
One thought, is that you can add a reference to your Main instance in your Xib using an IBOutlet in your View. Then you can simply do self.main.getValue() and it will be called on the correct instance.

Related

ViewController passed as parameter is not being deinitialized (Swift)

Setup:
I have a ViewController ProblemView and class A. I pass ProblemView to class A, so I can work on it. It looks like this (simplified):
class ProblemView: UIViewController{
var instanceOfA = A()
instanceOfA.passView(passedVC: self)
}
class A{
var workOn = ProblemView()
func passView(passedVC: ProblemView){
workOn = passedVC
// I noticed, if I declare a varible locally like var workOn2 = passedVC, my problem is solved -
// but I need the variable globally, because I don't want to pass it around within this class
}
func doSth(){
// here I interact with variables of the passed ViewController
}
}
Problem: Whenever I restart this process within the app the memory increases every single time until I get memory error.
What I tried: I added deinit to both classes. class A is always deinitialized but class ProblemView is not (this might be the problem?).
I also found out, that when I don't declare workOn globally but within the passView function, then it works just fine. But I need have the variable globally, because I use it within many different functions of A. What could be a solution or workaround to this problem?
Strong references to each other.
Try to change class A:
weak var workOn: ProblemView?
func passView(passedVC: ProblemView){
workOn = passedVC
// I noticed, if I declare a varible locally like var workOn2 = passedVC, my problem is solved -
// but I need the variable globally, because I don't want to pass it around within this class
}
func doSth(){
// here I interact with variables of the passed ViewController
}

What is a good example to differentiate between fileprivate and private in Swift3

This article has been helpful in understanding the new access specifiers in Swift 3. It also gives some examples of different usages of fileprivate and private.
My question is - isn't using fileprivate on a function that is going to be used only in this file the same as using private?
fileprivate is now what private used to be in earlier
Swift releases: accessible from
the same source file. A declaration marked as private can now only be accessed within the lexical scope it is declared in.
So private is more restrictive than fileprivate.
As of Swift 4, private declarations inside a type are accessible to extensions of the same type if the extension is defined in the same source file.
Example (all in one source file):
class A {
private func foo() {}
fileprivate func bar() {}
func baz() {
foo()
bar()
}
}
extension A {
func test() {
foo() // Swift 3: error: use of unresolved identifier 'foo'
// Swift 4: no error because extension is in same source file
bar()
}
}
let a = A()
a.foo() // error: 'foo' is inaccessible due to 'private' protection level
a.bar()
The private foo method is accessible only within the scope of
the class A { ... } definition. It is not even accessible from
an extension to the type (in Swift 3, see the second note below for
changes in Swift 4).
The file-private bar method is accessible from the same source file.
Notes:
The proposal SE-0159 – Fix Private Access Levels suggested to revert to the Swift 2 semantics in Swift 4. After a lengthy and controversial discussion on the swift-evolution mailing list, the proposal was rejected.
The proposal SE-0169 – Improve Interaction Between private Declarations and Extensions suggests to make private
declarations inside a type accessible to extensions of the same type
if the extension is defined in the same source file.
This proposal was accepted and implemented in Swift 4.
I just draw a diagram about private, fileprivate, open and public
Hope it can quickly help you , for text description please refer to Martin R 's answer
[ Update Swift 4, 5 ]
Updated for Swift 5
Private vs FilePrivate
For better clarity paste the code snippet in Playground
class Sum1 {
let a: Int!
let b: Int!
private var result: Int?
fileprivate var resultt: Int?
init(a : Int, b: Int) {
self.a = a
self.b = b
}
func sum(){
result = a + b
print(result as! Int)
}
}
let aObj = Sum1.init(a: 10, b: 20)
aObj.sum()
aObj.resultt //File Private Accessible as inside same swift file
aObj.result //Private varaible will not be accessible outside its definition except extensions
extension Sum1{
func testing() {
// Both private and fileprivate accessible in extensions
print(result)
print(resultt)
}
}
//If SUM2 class is created in same file as Sum1 ---
class Sum2{
func test(){
let aSum1 = Sum1.init(a: 2, b: 2)
// Only file private accessible
aSum1.resultt
}
}
Note: Outside of Swift file both private and fileprivate are not accessible.
A practical rule of thumb is that you use private for variables, constants, inner structs and classes that are used only inside the declaration of your class / struct. You use fileprivate for things that are used inside of your extensions within the same file as your class/struct but outside of their defining curly braces (ie. their lexical scope).
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
//This is not used outside of class Viewcontroller
private var titleText = "Demo"
//This gets used in the extension
fileprivate var list = [String]()
override func viewDidLoad() {
navigationItem.title = titleText
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return list.count
}
}
In Swift 4.0, Private is now accessible in extension but within same file. If you declare/define extension in other file, then your private variable will not be accessible to your extension**
File Private
File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
Syntax: fileprivate <var type> <variable name>
Example: fileprivate class SomeFilePrivateClass {}
Private
Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
Syntax: private <var type> <variable name>
Example: private class SomePrivateClass {}
Here is more detail about all access levels: Swift - Access Levels
Look at this images:
File: ViewController.swift
Here extension and view controller both are in same file, hence private variable testPrivateAccessLevel is accessible in extension
File: TestFile.swift
Here extension and view controller both are in different files, hence private variable testPrivateAccessLevel is not accessible in extension.
Here class ViewController2 is a subclass of ViewController and both are in same file. Here private variable testPrivateAccessLevel is not accessible in Subclass but fileprivate is accessible in subclass.
Although #MartinR's and #StephenChen's answer are perfect, Swift 4 changes things a little bit.
Private is now considered as private to a class in which it is declared and also to its extensions.
FilePrivate is considered to be private in that file be it a class in which the variable is defined, it's extension, or any other classes defined in that same file.
filePrivate - access controll level is within the file.
case 1: If we create extension with in same class file and try to access fileprivate function or fileprivate property in its extension - access allowed
case 2: If we create a extension of class in new file - And now try to access fileprivate function or fileprivate property - access not allowed
private - access control level is with in lexical scope
case 1: If property or function is declared as private in class - then scope is by default the class.
case 2: if private instance is declared with in function body - then scope of instance is limited to function body.
This is the explanation for swift 4. For swift 3, the difference is the private. swift 3 private cannot be accessed by its extension, only Class A itself can access.
After swift 4, fileprivate becomes a bit redundant, because person normally will not define the subclass in the same file. Private should be enough for most cases.
In the following example, language constructs modified by private and fileprivate seem to behave identically:
fileprivate func fact(_ n: Int) -> Int {
if (n == 0) {
return 1
} else {
return n * fact(n - 1)
}
}
private func gauss(_ n: Int) -> Int {
if (n == 0) {
return 0
} else {
return n + gauss(n - 1)
}
}
print(fact(0))
print(fact(5))
print(fact(3))
print(gauss(10))
print(gauss(9))
This is according to intuition, I guess. But, is there any exception?
Kindest regards.
class Privacy {
fileprivate(set) var pu:Int {
get {
return self.pr
}
set {
self.pr = newValue
}
}
private var pr:Int = 0
fileprivate var fp:Int = 0
func ex() {
print("\(self.pu) == \(self.pr) and not \(self.fp)")
}
}
extension Privacy {
func ex2() {
self.pu = 5
self.ex()
}
}
I like this because it is super simple for ivars.
Try changing fileprivate to private (and vice versa) and see what happens on compile...

Protecting the model in document architecture (Cocoa/Swift)

I am writing an OS X document-based application using cocoa/swift. So far I have a model, which is managed by the NSDocument subclass. Custom views are managed by custom view controllers, which update the views to keep them synchronised with the model.
The model->controller->view flow of information is strightforward. I have the view controllers observing the document and, when the document changes, the view controllers do their job with the views. The issue is that, in this process, the model's objects are obviously exposed to the view controllers, and therefore the view controllers could also modify the model if I wish (or if I make a mistake).
I'd like the document to be the only one who has "permission" to modify the model objects. The view controllers should have read-only access to them. Is there a way to do this in Swift?
Thanks in advance.
In Swift, the private variable modifier does not apply to classes which are defined in the same file, so if you put your Model class definition in the same file as your NSDocument subclass, then the NSDocument subclass can change the private Model variables as if they were public, but an NSViewController subclass defined in another file will not have access to the private Model variables.
Then you can make private variables partially private by writing:
private(set) var name: String
...which will allow the NSController subclass to read them, but not set them. Swift synthesizes setters and getters for all your variables (not just computed properties), and that tells Swift to make the setter private.
I tested private(set) with some observer code, and the above scenario will allow the NSDocument subclass to change the Model, but if the NSViewController subclass tries to change the Model, Xcode immediately flags the assignment with the error:
Cannot assign to the result of this expression
MyDocument.swift:
import Cocoa
class Employee: NSObject {
private(set) var name: String
init(name: String) {
self.name = name
super.init()
}
}
class MyDocument: NSDocument {
dynamic var worker = Employee(name: "Joe")
//...The rest of the NSDocument junk here
}
MyViewController.swift:
import Cocoa
class MyViewController: NSObject {
var document: MyDocument
var IdentifierForThisClass: Int = 0
init(document: MyDocument) {
self.document = document
super.init()
self.document.addObserver(self,
forKeyPath: "worker",
options: .Old | .New,
context: &IdentifierForThisClass
)
}
override func observeValueForKeyPath(
keyPath: String,
ofObject object: AnyObject,
change: [NSObject : AnyObject],
context: UnsafeMutablePointer<Void>) {
println("Observer:")
if context != &IdentifierForThisClass {
println("This Observer message was meant for a parent class!")
super.observeValueForKeyPath(keyPath,
ofObject: object,
change: change,
context: context
)
return
}
var newValue = change[NSKeyValueChangeNewKey] as! Employee
println("\tThe worker has been changed to: \(newValue.name)")
}
func doStuff() {
println("Inside doStuff():")
println("\tThe worker's name is \(document.worker.name)")
//document.worker.name = "Jenny"
}
}
Some code to exercise the classes:
let myDoc = MyDocument()
let viewController = MyViewController(
document: myDoc
)
myDoc.worker = Employee(name: "Jenny")
viewController.doStuff()
--output:--
Observer:
The worker has been changed to: Jenny
Inside doStuff():
The worker's name is Jenny
Then if I uncomment the line:
doStuff() {
...
//document.worker.name = "Jenny"
...
}
Xcode immediately flags that as an error.

Private var is accessible from outside the class

This was done in Playground, just to simplify.
class MyPrivateVar
{
private var priv: String?
}
var myInstance = MyPrivateVar()
myInstance.priv = "Something"
No compiler warning. In fact auto-complete is showing priv without a problem.
My understanding is that outside the boundaries of {} of the class, I'm not supposed to be able to see a private anything, func nor var.
Am I missing something?
Access modifiers in Swift are implemented differently than other languages. There are three levels:
private: accessible only within that particular file
internal: accessible only within the module (project)
public: accessible from anywhere
Unless marked otherwise, everything you write is internal by default.
The Swift blog had a post about access control when the features were introduced in beta 4, and Apple's documentation has a chapter as well.
Note: this answer is for Swift 2
The Swift Programming Language states:
Swift provides three different access levels for entities within your
code. These access levels are relative to the source file in which an
entity is defined, and also relative to the module that source file
belongs to.
If you wan't to test private access level with Swift, the following step by step may help you.
1/ Create a new Xcode project.
2/ Create a file, MyPrivateVar.swift, and add the following code in it:
class MyPrivateVar {
private var priv: String? = nil
}
3/ Create a second file, MySecondClass.swift, and add the following code in it:
class MySecondClass {
init() {
var myPrivateVar = MyPrivateVar()
myPrivateVar.priv = "some string"
}
}
Xcode will immediatly give you a Swift compiler error message:
'MyPrivateVar' does not have a member named 'priv'
4/ Now, remove the two previous files from your project and create a single file TwoClassesInAFile.swift with the following code in it:
class MyPrivateVar {
private var priv : String? = nil
}
class MySecondClass {
init() {
var myPrivateVar = MyPrivateVar()
myPrivateVar.priv = "some string"
}
}
This time, you will get no Swift compiler error message and you will be able to access MyPrivateVar's priv private property from MySecondClass because priv and MySecondClass are in the same file (your TwoClassesInAFile.swift file).
Furthermore, access levels also work for global variables. For example, Xcode won't give any compiler error if the following code is part of the same ViewController.swift file:
import UIKit
private var globalPrivate : String? = nil
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
globalPrivate = "some string"
println(globalPrivate)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
However, if you define globalPrivate outside of ViewController.swift, Xcode will generate an error message:
Use of unresolved identifier 'globalPrivate'

How to call instance method inside class using Swift

I am having an issue with calling an instance method within the class itself. If someone can provide me some insight it would be greatly appreciated.
My current code looks like this:
class Rect
{
func printthis() -> String {
return "this is working or what"
}
var toPrint:String = self.printthis()
}
The error I am getting in Xcode is: Use of unresolved identifier 'self'.
What am I missing here?
You can't call an instance method without an instance. The class is merely the template for instances. So i don't what you are trying to do here...
But the answer is no, you cannot call an instance method form the class definition because there is no instance yet.
Perhaps you want to delcare a class method and use that to set an instance variable on creation? If so, you might do that like this:
class Rect {
class func printthis() -> String {
return "this is working or what"
}
var toPrint:String
init() {
toPrint = Rect.printthis()
}
}
var r = Rect()
println(r.toPrint) //-> this is working or what
An instance of a class is not initialized and able to be referenced (even as 'self') until all of its variables have been assigned values.
An option that may work for you is to declare your variable as an implicitly-unwrapped optional, which is assigned nil by default. Then in the class's init method, since all of the variables have been assigned values, you are able to start calling methods on your instance.
class Rect {
var toPrint: String!
init() {
toPrint = printthis()
}
printthis() -> String {
return "this will work"
}
}
the problem is that swift is strict about initing all properties.
you may as a workaround
class Rect
{
func printthis() -> String {
return "this is working or what"
}
var toPrint:String = ""
init() {
toPrint = printthis()
}
}