Access data / functions from several view controllers - ios

I am trying to build an app with several screens. Every screen should have different buttons which all call one function.
My problem is that I do not understand how to call one function from different view controllers with input parameters.
Also I want to have another variable defined accessible and changeable from every view controller.
This is what I kind of want my code to be:
import UIKit
var address = "address"
public func makeRequest(Command: String){
let url = URL(address + Command)
print(url)
}
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let command = "command"
makeRequest(Command: command)
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("address")
address = "address2"
}
}

If all classes are view controllers put the method in an extension of UIViewController and the address constant in a struct
struct Constant {
static let address = "address"
}
extension UIViewController {
public func makeRequest(command: String) -> URL? {
guard let url = URL(string: Constant.address + command) else { return nil }
print(url)
return url
}
}

extension UIViewController{
func getName(name: String)-> String{
print("hai \(name)")
return name
}
}
you can write a extension for Viewcontroller and try to call the method like this. if you write the extension for viewcontroller you can directly call them with its reference
self.getName(name:"Raghav")

Related

How to notify a change from a class to multiple ViewControllers?

I'm writing because I'd like to know what's the best method to notify a change from a class to multiple ViewControllers. At the moment I'm using delegate method but I'm sure It's not the best one for this purpose. I created a class where I receive data and after a bit of processing I need to send the processed message to some ViewControllers (any one shows a piece of that message.). At the moment I have a singleton for the class and I assign its delegate to a different ViewController when I load it through a menu. What's your suggestion to do this job?
Here is an example of my actual code:
import Foundation
protocol MyClassDelegate {
func receivedData(_ sender: MyClass)
}
class MyClass: NSObject {
// create the var for delegate
var delegate: MyClassDelegate?
// save the single instance
static private var instance: MyClass {
return sharedInstance
}
private let sharedInstance = MyClass()
static func getInstance() -> MyClass {
return instance
}
func processData() {
// at the end of the process
delegate?.receivedData(self)
}
}
class Menu: UIViewController {
private var containerView: UIView!
private let myClass = Myclass.getInstance()
private var vcOne = VcOne()
private var vcTwo = VcTwo()
override func viewDidLoad() {
super.viewDidLoad()
containerView = UIVIew()
// set containerView position and dimensions
}
func selectViewController(previous: UIViewController, next: UIViewController) {
// remove actual loaded ViewController
previous.willMove(toParent: nil)
previous.view.removeFromSuperView()
previous.removeFromParent()
// assign the delegate
myClass.delegate = next
// add the new ViewController
self.addChild(next)
slef.addSubView(next.view)
next.didMove(toParent: self)
}
}
class VcOne: UIViewController, MyClassDelegate {
func receivedData(_ sender: MyClass) {
// data received
}
}
class VcTwo: UIViewController, MyClassDelegate {
func receivedData(_ sender: MyClass) {
// data received
}
}
You can use NotificationCenter https://developer.apple.com/documentation/foundation/notificationcenter to broadcast a message. or use KVO. Generally I consider notification center much easier.
simple example:
https://www.hackingwithswift.com/example-code/system/how-to-post-messages-using-notificationcenter

How to set Struct Variable data from one view controller And Getting same value from Another view controller

Im new in swift. I have create a struct class with one variable. Im trying to set value of that struct variable from my first view controller which is working perfectly but when im trying to get that same value from second view controller its give me nil value.
below is my some codding
//Struct class
import Foundation
import UIKit
struct CompanyData {
var companyName : String?
mutating func setData(name : String)
{
companyName = name
}
}
// FIRST VIEW CONTROLLER
import UIKit
class SelfCompanyNameView: UIViewController {
#IBOutlet weak var companyNameTxt: TextfieldDesign!
var company = CompanyData()
var comName = ""
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func compnyBtnPress(_ sender: Any)
{
if companyNameTxt.text?.count == 0
{
Alert.showAlert(on: self, with: "Required", message: "Please enter your company name")
}
else
{
comName = companyNameTxt.text!
company.setData(name: comName)
print("\(comName)===\(company.companyName!)")
let vc = storyboard?.instantiateViewController(withIdentifier: "SelfAddressView") as! SelfAddressView
navigationController?.pushViewController(vc, animated: true)
}
}
}
//SECOND VIEW CONTROLLER
import UIKit
class SelfAddressView: UIViewController {
var company = CompanyData()
override func viewDidLoad() {
super.viewDidLoad()
print(company.companyName)
}
}
You need to pass your model to secondView Controller like following code
// FIRST VIEW CONTROLLER
let vc = storyboard?.instantiateViewController(withIdentifier: "SelfAddressView") as! SelfAddressView
vc.company = company
navigationController?.pushViewController(vc, animated: true)
//SECOND VIEW CONTROLLER
import UIKit
class SelfAddressView: UIViewController {
var company: CompanyData!
override func viewDidLoad() {
super.viewDidLoad()
print(company.companyName)
}
}
You need to declare a static variable as a shared instance of your struct. And use that while setting and getting your value.
E.G.
struct CompanyData {
static let shared = CompanyData()
var companyName : String?
mutating func setData(name : String)
{
companyName = name
}
}
While setting the value, use as:
company.shared.setData(name: comName)
And while getting the value, use as:
print(company.shared.companyName)
I would rather use Protocols and delegators. Here is a great tutorial will help you understand the concept Sean Allen Swift Delegate Protocol Pattern Tutorial - iOS Communication Patterns Part 1

