I keep all of my users data in a User object and access it when I need it, as demonstrated in the example below on line 1. The inconvenience I encounter when using this method of data storing is when I need to edit these objects. Below, I have a Time object which belongs to the current user. Ideally, I would set the current user's times to a variable to make it more manageable, and then edit the times using that variable. The problem is, the time variable doesn't refer to the User.current().times[0] variable. Is there a way of using a variable essentially as a shortcut?
let time = User.current().times[0]
time.name = titleLabel.text
time.start = start
time.end = end
time.save()
User.current().times is of type [Time] which is a subclass of NSObject.
The declaration of the User class as requested with the current() function.
var this_user: User?
class User: NSObject {
var times = [Time]()
class func current() -> User {
if this_user == nil {
this_user = User()
}
return this_user!
}
}
Classes Are Reference Types
Unlike value types, reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used instead.
Just check User().current().times[0]. Is it a class or only a value? If you make an user object and holds that reference then you can get those.
For more Info see this.
Your code does work. The fault is in your testing procedure.
To prove this, I set up mock versions of your classes at the top level of a file, like this:
var this_user : User?
class Time : NSObject {
var start : String!
}
class User: NSObject {
var times = [Time]()
class func current() -> User {
if this_user == nil {
this_user = User()
}
return this_user!
}
}
Then, elsewhere, I ran this code:
let cur = User.current()
cur.times.append(Time()) // to give us a first Time in times
let time = User.current().times[0]
time.start = "Testing" // so let's see if we can write into it!
println(User.current().times[0].start) // Testing
This proves that when I set time.start it does reflect back into the Time that is in the times array of the current User. Therefore, I conclude that there is something wrong with the way you are testing your code. (Either that, or your description of your objects is inaccurate.)
Related
I have this class in LRVDetails.swift
class LRVDetails {
var heading = CurrentValueSubject<String, Never>(K.LRV.Register)
// other publishers
}
I have this assign in LRVViewController.swift
private var details = LRVDetails()
private var subs = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
details
.heading
.assign(to: \.text!, on: heading)
.store(in: &subs)
}
and in LRVCoordinator.swift I have
private var details = LRVDetails()
func UserDidPressButton() { // this is a delegate method from the VC
details.heading.send("New Heading")
}
I thought that because LRVDetails is a class, it only stores a reference. so if I simply instantiate it in each file and then call send() on it, the publisher should update, and then emit the value to the LRVViewController subscription and update the heading.
But it does nothing. The subscriber never receives a value ( I checked with a .print() operator ). When i call lrvController?.details.send() -- without the private tag -- in the coordinator, it works fine.
Is this because the subscriber is stored in the 'subs' variable in LRVCoordinator, and thus it has to be updated with the subscriber in LRVViewController? That's my best bet.
If not, why doesn't this work?.
Thanks!
Edit:
If they are different instances, why does this code print two?
class myClass {
var int = 1
}
let test1 = myClass()
let test2 = myClass()
test1.int = 2
print(test2.int)
// prints 2
I thought that because LRVDetails is a class, it only stores a reference. so if I simply instantiate it in each file and then call send() on it, the publisher should update
Your understanding is incorrect. You have created two different LRVDetails objects. (The syntax LRVDetails() creates a new object) One in the VC, and the other in the coordinator. You are correct that it stores references though, so it would work if you actually make the two details variable refer to the same LRVDetails.
But actually, you don't seem to need a details variable in the coordinator. Since the coordinator is the delegate of the VC, you could just pass the LRVDetails via the delegate method:
func UserDidPressButton(_ viewController: LRVViewController) {
viewController.details.heading.send("New Heading")
}
Needless to say, you should also change the caller of UserDidPressButton to pass self as the parameter.
Unfortunately I m newbie and cant solve this problem for days. the problem is I cant success to gather score from game engine class to view controller so that I can show it with a label.
here is I want to extract data from this func to global variables.
private func nextMove() {
current = .random(config)
if (well.hasCollision(current)) {
stopTimer()
scene.showGameOver(scores)
//extract score to another variable can be reached by another class
} else {
scene.show(current)
}
}
I tried many methods, creating a global class with init method, creating struct, creating protocol..etc that already written here, but never successed. any idea will be appriciated. thanks..
A Singleton could be what you're looking for.
Here's an example:
class ScoresSingleton {
static let sharedSession = ScoresSingleton()
var scores : Int = 0
}
You may then use globally by:
// Setting
ScoresSingleton.sharedSession.scores = 10
// Getting
var scores = ScoresSingleton.sharedSession.scores
Hope that helps.
From a few initial tutorials, I see that properties belong to a Class and are essentially 'global variables' as used in the C++ world (coded in this years ago). I also see variables as more of a 'local' entities only used / storing information within a method.
Then I came across this Quora thread: https://www.quora.com/Apple-Swift-programming-language/What-is-the-difference-between-a-property-and-a-variable
Now I see properties being able to execute code associated with their invocation. This is very cool, but also opened up a whole bunch of other questions for me.
Are there other simple and clear ways to remember the distinction between a property and a variable?
Properties belong to an object, whereas variables do not. A variable can be declared without having to be associated with a particular class, or other object. A property must be associated with a particular object (i.e.: a class, enum, or struct)
Local variables are just things that you work with. You have full control over these, and if you change a variable in a function, nothing outside of your function is ever gonna know. If I write a framework and you use it, and I decide to change something about a function's local variables, your app that uses my framework will keep working just as if nothing changed.
Classes, on the other hand, describe a contract. When you use a class, you have access to everything they publicly advertise. This means that if I write a framework and you use it, if I ever change or remove a public member on a class, your code will break if you were previously using that member.
For this reason, in many languages, it's bad practice to mark instance variables as public. Instance variables having no logic attached, if I want at some point to trigger something when a field is changed or if I want to remove the field entirely (and instead report a value in a sub-object or something), then I'm stuck with changing the public contract (turning the field in a pair of get/set methods, for instance), and possibly breaking your code.
Swift makes properties an indirection for this reason. Swift properties can be treated as dumb values for the most part, but if you ever need to change from a stored value to a computed value or something, you can do it without changing your class's interface. That way, you don't break existing code that relies on the property.
Swift variable, constant, Property
[Swift types]
variable - named storage of address. Every variable has a type which defines a memory size, attributes and behaviours
Swift variable and constants
constant is a variable but can not be modified after definition.
//definition
var <name> = <initial_value>
//type annotation
var <name>: <Swift_type> [= <initial_value>] // [] is optional
//var - variable
var myVariable1 = 11
var myVariable2: Int
myVariable2 = 12
//let - constant
let myConstant1 = 21
let myConstant2: Int
myConstant2 = 22
Global and local variable
Global variable is a variable which is defined out of function, class.
Local variable is: variable inside a type context(class, struct, enum)[About], inside a function, function parameter
Property
property - associate value with a type context. It is a variable + bounded getter/setter. It has field syntax but uses methods(getter/setter) under the hood.
Stored properties and computed properties
They can belong to instance(instance property) or type(type property):
Stored property (class, structure)
Computed property (class, structure, enum)
Stored property - is a local variable -> variable inside a type context. Swift stored property does not support instance variable like Objective-C.
variable stored properties - var
constant stored properties - let
It supports property observers (willSet, didSet)
Computed property - provide getter and optional setter to calculate a value every time
public class Person {
var firstName = "John"
var lastName = "Wick"
var fullNameComputedProperty: String {
get {
return "\(firstName) \(lastName)"
}
//optional
set {
let arr = newValue.split(separator: " ")
firstName = String(arr[0])
lastName = String(arr[1])
}
}
var addressStoredProperty: String {
//Property Observers
willSet {
print("old address:\(addressStoredProperty)")
print("new address:\(newValue)")
//addressStoredProperty is not updated yet
}
didSet {
print("old address:\(oldValue)")
print("new address:\(addressStoredProperty)")
}
}
}
Lazy Stored property
Property is calculate during first access to it(on demand)
only var lazy because let must have a value during initialization
Init/customize stored property by closure
Official doc
You are able to init/setup/customise a stored property with a help of closure
() at the end executes the closure immediately and assign a value to stored property(calculate and return a value).
in initializing case it is not possible to access to any instance variable or function because it has not initialized yet
in initializing case it will be executed only once for every object or if you use static - once for the class[Example]
Examples
func testStoredPropertyWithClosure() {
class ClassA { }
class ClassB {
static let staticStoredProperty: ClassA = {
//is called only when you access to it like ClassB.staticStoredProperty
print("init staticStoredProperty")
return ClassA()
}()
var storedProperty: ClassA = {
print("init storedProperty")
//self.foo() //Error: Class declaration cannot close over value 'self' defined in outer scope
return ClassA()
}()
func foo () {
storedProperty = {
print("customize storedProperty")
return ClassA()
}()
}
}
let b = ClassB()
b.foo()
ClassB.staticStoredProperty
}
closure stored property vs Computed property
closure stored property is called once and can be changed after initialization(if it is var)
Computed property is calculated every time when it is called
[Java variable, property...]
I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash
Here is a swift class file:
import Foundation
class DataPreparation {
// Variables
var userCountries = [String]() //Just 1 or 2 countries
var correspondingFullArrays = [[String]]()
//Get and set raw user countries from current image
func getUserCountries(countries: [String]) -> [String] {
userCountries = countries
return userCountries
}
func getCorrespondingFullArraysToUserCountries() {
println(userCountries) //Is empty
}
On a separate file viewController, the 2 methods lisOfCountries and getCorrespondingFullArraysToUserCountries are invoked independently one after the other:
DataPreparation().getUserCountries(otherArray[0])
DataPreparation().getCorrespondingFullArraysToUserCountries()
Problem is that second method can't access userCountries data set by first method because it is empty.
My guess, and you will correct me if i'm wrong, is that variables get destroyed after each method call.
So how are we supposed to handle small data persistance from one function to another, when they are called separatly ? I could of course regroup all in a single function, but is this the good way of doing ?
In each line of this code:
DataPreparation().getUserCountries(otherArray[0])
DataPreparation().getCorrespondingFullArraysToUserCountries()
you are creating a new instance of DatePreparation, and then calling a method on it. Since you do not assign the instance to a variable, the instance is destroyed as soon as it goes out of scope, which in this case corresponds to the same line where it is instantiated.
You should create an instance (and not 2), and assign to a variable. Then, you can call methods on that instance:
var dataPreparation = DataPreparation()
dataPreparation.getUserCountries(otherArray[0])
dataPreparation.getCorrespondingFullArraysToUserCountries()
When you are calling the function getCorrespondingFullArraysToUserCountries on DataPreparation() you are creating another instance each time.You are creating DataPreparation() instance each time. instead use
//create instance only first time
var dataPreparation:DataPreparation = DataPreparation()
//call methods on same instance
dataPreparation.getUserCountries(otherArray[0])
dataPreparation.getCorrespondingFullArraysToUserCountries()