Trouble putting data into global variables - ios

I have several method implementations that need access to the same variables. For example, I have a variable that I am placing a user's first name in, and I need to be able to access this variable in all of my various method implementations inside the same View Controller.
I have created a custom class called "PotentialFriend" which is a subclass of NSObject. PotentialFriend's header file also contains an NSString property called "name".
In my view controller's header file I have created an instance of my PotentialFriend class called "potentialFriend". I can successfully type "_potentialFriend.name" in the View Controller's main file, but if I set it to equal something like #"Steve" it shows as (null) in the console.
However, if I do the following, and actually initialize a local variable, I can successfully get the variable to hold the data:
PotentialFriend *potentialFriend = [[PotentialFriend alloc]init];
potentialFriend.name = #"Steve";
But this doesn't work for me because I have another method implementation in this VC that sets up the UITableView's settings and I need to be able to access the value of "potentialFriend.name".
The only way I know how to do this is with global variables, but like I said I can't get the global variables to actually hold the data.
Any ideas why I can't get the global variables to work?
EDIT:
I just went and ran a test. I created an NSString object in my View Controller's header file, and was able to successfully get it to hold data as a global variable like this:
_potentialFriendz = #"Steve";
So it must have something to do with the fact that I created my own custom class called PotentialFriend, but I still don't understand why that isn't working.

