Shared variable between two tabs for Xcode - ios

I have two view controllers that I am working on which both inherits from a Base view controller
class A_ViewController: BaseViewController
class B_ViewController: BaseViewController
Both of those VC interacts heavily with my firebase database. So I want a variable to keep track of all the downloaded items so those two VC can access it without the need to re-download the file again.
I tried to put a variable name in BaseViewController for the two A,B class to access
var allPostsDownloaded: [Post]!
So before A_VC downloads any data, it checks for this allPostsDownloaded variable and loads from it if the data exists. If it doesnt exist, I append to this variable. So when switching to B_VC, the same can be done and no data is re-downloaded when not required.
Reason I am not using segue or protocal to pass data around is that the two VC interacts quite heavly with my database. So it was alot cleaner to try and have a mutural data varaible to keep track of where things are.
However, the problem is that i
var allPostsDownloaded: [Post]!
gets called whenever I switch between A and B VC (Which are tabs). This will cause the variable to be empty and de-initialised.
I guess I could use a global variable instead but that is not good practice? Could anyone please explain why it gets re-called when new tab shows up? And the best solution for this.

as #avi mentioned create new singleton class, then you can pass and read easily. Below is an example
class PersistentData
{
static let sharedInstance = PersistentData()
// your global persistent variable
var allPostsDownloaded = [Post]()
}
So in your controllers you can simple read and set as below
// read
print(PersistentData.sharedInstance.allPostsDownloaded)
// set new data. this just example, hence depends on your case
PersistentData.sharedInstance.allPostsDownloaded.append(newPost)
PersistentData.sharedInstance.allPostsDownloaded = posts
also keep in mind that if you want to read new value when switching between tabs, you can get the updated in viewDidAppear or viewWillAppear

You can create a Singleton class with a instance variable and can save all downloaded data, and can access singleton class variable from any where of your project's classes.

Related

Best way to avoid singleton

For our iOS programming class we must make a framework for Swift iOS. We had the idea of a framework simplifying CoreData manipulation. I began by creating a class where you put the NSManagedObjectContext created in AppDelegate at the beginning, so you don't have to write this long (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext anymore.
open class SimpleCoreData {
var context: NSManagedObjectContext
init(context: NSManagedObjectContext) {
self.context = context
}
func delete(entity: NSManagedObject) /*-> Bool*/ {
// some code
}
func getAll(entityClass: NSManagedObject.Type) throws -> [NSManagedObject]? {
// some code
}
func create(entityDescr: NSManagedObject.Type) -> NSManagedObject? {
// some code
}
But I would like it to be accessible from everywhere in the application, and this simplification would be useless if you have to instantiate this each time.
I was first thinking about a singleton, but I recently learned it wasn't a good practice.
So do you know any solution to make this accessible from everywhere in the client app? Or maybe singleton is okay in this case?
Keeping Rob Napier's excellent comments in mind, if you decide to avoid a singleton in this case, a common approach would be
Create an instance of your SimpleCoreData class in the app delegate when the app launches.
Have the app delegate pass this to your initial view controller. That view controller would have a SimpleCoreData property but would not create the instance-- it would expect one to be assigned by whichever code creates it, which here is the app delegate.
Repeat this pattern everywhere you need a SimpleCoreData. That is, when you create an object that needs a SimpleCoreData, make sure it has a property with that type, and assign a value when you create it. For view controllers, a good place to do this is in prepare(for:sender:), if you're using segues.
It's not necessary to create the SimpleCoreData in the app delegate, though. You could create it at the first point in the app hierarchy where it's needed, and pass it along from there. So if it's only needed in the second view controller in the hierarchy and in other objects loaded from there, create it in that view controller.
This doesn't make your SimpleCoreData instance available everywhere automatically, it means that you're creating one and then passing it around. Often that works fine, but as Rob notes it's not always the best approach. It can lead to passing an object along to an object that doesn't need it, because some other object that gets created later on does. For example if you need SimpleCoreData in your initial view controller but then not again until five levels down the hierarchy, you still need to pass it along every step of the way. That's an example of when a shared instance can be useful. It's not a singleton since other instances are allowed, but it's a default instance that can be used as needed.
I finally learned thanks to you that singletons aren't so evil, they can be used in some case, including this one, and it seemed in my case that it was a good choice. Maybe I will change it for a shared instance pattern.
So the singleton works well. Thank you everybody for your advices, I learned a lot in design patterns.

Using an Array Variable in All Views

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)

