How to set a delegate in Swift - ios

I want to send my UserModel with all user informations from a ViewController (ShowUserViewController) to another ViewController (ChatViewController) with a delegate but its not working.
In my ShowUserViewControllers user are all informations I want to send to the ChatViewController.
var user: UserModel?
In my ChatViewController I have the following declaration where I want to send my datas:
var currentUser: UserModel?
Here my protocol:
protocol UserInfoToChatID {
func observeUserID(user: UserModel)
}
Here I prepare the segue and set delegate by tapping the button:
} else if segue.identifier == "UserInfoToChatVC" {
let chatVC = segue.destination as! ChatViewController
chatVC.currentUser = self.user
}
}
var delegate: UserInfoToChatID?
#IBAction func chatButtonTapped(_ sender: UIBarButtonItem) {
delegate?.observeUserID(user: user!)
}
At last I call the delegate in my ChatViewController:
extension ChatViewController: UserInfoToChatID {
func observeUserID(user: UserModel) {
self.currentUser = user
performSegue(withIdentifier: "UserInfoToChatVC", sender: self)
}
}

If you need to pass data from one ViewController to another, you don't have to use delegates for this. You can just pass this data as sender parameter of performSegue method:
performSegue(withIdentifier: "UserInfoToChatVC", sender: user!)
then in prepare for segue just downcast sender as UserModel and assign destination's currentUser variable
...
} else if segue.identifier == "UserInfoToChatVC" {
let chatVC = segue.destination as! ChatViewController
chatVC.currentUser = sender as! UserModel
}
}
But in your case you actually don't have to pass user as sender. You can just assign destination's currentUser variable as ShowUserViewController's global variable user
...
} else if segue.identifier == "UserInfoToChatVC" {
let chatVC = segue.destination as! ChatViewController
chatVC.currentUser = user!
}
}

2 things:
first, if you just want to pass data from one viewController to other viewController you don't need to use delegate pattern, just pass the object to the next viewController on prepare form segue.
second, if you want to implement the delegate pattern you should have one viewController than call to the delegate and the other implement the functions.
example:
protocol ExampleDelegate: class {
func delegateFunction()
}
class A {
//have delegate var
weak var delegate: ExampleDelegate?
// someWhere in the code when needed call to the delegate function...
delegate?.delegateFunction()
}
Class B: ExampleDelegate {
func delegateFunction() {
// do some code....
}
//when you move to the next viewControoler(to A in that case)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AClass" {
if let vc = segue.destination as? A {
vc.delegate = self
}
}
}

To pass the UserModel object forward, from ShowUserViewController to ChatViewController, you should use something called Dependency Injection:
So you'll do something like this inside ShowUserViewController:
#IBAction func chatButtonTapped(_ sender: UIBarButtonItem) {
performSegue(withIdentifier: "UserInfoToChatVC", sender: nil)
}
Note: The sender parameter should be the object that initiated the segue. It could be self, i.e. the ShowUserViewController object, but I'd advise against passing the UserModel object, because that object did not initiate the segue, and has nothing to do with navigation at all. It should be injected inside the Destination Controller later on.
In the same file, override the prepare(for:) method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "UserInfoToChatVC" {
let chatVC = segue.destination as! ChatViewController
chatVC.currentUser = self.user
}
}
I believe you've mostly done this part right, but you may need to communicate back from ChatViewController to ShowUserViewController.
In that case, you can and should use Delegation.
Create something like this inside ShowUserViewController:
protocol ChatViewControllerDelegate: class {
func didUpdateUser(_ model: UserModel)
}
class ChatViewController: UIViewControler {
var user: UserModel?
weak var delegate: ChatViewControllerDelegate?
/* more code */
func someEventHappened() {
delegate?.didUpdateUser(self.user!)
}
}
Finally, there is an additional line to be added to the prepare(for:) method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "UserInfoToChatVC" {
let chatVC = segue.destination as! ChatViewController
chatVC.currentUser = self.user
// Add this line...
chatVC.delegate = self
}
}
And specify that the ShowUserViewController implements the ChatViewControllerDelegate protocol, then override the didUpdateUser(_:) method:
func didUpdateUser(_ model: UserModel) {
// Some code here
}