It sounds like you aren't allocating and initializing an object for potentialFriend. Just declaring it as a property in your header doesn't instantiate an object inside of it. It is just a property that can hold an object of the specified type.
You need to do something like:
self.potentialFriend = [[PotentialFriend alloc] init];
in viewDidLoad: or at some other initialization method (like initWithCoder:)
In other languages, setting potentialFriend.name without an instantiated object would throw some kind of exception or something, but Objective-C allows sending messages to nil (you are doing the equivalent of [potentialFriend setName:#"string"] when you use the dot-syntax).

Related

Shared variable between two tabs for Xcode

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.

iOS Objective C View Controller Not Using Properties

Let's say I have a simple app that is loading data into a table view. It then allows you to view details (etc).
My table view controller on first load looks something like this below.
Notice I am not using an "property" declarations for these variables. Is this OK? Are there any disadvantages regarding the way memory is then handled?
#interface TblVC ()
{
MBProgressHUD *hudLoad; // new up loading while I go get data
NSMutableArray *results; // set to results after loading data
CLLocationManager *locManager; // get location in view load
}
#end
#implementation TblVC
{
}
- (void)viewDidLoad
{
// spin up the above variables here which can then be used in other methods inside view controller
}
Just use properties. There is absolutely no reason to use the old-style instance variables anymore.
Apple's documentation on properties goes into detail about the benefits. https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html
An instance variable is unique to a class. By default, only the class and subclasses can access it. Therefore, as a fundamental principal of object-oriented programming, instance variables (ivars) are private—they are encapsulated by the class.
By contrast, a property is a public value that may or may not correspond to an instance variable. If you want to make an ivar public, you'd probably make a corresponding property. But at the same time, instance variables that you wish to keep private do not have corresponding properties, and so they cannot be accessed from outside of the class. You can also have a calculated property that does not correspond to an ivar…
Without a property, ivars can be kept hidden. In fact, unless an ivar is declared in a public header it is difficult to even determine that such an ivar exists.
A simple analogy would be a shrink-wrapped book. A property might be the title, author or hardcover vs. softcover. The "ivars" would be the actual contents of the book. You don't have access to the actual text until you own the book; you don't have access to the ivars unless you own the class.

Passing Arrays/Objects between ViewControllers in Swift

Following on from this question: Is there a reason that Swift array assignment is inconsistent (neither a reference nor a deep copy)? -
I have been playing with passing objects in Swift and noticed some strange results.
To clarify the kind of behaviour i'm used to (prior to Swift) would be that of Objective C.
To give an example in one of my Applications (written in Obj C) I have the concept of a 'notification list'. - really just an array of custom objects.
In that App I often pass my global array of 'notifications' to various viewControllers which provide a UI to update the list.
When I pass the global array to a child viewController I assign it to a local array variable in the recipient object. Then, simply by updating/changing the local array these changes are reflected in the global array on the rootViewController. I understand this behaviour is implicit in Objective C as objects as passed by reference, but this is really handy and I have been trying to replicate this behaviour in Swift.
However whilst I have been rewriting my App in Swift I've hit a wall.
I first tried to pass a Swift array of strings (not NSMutableArray) from the rootViewController to a child viewController (as described above).
Here is the behaviour when passing in the array of Strings the child viewController:
I Pass in:
[Bill, Bob, Jack] and then assign this passed array to a local array for local modification,
Then I append the String “Frank” to the local array
The results are:
Local array = [Bill, Bob, Jack, Frank]
Global array = [Bill, Bob, Jack]
No changes to the local array are reflected back to the global array. - The SAME result occurs for a change of element (without changing the length of the array.)
I have also tried the above experiment with a more real world example - passing in an array of my custom 'notification' objects to a child viewController. The SAME result occurs with none of the changes to the locally assigned array of custom objects being reflected to the original global array that was passed in.
This behaviour is not desirable to me, I assume the best practice here is to use delegate protocols to pass the modified array (or whatever object) back to the parent object and then to manually update the global array?? - if so this creates quite an extra workload over the Objective C style behaviour.
Finally I did try the inout keyword, which effectively lets you directly modify the function parameter var thats passed to the destination object.
Changes are reflected back to the global array (or object) However the problem is, if the input parameter is assigned to a local variable (to edit outside of scope of the init function) changes to the local variable are still not reflected in global scope.
I hope the above makes sense - It's really stifling my productivity with Swift.
Am I missing something or is this schizophrenic behaviour expected?
If so what is best practice on passing modified data back, delegates?
The linked question provides the answer - it is for performance.
The behaviour may not be desirable for you, but I would say that relying on side-effects from calling methods to modify parameters is the behaviour that is not considered desirable - particularly in a multi-threaded, multi-core environment where data structures can be corrupted.
A design that relies on side-effects is flawed, in my opinion.
If functions need to modify the "global" then they should either return the new value, or if that isn't possible then you should wrap your array inside an object and provide appropriate functions to manipulate the data values.
Swift blurs the lines between intrinsic and object somewhat with arrays, which makes it a little confusing - in Objective-C an NSMutableArray is an object so it always passed by reference.
For notifying other objects that the data has changed you can use an observer pattern. The typical delegate pattern only has a single registered delegate - With an observer pattern you can have multiple registered observers.
You can do this through NSNotificationCenter or an array of "delegates". The former has the advantage of decoupling the code more than delegation
Why don't you create a Model class that contains the array as a var. Add methods to the Model class to manipulate the array and store the new instance in the property. Create a single instance of the Model class at startup and pass it to the view controllers. They all access the array through the Model or through methods in the Model class. The behavior of Swift (where it copies the array on change of size) will be hidden from all of the view controllers.

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.

How to use the values from an array defined in another class

I followed John Wordsworth tutorial (http://www.johnwordsworth.com/2011/10/adding-charts-to-your-iphone-ipad-app-using-core-plot/) to produce a line graph with CorePlot.
Later I decided to get some JSON data from a website so that I can use it for my graph.
All the url connection methods are performed in my ViewController.m file. And there I created an NSArray called indicator containing all the JSON data.
My problem is that all of the graph's parameters are defined in a separate NSObject class called SimpleScatterPlot.m where I would like to use the array of strings "indicator" (defined in ViewController.m) to customize the x-Axis labels.
What do I have to do so that I can use this JSON data array in SimpleScatterPlot.m?
I tried #import "ViewController.h" but it did not solve it.
Thanks in advance for any guidance you can give me.
Not sure if I am understanding it correctly, but I think you have not try to access the indicator nsarray from SimpleScatterView.m but to set there the value from ViewController.m.
1) Define a public NSArray in SimpleScatterView.m and synthesize it.
2) Instead of trying to gain access, use "prepareForSegue" in ViewControler.m to set indicator in the destinationSegueController.
3) In SimpleScatterView.m implement "-(void)setIndicator:(NSArray *)indicator" and update the GUI as needed.
Although the "Model - View - Controller" paradigm recommends not to use ViewControllers to perform communications and so on, but that is for another thread.
Create a property in Viewcontroller and synthesize it. Then pass the JSON data to the property.
To access the array in SimpleScatterView.m:
Viewcontroller *viewcontrol = [[ViewController alloc] init];
You can then access the array by using viewcontrol.indicator.
NSLog(#"%#",viewcontrol.indicator);
If I do not read incorrectly what you are trying to do, my guess is that you could appropriately use SimpleScatterView from within your UIViewController view (this is, assuming that SimpleScatterView is actually a UIView).
In this case, when your controller has downloaded its data, you could instantiate SimpleScatterView and display it by adding it to your view controller view.
... <data has been downloaded> ...
SimpleScatterView* scatterView = [[SimpleScatterView alloc] initWithFrame:...];
[self.view addSubview:scatterView];
When you initialize your SimpleScatterView, you could pass it a reference to the data array (say, in its custom init method, or using a property). e.g.:
SimpleScatterView* scatterView = [[SimpleScatterView alloc] initWithFrame:... andData:(NSArray*)...];
or:
scatterView.dataSet = <your_json_array>;
Of course, you have plenty of alternatives to this design. Specifically, I would mention:
the possibility of using a model class to handle all of your data, such that the controller writes the data to the model, while the plot view reads the data from it. The model could be implemented though a singleton in this case for ease of access;
the possibility of using a "dataSource" for your plot view: this would entail defining a protocol between the view and its datasource (e.g., a getData method); your view controller would play the role of the data source (in other words, instead of passing the array to the plot view as in above example, you would pass a reference to the controller and the view would access its data).

Resources