passing data between interfacecontrollers not working in apple watch

I was using this code to pass data between controllers.
InterfaceController.reloadRootControllersWithNames(["1","2","3"], contexts: ["adf","asd","asd"])
I called this code in willActivate() method, that you can see here.
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
InterfaceController.reloadRootControllersWithNames(["1","2","3"], contexts: ["adf","asd","asd"])
}
I have no idea about whats wrong with this code, in apple watch, the app is just refreshing again and again. Is there any other method to pass data between interface controllers while we use page based navigation.??
Please Find the below way of passing data in paged-based navigation in watch application (Watch os 2 and later).
Note :
An array of strings, each of which contains the name of an interface controller you want to display in the page-based interface. In your storyboard, the name of an interface controller is stored in the object’s Identifier property, which is located in the attributes inspector. The order of the strings in the array is used to set the order of the corresponding interface controllers. This parameter must not be nil or an empty array.
Objectiv C
[self presentControllerWithNames:#[#"FirstViewController",#"SecondViewController",#"ThirdViewController"] contexts:#[#"adf",#"asd",#"asd"]];
Swift
let controller = ["FirstViewController","SecondViewController","ThirdViewController"]
let pageContexts:[AnyObject]? = ["adf","asd","asd"]
presentControllerWithNames(controller, contexts: pageContexts)
If you call reloadRootControllersWithNames(NSArray, contexts: NSArray) onto the view controller, it will do as the function is titled: reload. Therefore, you have a view controller that refreshes all 3 of the items in the first Array you give, and then, since your current view controller calls willActivate() upon loading, it infinitely refreshes.
I don't have enough reputation to make this a comment, but my suggestion here is to find a method that you can call on an instance of the watch interface you want to in order to pass data.
What I mean is, if you have each interface as its own class, then you can make a method in the interface you want to get to that sets a property to whatever data you want to transfer before you present the interface.
I also use Objective-C more than Swift, but here's what my guess is as to what the Obj-C code would come to:
SecondWatchInterface *secondWatchInterface = [SecondWatchInterface new];
[SecondWatchInterface setSomeDataWithAStringValue:#"Foo"];
[self presentWatchInterface:SecondWatchInterface]
In Swift, I'm thinking this would equate to:
SecondWatchInterface *secondWatchInterface = SecondWatchInterface.new()
secondWatchInterface.setSomeDataWithAStringFile:"Foo"
presentWatchInterface(secondWatchInterface)
I'm unfamiliar with WatchKit myself; I typically use SpriteKit. I hope I helped identify what the problem was, at least!
To pass data between interface controllers you need to go for this method:
presentControllerWithName(“Second Watch Controller”, context: [“segue”:“pagebased”,“data”: "Data Passed Successfully”]);
Also, you can refer to this example: http://www.informit.com/articles/article.aspx?p=2272657&seqNum=3

How to avoid the global state in Swift

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

How to set values to a class without creating an instance of class?

Currently I have 2 ViewController files I want to share common data between, however I want to have a class called GlobalVariables store all of the variables throughout my code. In one of the ViewController files, I want to set the value of a variable (I have created a setVariable method in the GlobalVariables.m file) and from the ViewController, I want to call this method so it sets the variable. How can I do this without creating an instance of GlobalVariables as any changes I make to the instance will not carry over when I create another instance of GlobalVariables in my second ViewController file?
Use singleton Design Pattern. It’s an extremely powerful way to share data between different parts of code without having to pass the data around manually. And also it will always be a single instance through out the Application life cycle
Please check this link:
Singleton Class
Define your varible in AppDelegate. AppDelegate is a Singleton class.
And access that variable where you want.
It's unclear what you mean by "set values to a class". There are 3 kinds of variables in Objective-C: global variables, instance variables, and local variables. Local variables are not relevant. If you con't want to create an instance, then instance variables are out. So then you want global variables. They can be set and accessed by any function or method.

Resources