passing data to Programmatic UITabBarController - uiviewcontroller

Whats the best way to pass data to a UITabBarController from a UIViewController in Swift 3?
I am building a single view application and I have a details view which needs to be split into 4 sections. I want to use a TabBar to achieve this and pass an ID from a list in UIViewController and then pass that value down to each individual UIViewController in the TabBar. This is so that I can call a web method which takes the ID to fill out data in the individual views.
The app is being converted from Obj-C with a Storyboard to Swift 3 without a Storyboard so I don't have access to the usual segue definitions I did have. I also used to use UserDefaults in the Obj-C version to save away the selected ID and then pull it out again in the individual views. However, I'm not convinced this is the best way to achieve what I am after.
So far I have tried passing along by accessing the UITabBarController class in the initial UIViewController and setting a variable in the ViewController and then passing it down to the ViewController in the first tab. This works if I am pushing from UIViewController to UIViewController (The first view in the TabBar, but run standalone) but doesn't work when the target Controller is a TabBarController.
I have also tried posting a Notification in the ViewControler and Observing the result in the TabBarController. However the observe code is never called. I'm using:
Selected Row:
NotificationCenter.default.post(name: notificationName, object: selectedRowID)
Then in ViewDidLoad on the TabBarController (Also tried viewDidAppear and viewWillAppear):
NotificationCenter.default.addObserver(self, selector: #selector(setID), name: notificationName, object: nil)
The above works, BUT, only when I select the second tab and then go back to the first tab and then it gets called twice!?!?!
I have also tried a Singleton class, but that just flat out didn't work.
Is using UserDefaults really the best way to do this?

You can create a new class holding the variables to pass the value:
class MyTabController: UITabBarController {
var myPassedString = String()
}
Go to the StoryBoard, select the tab bar controller and open the Identity inspector, change the class to: MyTabController.
let tabBarController = window?.rootViewController as? UITabBarController
Define in each ViewController where you need the shared data:
var myPassedString = String()
To make to this variable shared, code in each ViewController:
override func viewDidLoad() {
super.viewDidLoad()
myPassedString = (tabBarController as! MyTabController).myPassedString
}

Thanks. I am not using a storyboard.
Got it working using a singleton after realising that
1. To get the values back out you must call the singleton in viewWillAppear rather than viewDidLoad and
2. When programmatically creating this, the viewWillAppear of the first child view in the TabBar is observed instead of the viewWillAppear of the actual TabBar controller itself.
for reference for anyone else trying to do similar, my code as follows
open class LastAccessedEvent {
var ID : String = ""
class var sharedInstance : LastAccessedEvent {
struct Static {
static let instance : LastAccessedEvent = LastAccessedEvent()
}
return Static.instance
}
var EventID : String {
get{
return self.ID
}
set {
self.ID = newValue
}
}
}
then in the viewWillAppear of the child view,
if !LastAccessedEvent.sharedInstance.ID.isEmpty{
let myEventID = LastAccessedEvent.sharedInstance.ID
}

Related

handle json type data binding Angular 4

What is the best to handle reference type data, for example json data, bind to a component. For example,
<my-component [data]="jsonData"></my-component>
where in .ts jsonData= {'key1':'value1','key2','value2'}
Now if on button click I change my jsonData as
{'key1':'valueX','key2','value2'}
Angular won't be able to detect this change, hence doesn't run ngOnChanges(),because angular only checks reference of the data-binding changed or not as a result view won't be refreshed. How should I deal with it?
I can put logic to refresh my view in ngDoCheck() but it is called a lot of times. If json is large, then processing it each time ngDoCheck runs can be an expensive operation.
One possible solution I could think of is to make a service for that component, and on button click, pass the new json to that service, service will publish that event with changed data to my component and component, listening for the events of service, will refresh the view with that new data.
Is there any easier way to handle JSON/Array changes bind to component?
You can use a setter for #Input property in your component.
Intercept input property changes with a setter
Use an input property setter to intercept and act upon a value from the parent.
#Input() set data(jsondata: any) {
this._data = jsondata;
}
Refer to the documentation here for more details.
Update after comments:
Yes you can implement ngOnChanges as well for this,
as the documentation states:
You may prefer this approach to the property setter when watching multiple, interacting input properties.
See the below example from the documentation,
export class MyComponent implements OnChanges {
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
for (let propName in changes) {
let changedProp = changes[propName];
let to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) {
console.log(`Initial value of ${propName} set to ${to}`);
} else {
let from = JSON.stringify(changedProp.previousValue);
console.log(`${propName} changed from ${from} to ${to}`);
}
}
}
}
You can get hold of the old and new values of your members using previousValue and the currentValue properties respectively.

How AppDelegate reference to subclass of ViewController works

I am using Swift 4 and I am wondering how having a reference to a ViewController in AppDelegate works. I have two questions which I have put in bold. I understand that AppDelegate is a delegate that has functions that get called when the app becomes foreground or background, or even when the user does the "open with" event on a file extension that my app is registered to handle and selects to open that file with my app- AppDelegate lets the app know that took place. 1) But what I don't understand is how AppDelegate should communicate with my Views to tell them that these events happened. I am wondering what the good design pattern is for doing so. My solution is to have a reference to a viewController so here is an example:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var hotSpotRISViewController = HotSpotRISViewController()
...
func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
hotSpotRISViewController.setURL(theURL: url)
return true
}
}
What I don't understand is how this actually works. I thought that AppDelegate looks at Main.storyboard and starts that and that Main.storyboard has it's own reference to HotSpotRISViewController(). 2) How is it that by making a reference to HotSpotRISViewController() in the AppDelegate, that it uses this reference instead?

Function used in selector in iOS Swift can only be in the same file?

I have created a time activate on the View Controller.
It needs to call a selector, and for that selector, I am calling a function.
When the function is in the View Controller file, it works. If i put this function in another Swift file that I created in the app, it does not find the Selector and I get an error during runtime.
Is it possible to put functions in Swift in different files or not?
#IBAction func startNotificationTimer(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "update_start", userInfo: nil, repeats: true)
timerStatus.text = "Started"
}
"update_start" is in a different file.
Thanks for the help
It doesn't matter what file your update_start method is in, but it does matter that it be a method in your View Controller class. If you define it in a file other than the one where that class is declared, you'll need to define the method in an extension on that class.
Also, NSTimer selectors should end with a colon (update_start:), because they take a single parameter (the timer) when called.

Swift + Xcode 6: What method is called when storyboard segue is performed

I am creating my first app with storyboards. So far, I have two ViewControllers - on the first VC, the user taps a button, and it takes them to the second VC, using a 'Present modally" segue.
Is there some method that is called when the new view appears? I want to be able to write the code to start the game in here.
You should look at viewDidLoad(), viewWillAppear(_:) and viewDidAppear(_:). Most likely what you need is the first one.
I think you're searching for the prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "Load View") {
// pass data to next view
}
}
It's called right before the segue is performed so you can for example identify the target ViewController and pass it some params.

Passing value from one ViewController to another in Storyboard in SWIFT Language

I start project in Swift Language. Apps almost done. Single ViewController logics are all done. But I need to communicate with each other ViewController.
I want to pass some value from one ViewController to another ViewController. I'm using StoryBoard. I want to pass value using prepareSeague function as like Objective-C has. Is there any way to do this without using delegate-way.
In the first view controller handle prepareForSegue method and provide additional values to the second view controller, like:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "someidentifier" {
let vc = segue.destinationViewController as MySecondViewController
// vc.someProperty = someValue
}
}