passing data between view controllers without changing views

I want to pass data between two view controllers, but don't want the view to change when the users presses my save data button.
The users needs to fill in multiple data fields, and when finish can press another button to go to the second view controller.
I found many tutorials how to pass data using segue, but they all change view as soon as the 'save button is pressed'.
Any one can explain to me how to alter the code?
#Phillip Mills: here is how I used your code. (what am I doing wrong?)
code:
//////// declaring classes on FirstViewController (trying it first on only one ViewController)
class FakeVC1 {
func userInput() {
DataModel.shared.username = outbj14u.text
}
class FakeVC2 {
func viewAppears() {
if let name = DataModel.shared.username {
outbj14p.text = name
print("I have nothing to say")
}
}
}
class DataModel {
static let shared = DataModel()
var username: String?
}
////till here
//// here is where i call the functions
override func viewDidAppear(_ animated: Bool) {
FakeVC1().userInput()
FakeVC2().viewAppears()
if let xbj14p = UserDefaults.standard.object(forKey: "outbj14p") as? String
{
outbj14p.text = xbj14p
}
if let xbj14u = UserDefaults.standard.object(forKey: "outbj14u") as? String
{
outbj14u.text = xbj14u
}
////
#Phillip Mills: Below is what I have know. I think I got the code on the FirstViewController right, but the code on the Second View controller must be wrong. I don't get any errors, but the text field on the SecondViewController remains unchanged after putting input on in the FirstViewController
//// Code on the FirstViewController
class DataModel {
static let shared = DataModel()
var username: String?
}
#IBAction func savebj14p(_ sender: Any) {
outbj14p.text = inbj14p.text
DataModel.shared.username = outbj14p.text
UserDefaults.standard.set(inbj14p.text, forKey: "namebj14p")
}
//and on the SecondViewController
#IBOutlet weak var bj14u: UILabel! // connected to a label
//and
class DataModel {
static let shared = DataModel()
var username: String?
}
override func viewDidAppear(_ animated: Bool) {
if let name = DataModel.shared.username {
bj14u.text = name
}
}
In your case, don't pass data.
Create a shared object to act as your data model. When users fill in the fields, update the data model.
When the user moves to the second controller/view, that controller uses the data model object to show what it needs to.
class FakeVC1 {
func userInput() {
DataModel.shared.username = "Me"
}
}
class FakeVC2 {
func viewAppears() {
if let name = DataModel.shared.username {
print(name)
} else {
print("I have nothing to say")
}
}
}
class DataModel {
static let shared = DataModel()
var username: String?
}
FakeVC1().userInput()
FakeVC2().viewAppears()
If you need to pass value to another viewcontroller without changing the view , you can user NSNotificationCenter class
Refer this link for more details
NSNotificationCenter addObserver in Swift
what i will recommend is to use a global variable or array, you will have the info in all view controllers and you will be able to call it in your new view controller.

Swift 3 : Back to last ViewController with sending data [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 5 years ago.
I'm trying to go back to my las viewController with sending data, but it doesn't work.
When I just use popViewController, I can go back to the page, but I can't move my datas from B to A.
Here is my code :
func goToLastViewController() {
let vc = self.navigationController?.viewControllers[4] as! OnaylarimTableViewController
vc.onayCode.userId = taskInfo.userId
vc.onayCode.systemCode = taskInfo.systemCode
self.navigationController?.popToViewController(vc, animated: true)
}
To pass data from Child to parent Controller, you have to pass data using Delegate pattern.
Steps to implement delegation pattern, Suppose A is Parent viewController and B is Child viewController.
Create protocol, and create delegate variable in B
Extend protocol in A
pass reference to B of A when Push or Present viewcontroller
Define delegate Method in A, receive action.
After that, According to your condition you can call delegate method from B.
You should do it using delegate protocol
class MyClass: NSUserNotificationCenterDelegate
The implementation will be like following:
func userDidSomeAction() {
//implementation
}
And ofcourse you have to implement delegete in your parent class like
childView.delegate = self
Check this for more information
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
You have to send back to last ViewController with 2 options.
1. Unwind segue. (With use of storyboard)
You can refer this link.
2. Use of delegate/protocol.
You can refer this link.
Also this link will be useful for you.
You can use Coordinator Pattern
For example, I have 2 screens. The first displays information about the user, and from there, he goes to the screen for selecting his city. Information about the changed city should be displayed on the first screen.
final class CitiesViewController: UITableViewController {
// MARK: - Output -
var onCitySelected: ((City) -> Void)?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
onCitySelected?(cities[indexPath.row])
}
...
}
UserEditViewController:
final class UserEditViewController: UIViewController, UpdateableWithUser {
// MARK: - Input -
var user: User? { didSet { updateView() } }
#IBOutlet private weak var userLabel: UILabel?
private func updateView() {
userLabel?.text = "User: \(user?.name ?? ""), \n"
+ "City: \(user?.city?.name ?? "")"
}
}
And Coordinator:
protocol UpdateableWithUser: class {
var user: User? { get set }
}
final class UserEditCoordinator {
// MARK: - Properties
private var user: User { didSet { updateInterfaces() } }
private weak var navigationController: UINavigationController?
// MARK: - Init
init(user: User, navigationController: UINavigationController) {
self.user = user
self.navigationController = navigationController
}
func start() {
showUserEditScreen()
}
// MARK: - Private implementation
private func showUserEditScreen() {
let controller = UIStoryboard.makeUserEditController()
controller.user = user
controller.onSelectCity = { [weak self] in
self?.showCitiesScreen()
}
navigationController?.pushViewController(controller, animated: false)
}
private func showCitiesScreen() {
let controller = UIStoryboard.makeCitiesController()
controller.onCitySelected = { [weak self] city in
self?.user.city = city
_ = self?.navigationController?.popViewController(animated: true)
}
navigationController?.pushViewController(controller, animated: true)
}
private func updateInterfaces() {
navigationController?.viewControllers.forEach {
($0 as? UpdateableWithUser)?.user = user
}
}
}
Then we just need to start coordinator:
coordinator = UserEditCoordinator(user: user, navigationController: navigationController)
coordinator.start()

Implementing protocol to pass data back to a view controller from a pushed view controller

I am new to iOS development and have been stuck on this problem for a couple of hours. I thought I did it correctly in setting up the structure. Can someone please check it? When I call self.delegate!.updateData(daysSet) in the view controller that should send data back, it gives me the error below
expression resolves to an unused function
Here is what I've tried:
The view controller that should send data back:
import Foundation
import UIKit
class RepeatDailyValueViewController: UITableViewController {
var delegate: RepeatDailyValueViewControllerDelegate? = nil
var daysSet : String = "bam"
override func viewDidLoad() {
super.viewDidLoad()
self.delegate!.updateData(daysSet)
}
}
The view controller that is supposed to receive the data
import Foundation
import UIKit
protocol RepeatDailyValueViewControllerDelegate {
func updateData(daysSet: String)()
}
class NewAlarmViewController: UITableViewController, RepeatDailyValueViewControllerDelegate {
var daysSet : String = "bam"
#IBOutlet var labelDay: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc = segue.destinationViewController as! RepeatDailyValueViewController
vc.delegate = self
}
func updateData(daysSet: String)() {
self.labelDay.text = daysSet
}
}
Your problem is that in your protocol you've declared that updateData as a function of type String -> (Void -> Void) (a function that takes a String and returns another function that takes no arguments and returns nothing) instead of what I think you were going for, a function of type String -> Void (a function that takes a String and returns nothing). So, when you give updateData a string, you are getting back a function that takes no argument. That's the unused function the error is describing.
This is called a curried function and can be very useful in other situations.
To fix this, you just need to delete the extra set of parentheses in your protocol declaration:
protocol RepeatDailyValueViewControllerDelegate {
// Delete the parentheses after (daysSet: String)
func updateData(daysSet: String)
}

Resources