Related

Passing data to detailVC in MVP architecture

I have two view controllers. I want to pass data from mainVC to detailVC during preparing segue. What's better option: calling and pass data to secondVCpresenter (which update View) or directly pass data to secondVC?
class MainVC: UIViewController {
var dataToSend = [Data]
.
.
.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destVC = segue.destination as? DetailVC {
destVC.detailPresenter.setData(data: dataToSend)
}
class DetailVC: UIViewController {
lazy var detailPresenter = DetailPresenter(detailDelegate: self)
var newData = [Data]
extension DetailVC: DetailDelegate {
func setData(data: [Data]) {
newData = data
}
}
protocol DetailDelegate: class {
func setData(data: [Data])
}
class DetailPresenter {
weak var detailDelegate DetailDelegate?
init(detailDelegate: DetailDelegate) {
self.detailDelegate = detailDelegate
}
func setData(data: [Data]) {
detailDelegate?.setData(data: data)
}
}
I wonder if it is ok to call detailPresenter from prepare segue in MainVC and if it is not too dirty way to send data?
I agree with Kudos, in this case there's no need for a delegate to pass data to DetailVC as it's already referenced. Using a delegate for this has made it unnecessarily complicated. So yes calling detailPresenter is quite a dirty way of doing things.

Can not pass Data To ContainerView

I have tried 2 methods to pass the data from ViewController to ContainerView, with and without segue
Here is without segue method
ViewController
class DetailPostBookReviewVC: UIViewController {
var postid: String!
#IBAction func Menubutton(_ sender: Any) {
print(postid!)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MenuBookReviewVC") as! MenuBookReviewVC
vc.favpostid = postid
}
ContainerView
class MenuBookReviewVC: UIViewController {
var favpostid = String()
#IBAction func Deletepost(_ sender: Any) {
print(favpostid)
}
}
result: favposid has Nill Value
UPDATE this is with segue method
class DetailPostBookReviewVC: UIViewController {
var postid: String!
#IBAction func Menubutton(_ sender: Any) {
print(postid!)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "toMenuBookReviewVC") { //"toMenuBookReviewVC" is identifier
let vc = segue.destination as! MenuBookReviewVC
vc.favpostid = postid!
}
}
Pass your data like. User prepare(for:sender:)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if
segue.identifier == "MyIdentifierInStorybiard", // Set that
let controller = segue.destination as? MenuBookReviewVC {
controller.favpostid = postid
}
}
I think you postid is not String type so print the null value
In this way, you can't pass data for the container view. if in this way without presenting controller and push controller you can use the global variable then direct pass data and use any controller you want to use.
Example
import UIKit
class ViewController: UIViewController {
var postid: String!
override func viewDidLoad() {
super.viewDidLoad()
postid = "23" // You change your post ID
}
#IBAction func Menubutton(_ sender: Any) {
favpostid = postid
}
}
var favpostid : String!
class MenuBookReviewVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(favpostid)
}
}
For Passing Data To Container View you can use this
UserDefaults.standard.set(value, forKey: "SomeKey")
after your data is used you can clear that default value.
UserDefaults.standard.set("", forKey: "SomeKey")

How to access variables when preparing for segue?

