I have two View Controllers accessing constants inside a single global structure, defines in a separate file. The structure is as follows:
struct Constants {
struct FAQ {
struct General {
static let QUESTIONS: [String] = [
//Some String questions here
]
static let ANSWERS: [String] = [
//Answers here
]
}
struct Specific {
static let QUESTIONS: [String] = [
//Questions
]
}
}
struct Tips {
struct General {
static let QUESTIONS: [String] = [
//Questions
]
static let ANSWERS: [String] = [
//Answers
]
}
}
}
Now, from one VC, controlling the FAQs of my app, I access Constants.FAQ and all the data inside it.
From another VC handling the Tips, I access Constants.Tips.
Now, to my question. What is the lifetime of the FAQ structure, once the FAQ View Controller has been removed from memory by the OS? Does it stay in memory for the app's lifetime?
To give a use case, take for example:
FAQ VC is created and starts execution. FAQ VC accesses Constants.FAQ.General.QUESTIONS and does its processing. Now, FAQ VC stops execution and is removed from memory.
After a while, FAQ Tips is created and starts execution. It accesses Constants.Tips.General.QUESTIONS and does the processing.
Now at this time, is the Constants.FAQ.General.QUESTIONS String array still in memory? Or was it's lifetime limited to it's access in the FAQ VC.
Another question, while I'm at it: Is using this kind of structure good practice?
Thanks.
This structure and all the sub structures and static methods will be defined at app startup, and will exist as long as the app is running.
To answer your second question, no this not generally considered good practice. Global data is generally error prone, resistant to change, and difficult to test. In your case the data is static, which avoids some of the issues with global shared data.
Usually data is kept in a separate data file and loaded at runtime. it is advantageous to pass the data to each view controller, by setting a property, or through a method call.
Consider loading json or plist into an array or dictionary, then parse the data into an instance of this structure.
It depends on how Constants() is created. If it's done at file scope in the same module that defines it, then it will never go away, since 'let constants = Constants()` retains a reference.
Related
I'm trying to simplify a huge file in Swift and I'm not sure what would be the best approach. It might be a very naive question as I'm still fairly new to Swift :)
For the sake of simplicity, let's say I have one User struct and in it I can call a ton of different setupModeA, setupModeB and so on, called depending on various conditions:
struct User {
var currentMode: String
mutating func setupModeA() {
currentMode = "A"
}
mutating func setupModeB() {
currentMode = "B"
}
// and so on for each mode
}
I'd like to be able to take the setupMode*() functions and their associated helper functions into a separate file to make it easier to read through, since most of the time I'd be focusing on only one mode.
I was thinking:
struct ModeA {
mutating func setup() {
// change the mode from there
}
}
However I can't figure out how to simply pass and mutate the User info to this new struct.
I thought about:
initializing a bunch of ModeX objects with a user variable in the User struct and passing the User on init, but it creates weird behaviours.
using static methods on the ModeX structs, but then I'm not quite sure how to mutate the User
taking a more functional approach & not mutating the User but instead cloning it and re-assigning it each time... this would probably work but since my code is very sequencial it feels like added complexity / memory usage for nothing.
switching from struct to classes and hoping it gives me other options... but I'm afraid everything would break
really complex things that feel too weird and unintuitive to be mentioned here :)
Any pointers would be much appreciated.
I am new to Swift, coming from C and Java background.
I am trying to pass a struct (called 'Player') object from one view controller to another, and as i learned one proper way to do is first defining it in the target VC like:
class TargetVC: UIViewController {
var playerVar: Player!
}
And then assigning it in prepare function inside source VC:
if let targetVC = segue.destination as? TargetVC
{
targetVC.playerVar = myPlayer
}
But i thinked about simply using a global variable to pass the Player object to the target view controller by defining it like:
var myPlayer = Player() // Global Variable
class SourceVC: UIViewController {
}
Then reaching it in target VC:
class TargetVC: UIViewController {
myPlayer.name = // Stuff
}
Would this method cause any problems in runtime or does it have any disadvantages? If yes, what are them?
Thanks in advance.
Passing data gives to you to conform programming principles to build a good classes, even in your example. It makes classes more extendable and reusable in many cases.
Global var gives some advantages to reach needed data, but you have to care about reference cycles, memory managing, handle threads, queues and blocks, etc.
You can also read out about singletones, static vars,... – where you can get some data from class scope vars.
One disadvantage is, as the name suggests, everything has access to global variables, even things that have no reason to see, much less modify, that data.
In an app that I am working for, I need array variable that can be used in all UIViews. Currently when the array is changed in a view it is stored in the database, and when I need the updated version of the array in the previous view, I use the viewWillAppear method and retrieve the updated array from the database. While going to another view by a segue, I use the passing data by prepareForSegue, but if I use the back button, or just change the screens via tab bar, I use the viewWillAppear and a query.
Is there any way that when the array is created in a view, the data in it will be accessible in all views?
As I've stated in my comment, singletons are generally frowned upon for a myriad of reasons. However, there is much debate on this topic:
What is so bad about singletons?
Having said that, the best way I know to make a variable globally available for the session is by creating a singleton.
struct myArray {
static var data: [Int] = []
}
You could set this singleton up to fetch the records using CoreData and store the current working version in a static variable for quick access.
note: I am really very curious to see some other approaches.
Singleton
Singleton is basically a global variable that you can use them in any views, but some developers experience some bugs and difficulties, use it at your own risk, I recommend this method when you're definite that you will use that data a lot (STILL RISKY), but this method is like goddess of data handling :).
Create a NSObject subclass and call it DataManager.swift (I call it data manager cause it handle data.) as following:
import UIKit
class DataManager: NSObject {
//Store Data Globally
static var someData: NSArray! //This Boolean, you can choose whatever you want.
}
the static is what keep your data live.
Now you can store and receive someData from anywhere like you handle any data type like this.
//Store
DataManager.someData = []
//Receive
print(DataManager.someData)
New to iOS development and trying to develop my app with correct practices. My main issue is trying to figure out the best way to store a bunch of simple data globally.
The app connects to a computer adhoc wifi and gathers information about the current state. (ex// gpu temp, computer name, display resolution, date/time, etc). The various view controllers I have then allow the user to adjust some of these settings so I want to be able to keep track of these changes across all view controllers.
I have been wrestling between using a plist, core data, or singleton class to store all the information. Since the user will be connected to one computer for the life of the app I am leaning towards singleton since it seems easiest to get and set data. However, I have read that using a singleton is really not the best practice for various reasons. I have read up on plists and core data a little and seems like a decent amount of work just to get and set values. Should I spend the time using those or would some other method be a better way to accomplish all of this.
Update:
After both answers from Aaoli and Swift Rabbit. I wanted to clarify for my use case that the data I am storing did not need to be "saved" when the app was closed. So if you are coming to this question and need to store your data even if the app closes look at #SwiftRabbit's answer.
To use Global variable in Swift you need to define struct outside the class and because you don't need the variable to be swimming in the whole app memory without binding:
struct GlobalVar {
static var myvar = 0
}
To use simply the global variable from anywhere use :
GlobalVar.myvar = 10
In my case, using "simple" data, which is not sensitive, I usually use the user defaults. They can be accessed globally through the project. For example, it can be set this way :
var prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
prefs.setObject(myValue, forKey: "myKey") // myValue is of type typeOfValue
prefs.synchronize()
It can be accessed this way :
var prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
prefs.valueForKey("myKey") as? typeOfValue
Using setObject can be used for any kind of value, that's why I cast it when retrieving it from the userDefaults. You could actually use other methods to insert it and retrieve it that would make your task easier. For example, you could tell you are inserting a bool with :
prefs.setBool(<#value: Bool#>, forKey: <#String#>)
And specify you are retrieving one with :
prefs.boolForKey(<#defaultName: String#>)
All methods can be found in the apple documentation here
My opinion: create a singleton, e.g. in this way:
class MyClass {
/// The singleton instance
private static var singletonInstance: MyClass!
class var sharedInstance: MyClass! {
get {
singletonInstance = singletonInstance ?? MyClass()
return singletonInstance
}
}
private init() { }
}
Or in this way:
class MySingletonClass {
static let sharedInstance = MySingletonClass()
private init() {
}
Many people consider Singleton a poor design pattern, but in my opinion it is not. It is good to have a pattern which ensures that a piece of information is only available once. What is not good is to have global access to this Singleton from anywhere in the application. Some people counter-act this with "Dependency Injection", which basically means that your classes does not "pull" the information from the Singleton but the information is "pushed" into the class from (some other instance) outside. This promotes loose coupling and better test-ability. But this is an advanced topic which needs much more information. (Google "typhoon framework" for iOS if you are interested in this)
I been reading about avoiding the mutable state, and how the singleton pattern is bad for having a global state.
I see few answers about Dependency injection http://www.objc.io/issue-13/singletons.html, but I can not find how to solve this basic approach:
How maintain the user data arround the app?, the solution is to pass the user information from the one view (where is requested by webservice) through the views by parameter to the seven push view (where is needed again) ?
there is a better way?, or the singleton pattern is sometimes necessary?
I use a singleton to represent my Engine class in Swift. It is initialized once on launch and contains static class variables stored in a struct. Works fine for me.
class Engine{
struct properties{
static var resourceManager:ResourceManager!;
...
}
init(){
properties.resourceManager = ResourceManager();
}
In another class I can then call
Engine.properties.resourceManager