Pass data from a VC without Segue to a non VC Class - ios

I passed data from ViewController1 to ViewController2 via segue, but how can I send data to the Class? This class is not a ViewController.
ViewController1 has a UIPickerView that gives the data (String).
The String will complete an URL needed in ViewController2.
Class
class A: SendDataFromDelegate {
func sendData(data: String) {
self.data = data
}
var delegate : SendDataFromDelegate?
ViewController1
#IBAction func Picker(_ sender: Any) {
var delegate: SendDataFromDelegate?
delegate?.sendData(data: data)
}
protocol SendDataFromDelegate {
func sendData(data : String)
}
Is this a good way to do it?
Or should I create all the possible URLs in the class, and call them from ViewController2?

You should create a protocol with delegate functions like this:
protocol ClassDelegate: class {
func doSomething()
}
In your class A you should implement that protocol this way:
class A: ClassDelegate {
inAFunction(){
ViewController1.delegate = self
}
func sendData(data: String) {
self.data = data
}
}
In the viewController you want to send data to your Class A you should have reference of your class A and use the delegate variable:
class ViewController1: UIViewController {
weak var delegate: ClassDelegate?
func clickHere(){
delegate.sendData()
}
}
When you use clickHere function it triggers Class C sendData function.
Try it ;D

Related

Why protocol type never gets a value?

I am trying to understand more protocols. I could not figure out myProtocolFunction never call and print or passing the variable as below.
I think my problem is about initialization. When I try with two viewcontroller everything is okay but MyStruct protocol instance is nil. Thanks for your help.
class ViewController: UIViewController, MyProtocol {
var myProtocolValue: String = ""
var globalInstance = "a"
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func click(_ sender: Any) {
var myStruct = MyStruct()
myStruct.myProtocol = self
}
func myProtocolFunction(passing: String) {
globalInstance = passing
print("never print")
}
}
protocol MyProtocol {
func myProtocolFunction(passing: String)
}
struct MyStruct {
var myProtocol: MyProtocol?
init() {
makeSomething()
}
func makeSomething() {
myProtocol?.myProtocolFunction(passing: "abc")
}
}
1- You need to make
var myStruct = MyStruct() // an instance variable
#IBAction func click(_ sender: Any) {
myStruct.myProtocol = self
}
2- Also you call init before even you set myProtocol as its called when you do var myStruct = MyStruct() and at that time myProtocol is nil as its not yet set
init() {
makeSomething()
}
Check full working edit
class ViewController: UIViewController, MyProtocol {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func myProtocolFunction(passing: String) {
print(passing)
}
#IBAction func click(_ sender: Any) {
var myStruct = MyStruct(self)
}
}
protocol MyProtocol {
func myProtocolFunction(passing: String)
}
struct MyStruct {
var myProtocol: MyProtocol?
init(_ mine:MyProtocol) {
myProtocol = mine
makeSomething()
}
func makeSomething() {
myProtocol?.myProtocolFunction(passing: "abc")
}
}
Think about protocols as a type, in your struct you are basically saying
that this struct have a variable of this type ( your protocol )
and on init you are trying to call its function.
What you're missing is setting an instance of type ( your protocol )
so it can get called.
for instance :
MyStruct = MyStruct(myProtocol: ViewController())
Now your nullable protocol have a value and can call it's function in another word, protocols can't have implementation directly into them they're more like a signature contract
Read more about protocols here.
You need to create MyStruct() object out of the click action else when the scope is over your object is deinitialize.
Also, You have to call .makeSomething() function on the button click as the time of object creation delegate is nil.
var myStruct = MyStruct() // Create an instance here
#IBAction func click(_ sender: Any) {
myStruct.myProtocol = self
// Add your action
myStruct.makeSomething()
}

Passing data to various view controllers via delegate