I'm preparing for a Segue (just learning at the moment!) and want to pass an array, which is created in a function, through a segue to arrive at the new view controller.
It's working fine if I just put a text string in there, but when I try change to an array it is blank - I think because it can't access the array because it's outside the scope - but I'm stuck on how to do it
Here's the initial VC code :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = (testArray)
}
}
//Get the new view controller using segue.destinationViewController.
//Pass the selected object to the new view controller.
}
func testfunction() {
let testArray = ["blah","blah","ploop"]
}
and the 'receiving VC code'
class WorkoutViewController: UIViewController {
var workoutName = [String]()
override func viewDidLoad() {
super.viewDidLoad()
print(workoutName)
// Do any additional setup after loading the view.
}
I'm nearly there with it but think I must be missing something basic. How do you do this passing arrays/other variables created in functions?
If the function had more than 1 variable/array, would that change the approach? I.e. one function might produce the exercises in the workout AND the number of reps for example
You can send it in sender
self.performSegue(withIdentifier: "goToWorkout ", sender: testArray)
and in
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = sender as! [String]
}
}
Yes, both occurrences of testArray are not in the same scope and the compiler complains.
Declare the function this way
func testfunction() -> [String] {
let testArray = ["blah","blah","ploop"]
return testArray
}
and assign the array by calling the function
destVC.workoutName = testfunction()
Your issue is caused by testArray being a local variable defined inside the testfunction function making it only accessible from inside the function. If you want to make a variable accessible from everywhere inside the class, you'll have to make it an instance property.
class InitialVC: UIViewController {
let testArray = ["blah","blah","ploop"]
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = testArray
}
}
//Get the new view controller using segue.destinationViewController.
//Pass the selected object to the new view controller.
}
}
When saying:
destVC.workoutName = (testArray)
in the prepare(for segue: UIStoryboardSegue, sender: Any?) method, it definitely doesn't refers to testArray variable in testfunction(), since it is a local variable. If you are not getting a compile time error at the above line, then probably your declaring testArray somewhere in the view controller or maybe globally in the app.
So what you could do is to declare testfunction() as testfunction() -> [String]:
func testfunction() -> [String] {
return["blah","blah","ploop"]
}
thus:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = testfunction()
}
}
}
func testfunction() -> [String] {
return ["blah","blah","ploop"]
}
I guess this is what you want. Functions / methods can return values. It can be achieved by the -> syntax.
Now you can use like:
destVC.workoutName = testfunction()
Few notes
Functions, methods and variables should be named through camel case notation. So, instead of testfunction you should write testFunction (maybe choose a better name also).
Do not forget to read Apple documentation on the subject: Functions.

Can't send data between to view controllers Swift

I have got a tabbed project in my Xcode.But my Entry Point is a separate view controller that is not connected to tabs.So when user clicks Login button i send value of input to one of view controllers in tabbed part of my project.I have a segue between separated VC and VC that I send data to.Here is my code In part where I send the data
protocol SendDel {
func userDidEnterData(data: String)
}
LogInViewController: UIViewController {
var delegate:SendDel!=nil
#IBAction func SendB(_ sender: Any) {
if(delegate != nil){
if(self.Usn != nil){
let data = self.Usn.text
self.delegate?.userDidEnterData(data: data!)
}
}
}
}
And here is code in part where I receive data
class FirstViewController: UIViewController,SendDel {
func userDidEnterData(data: String) {
UsernameLabel.text="\(data)"
dismiss(animated: false, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "First"){
let sendingVc=segue.destination as! LogInViewController
sendingVc.delegate = self
}
}
}
But unfortunately it is not working.
Actually you setting the delegate property from FirstViewController and then when you present the LogInViewController you have write the code var delegate:SendDel!=nil which makes delegate nil every time you tapped the button.
So try below code :
var delegate: SendDel?
Hope it works for you.

How can I call a method that is inside a UIViewController embedded in a container from a parent UIViewController?

I have an ios app in swift and I have a UIViewController (let's call it parentController) with a container. This container embeds another UIViewController called embedController.
embedController contains a method that prints a message to a console.
How can I call this method from my parentController?
I tried to use protocols, my current code is as follows:
class ParentController: UIViewController {
var handleEmbedController:HandleEmbedController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "embedViewSegue"){
if let embed = segue.destinationViewController as? EmbedController {
embed.value1 = value1
}
}
#IBAction func sendMsgButtonAction(sender: AnyObject) {
handleEmbedController?.printMsg() //this so far does nothing
}
}
and my embedController:
protocol HandleEmbedController: class {
func printMsg()
}
class EmbedController: UITableViewController, HandleEmbedController{
var value1 = ""
func printMsg(){
print("printing some embedded message")
}
}
How can I print this message from a parent controller?
What are you doing in your prepare for segue? Aren't you supposed to set your delegate (protocol) there? Like this:
if (segue.identifier == "embedViewSegue"){
if let embed = segue.destinationViewController as? EmbedController {
self.handleEmbedController = embed
}
}
If you put a breakpoint in sendMsgButtonAction you should see that the property handleEmbedController is nil. And that's why the method call does nothing since you are safely unwrapping it with ?.

Resources