Struggling to learn the basics of passing data via delegates. I am trying to pass a string from my viewController to my viewController2 and print it. I am getting the error:
"Type ViewController2 has no member delagate" in my view controller 2.
I cannot figure out where I have gone wrong.
viewController 1:
protocol datadelagate {
func printThisString(string: String)
}
class ViewController: UIViewController {
var delegate: datadelagate?
override func viewDidLoad() {
delegate?.printThisString(string: "This was passed from first controller to second controller")
}
}
This is my viewController 2:
class ViewController2: UIViewController, datadelagate {
func printThisString(string: String) {
print(string)
}
override func viewDidLoad() {
super.viewDidLoad()
ViewController2.delagate = self
print(String.self)
}
}
If you want ViewController2 to print some value from ViewController, you might have to do it this way:
protocol datadelagate {
func printThisString(string: String)
func getStringFromVC1() -> String
}
class ViewController: UIViewController, datadelagate {
let someString: String = "From VC1"
func printThisString(string: String) {
print(string)
}
func getStringFromVC1() -> String {
return someString
}
override func viewDidLoad() {
ViewController2.delagate = self
}
}
class ViewController2: UIViewController {
var delegate: datadelagate?
override func viewDidLoad() {
super.viewDidLoad()
//This is how something from VC2 is sent to VC1's scope.
delegate?.printThisString(string: "Calling the delegate to print something from ViewController2 on first ViewController")
//The below call gets you some value from VC1. (This is what you wanted, I belive...)
print(delegate?.getStringFromVC1())
}
}
Now for some explanation:
For simple understanding, assume a delegate as a person who does some specific job (protocol).
You have a `delegate'
You ask your delegate to work with your friend, and your friend acknowledges. (assigns your delegate by You.delegate = self, where self is your friend)
Now, through your delegate, you can do something with your friend, by asking your delegate to do some job (defined in protocol).
EDIT
The code above won't work, as non-static data members are trying to be accessed without creating an instance
Working code
import UIKit
class ViewController2: UIViewController {
static let sharedInstance = ViewController2()
weak var delegate: DataDelagate?
override func viewDidLoad() {
super.viewDidLoad()
//This is how something from VC2 is sent to VC1's scope.
delegate?.printThis(string: "Calling the delegate to print something from ViewController2 on first ViewController")
//The below call gets you some value from VC1. (This is what you wanted, I belive...)
print(delegate?.getStringFromVC1() ?? "s")
}
}
class ViewController: UIViewController {
static let sharedInstance = ViewController2()
var someString: String = "From VC1"
override func viewDidLoad() {
super.viewDidLoad()
ViewController2.sharedInstance.delegate = self
}
}
extension ViewController: DataDelagate {
func printThis(string: String) {
print(string)
}
func getStringFromVC1() -> String {
return someString
}
}
protocol DataDelagate: AnyObject {
func printThis(string: String)
func getStringFromVC1() -> String
}

Delegate is nil in a Swift program

I'm practising how to communicate between two view controllers using protocol and delegate (in the xCode background even when I'm using protocol in my project I get the same problem, Delegate is nil), but the problem after setting everything it shows me that my delegate is nil and the sender VC does not send any data since the delegate is nil.
I have confirmed to the protocol and I have set the receiver VC as the delegate, but still can not see where the problem is.
The Protocol
protocol theCommunicationsStructionProtocol{
func dataToTransmit(Data: String)
}
The Sender VC
class TheSenderVC{
var delegate: theCommunicationsStructionProtocol?
func lookingForDelegate(){
self.delegate?.dataToTransmit(Data: "Data has been sent")
}
}
The Receiver VC
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj = TheSenderVC()
func delegateLuncher(){
TheSenderVCObj.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
calling delegateLuncher() to set the delegate in the receiver VC
TheSenderVC().lookingForDelegate()
calling lookingForDelegate() from the sender VC to look for the
delegate and send it the data
TheReceiverVc1().delegateLuncher()
Note: I have tried accessing the delegate from the receiver VC using this way:
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj: TheSenderVC?
func delegateLuncher(){
self.TheSenderVCObj?.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
but I still getting the same problem:
delegate is nil
Finally, I found the solution!
the problem is I was making instances of the TheSenderVC, instead of takling to the original TheSenderVC.
when I was making an object (an instance) of TheSenderVC the problem occurred! instead I have to access the original TheSenderVC, which means I have to use static :)
here is the old delegate set up
var delegate: theCommunicationsStructionProtocol?
from TheSenderVC
here is the new delegate set up
static var delegate: theCommunicationsStructionProtocol?
therefore the
func lookingForDelegate(){
self.delegate?.dataToTransmit(Data: "Data has been sent")
}
will be changed to
static func lookingForDelegate(){
self.delegate?.dataToTransmit(Data: "Data has been sent")
}
since now it includes a static property (delegate)
on the other hand, the The ReceiverVC1 should be changed from:
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj = TheSenderVC()
func delegateLuncher(){
TheSenderVCObj.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
to:
class TheReceiverVc1: theCommunicationsStructionProtocol{
func delegateLuncher(){
TheSenderVC.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
accessing the delegate from the original TheSenderVC()
Where are you create the reference of TheSenderVCObj
replace var TheSenderVCObj: TheSenderVC? to var TheSenderVCObj = TheSenderVC()
let try below code:
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj = TheSenderVC()
func delegateLuncher(){
self.TheSenderVCObj?.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
your TheSenderVCObj also nil according to your code.
NOTE: use proper naming conventions.
Because the TheReceiverVc1 was automatic deinit by ARC.
You need to save reference of the instance like that's:
class ViewController: UIViewController {
let theReceiverVc1: TheReceiverVc1 = TheReceiverVc1()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
theReceiverVc1.delegateLuncher()
}
}
Also make sure when you using delegate set it as weak var:
weak var delegate: theCommunicationsStructionProtocol?

Using Selector in Swift 3

I am writing my iOS Application in Swift 3.
I have a UIViewController extension, where I have to check if the controller instance responds to a method. Below is the code that I a trying out.
extension UIViewController {
func myMethod() {
if self.responds(to: #selector(someMethod)) {
}
}}
Here the responds(to:) method throws a compile time error
Use of unresolved identifier "someMethod".
I read in another post, we have to use self inside the selector argument, but even that is throwing some error.
A simple workaround:
#objc protocol SomeMethodType {
func someMethod()
}
extension UIViewController {
func myMethod() {
if self.responds(to: #selector(SomeMethodType.someMethod)) {
//...
self.perform(#selector(SomeMethodType.someMethod))
// or
(self as AnyObject).someMethod?()
//...
}
}
}
A little more Swifty way:
protocol SomeMethodType {
func someMethod()
}
//For all classes implementing `someMethod()`.
extension MyViewController: SomeMethodType {}
//...
extension UIViewController {
func myMethod() {
if let someMethodSelf = self as? SomeMethodType {
//...
someMethodSelf.someMethod()
//...
}
}
}
Create a protocol which requires someMethod()
protocol Respondable {
func someMethod()
}
And a protocol extension which affects only UIViewController instances
extension Respondable where Self : UIViewController {
func myMethod() {
someMethod()
}
}
Adopt the protocol to some of the view controllers
class VC1 : UIViewController, Respondable {
func someMethod() { print("Hello") }
}
class VC2 : UIViewController {}
class VC3 : UIViewController {}
Now call the method in the extension
let vc1 = VC1()
vc1.myMethod() // "Hello"
Otherwise you get a compiler error:
let vc3 = VC3()
vc3.myMethod() // error: value of type 'VC3' has no member 'myMethod'
Swift 4 answer:
If the selector is written as a string you won't get that error.
extension UIViewController {
func myMethod() {
if self.responds(to: "someMethod")) {
}
}
}
And then in the viewcontroller (dont forget the #objc):
#objc func someMethod() -> Void {}

How to pass data between UIViewControllers with protocols/delegates

In the code below I have a ViewController("SenderViewController"), which passes a message to the main ViewController when a button is tapped. What I don't fully understand is how does messageData() method in the main ViewController know when to listen for the message.
Can someone please explain me what is triggering the messageData() method in the main ViewController?
SenderViewController:
import UIKit
protocol SenderViewControllerDelegate {
func messageData(data: AnyObject)
}
class SenderViewController: UIViewController {
#IBOutlet weak var inputMessage: UITextField!
var delegate: SenderViewControllerDelegate?
#IBAction func sendData(sender: AnyObject) {
/
if inputMessage.text != ""{
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
self.delegate?.messageData(inputMessage.text!)
}
}
}
Main ViewController:
import UIKit
class ViewController: UIViewController, SenderViewControllerDelegate{
#IBOutlet weak var showData: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func goToView(sender: AnyObject) {
let pvc = storyboard?.instantiateViewControllerWithIdentifier("senderViewController") as! SenderViewController
pvc.delegate = self
self.presentViewController(pvc, animated:true, completion:nil)
}
// What triggers this method, how it know when to listen?
func messageData(data: AnyObject) {
self.showData.text = "\(data)"
}
}
Thanks a lot!
Objects don't exactly listen for method calls. They sit there, waiting to invoked.
The line
self.delegate?.messageData(inputMessage.text!)
From your SenderViewController is a function call. (The term method and function are pretty much interchangeable, although the method is usually used for the functions of objects.) It invokes the function messageData in ViewController.
While Presenting SenderViewController from MainViewController you are setting the delegate as self. So whenever you call the delegate method in SenderViewController
self.delegate?.messageData(inputMessage.text!)
following method of MainViewController will act as a callback
func messageData(data: AnyObject) {
self.showData.text = "\(data)"
}
In SenderViewController:
When you tap button you invoke sendData method. In this method you ask delegate to invoke its messageData method. Delegate property declared as SenderViewControllerDelegate type, so you can do that (see this protocol defenition).
In ViewController (first view controller):
Before you open second view controller, in method goToView you seting up property delegate of SenderViewController to 'myself', to exact instance of ViewController, since you declared that it confirm protocol SenderViewControllerDelegate by implementing method messageData. So, ViewController is now saved as delegate property in SenderViewController, and can be used to invoke messageData!
self.delegate?.messageData(inputMessage.text!)
#IBAction func sendData(sender: AnyObject) {
if inputMessage.text != ""{
self.delegate?.messageData(inputMessage.text!)
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}else{
//handle here
}
Note: If you need to pass multiple data to mainViewController then use dictionary to pass them. i.e.
SenderViewController:
import UIKit
protocol SenderViewControllerDelegate {
func messageData(data: [String : Any])
}
class SenderViewController: UIViewController {
#IBOutlet weak var inputMessage: UITextField!
var delegate: SenderViewControllerDelegate?
#IBAction func sendData(sender: AnyObject) {
let myDict = [ "name": "Name", "age": 21, "email": "test#gmail.com"] as! [String : Any]
self.delegate?.messageData(myDict)
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
Main ViewController
import UIKit
class ViewController: UIViewController, SenderViewControllerDelegate{
#IBOutlet weak var showData: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func goToView(sender: AnyObject) {
let pvc = storyboard?.instantiateViewControllerWithIdentifier("senderViewController") as! SenderViewController
pvc.delegate = self
self.presentViewController(pvc, animated:true, completion:nil)
}
// What triggers this method, how it know when to listen?
func messageData(data: [String : Any]) {
print(data["name"])
print(data["age"])
print(data["email"])
}
}

Resources