coding convention for UITableViewController - ios

My question is very simple, I just want to understand why some people using extension for those delegate methods of UITableViewController? Why not directly code inside the controller? What I mean is, I saw people doing this:
class MyTableViewController: UITableViewController {
// bla bla
}
extension MyTableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
...
}
What is the benefit of having the delegate methods implementation in extension? Why not just put the code inside MyTableViewController without extension? If it is a convention, then, why? What is good to do so & what is bad not doing it?

Today Maintainable of our code is more important than Performance
With this in mind, more programmers search to accomplish this task with extravagant stratagems. Using extension is one of them.
With an extensions you could make your code more readable.
But this trick is really helpful?
Single responsibility principle affirm that an object should be responsible of a single task.
In our ViewControllers we usually put so many things like TableView, IBAction, CollectionView and so on...
So, how can I follow the SRP without using the extension?
Just create a new object which will be delegate to manage, for example, the TableView.
class TableController: NSObject {
var dataSource: [Any]
init(dataSource: [Any]) {
super.init()
self.dataSource = dataSource
}
}
// here extension could help you to make your code more readable
extension TableViewController: UITableViewDataSource {
/* the TableViewDataSource method */
}
So, now, in your ViewController you can do this:
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
var tableController: TableController!
override func viewDidLoad() {
super.viewDidLoad()
self.tableController = TableController.init(dataSource: [1,2,3])
self.tableView.dataSource = self.tableController
}
}
Hope this help you ;)

UIViewController generally contains many functions like IBOutlets, Private helper functions, IBActions and ViewController LifeCycle methods etc. If you put all your code in ViewController it becomes little difficult to read and hard to maintain as we add more functionality. Extensions is one of the ways to separate the logical functionality in separate files so that it's easy to read and maintain the code and modify when needed. Using extension to put all the functions which confirms to some protocol like UITableViewDataSource is one way.
One benefit of using extension is that you create multiple files and add the functions like in normal classes.
Disadvantage is you can't access private variables from the classes also can't declare constants in extensions.
If you prefer to keep the code in same file then using //MARK: is another way to group similar functionalities
//MARK: ViewController LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
}

Related

Custom Delegate & DataSource NSObjects best practice

So consider this case, i have a UIViewController that contains a simple UICollectionView, but the Delegate & DataSource protocols are separated NSObject's from the UIViewController.
It looks something like this
class MainCollctionViewDelegate: NSObject, UICollectionViewDelegate
class MainCollectionViewDataSrouce: NSObject, UICollectionViewDataSource
And i use them inside my UIViewController like this,
lazy var CVDelegate = MainCollctionViewDelegate()
lazy var CVDataSource = MainCollectionViewDataSrouce()
//MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
collectionView.registerCell(PlainCell.self) // register custom cell Nib into collection View.
collectionView.delegate = CVDelegate //Set Deleagte
collectionView.dataSource = CVDataSource // Set data Source
}
Is this approach going to cause any memory leaks in the future ? considering i will implement an injection to fill the data source of the CollectionView to be something like this in the future.
MainCollectionViewDataSrouce(with: Foo) // Foo is some data to populate the collection with.
Is there a better practice to this ? considering I'am trying to achieve
the minimum code writing (redundancy).
Note: this also applies for UITableViewDelegate & UITableViewDataSource
Is this approach going to cause any memory leaks in the future ?
Not right now.
Your memory graph will look like:
So here no memory cycles and no reasons to leak memory.
Important. If you add reference from DataSource / Delegate on your viewController, make sure it is weak reference, otherwise you will create memory cycle.
Note. You can add strong references from DataSource / Delegate on collectionView, since collectionView have weak references on dataSource and delegate. So no cycle as well
Side note
Better to register cells in data source, since "only" data source know what types of cell will be used.
Your question is rather vague, but in general, that is a very common practice. We use this pattern a lot in our company:
class MainCollectionViewController: UIViewController {
lazy var dataSource: UICollectionViewDataSource = self
lazy var delegate: UICollectionViewDelegate = self
static func with(dataSource: UICollectionViewDataSource, delegate: UICollectionViewDelegate) -> MainCollectionViewController {
let vc = MainCollectionViewController()
vc.dataSource = dataSource
vc.delegate = delegate
return vc
}
}
extension MainCollectionViewController: UICollectionViewDataSource {
// code
}
extension MainCollectionViewController: UICollectionViewDelegate {
// code
}
The two primary uses are for unit testing and for passing data to the view controller. The tester can inject custom data source and delegate at test time:
let testVC = MainCollectionViewController.with(dataSource: ..., delegate: ...)
// do test
Or passing data to it:
// In another view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destination as? MainCollectionViewController {
destinationVC.dataSource = ...
destinationVC.delegate = ...
}
}
As to memory leak, this pattern is generally safe but obviously someone will run into a memory problem once in a while. Your mileage may vary.

How to use delegates properly in Swift?

I read a lot about the delegates but in practice I cannot use it properly.
Description: I have A: UIViewController, B: UIView, C: UIViewController. I want to run segue from A: UIViewController to the C: UIViewController from the inside of B: UIView.
I've tried:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class B: UIView { ... }
where in my A: UIViewController:
override func viewDidLoad() {
B().delegate = self
}
func runSegue(identifier: String) {
self.performSegueWithIdentifier(identifier, sender: self)
}
and trying to call it via:
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = nil
a!.runSegue("goToMainPage")
}
but I'm sure that I do not use it properly. Can anyone help me with it? I do not want just an answer. Please describe me it concept shortly
Delegates are just a Design Pattern that you can use in a number of ways. You can look at the Apple Frameworks to see how and where to use delegates as examples. A table view delegate is probably the best known delegate in UIKit.
Delegates serve as a callback mechanism for code to communicate with an instance of an unknown class without knowing more than that that instance will respond to the methods of the delegate protocol.
An alternative to a delegate is to use a closure (what we used to call a block in Objective-C). When to use one vs. the other is a matter of taste. There are a couple of rules of thumb, like for instance outlined here.
What you are doing is, IMO, the proper way to use delegates. You separate the view functionality from the View Controller's functionalities via a delegate, and so the contract for your view is clear: the user needs to respond to the delegate method.
Your code works and is correct. I made a quick implementation here: https://github.com/kristofvanlandschoot/DelegateUsage/tree/master
The main difference from your example, and maybe that's the place where you made a mistake is the third part of your code where you should write something like:
#IBAction func send(sender: AnyObject) {
delegate?.runSegue("segueAB")
}
There are multiple errors in your code, for example:
Here you are creating a new B, and setting A as a delegate of that new instance, no the one you actually want
override func viewDidLoad() {
«B()».delegate = self
}
And here you are creating force unwrapping a nil value
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = «nil»
«a!».runSegue("goToMainPage")
}
If what you want to do is tell A to perform a segue to C, from inside B, all you need to do is to call performSegueWithIdentifier on A
For example:
class B: UIView {
weak var referenceToA: UIViewController? = nil // set this somewhere
#IBAction func send(sender: AnyObject) {
guard let a = referenceToA else {
fatalError("you didn't set the reference to a view controller of class A")
}
a.performSegueWithIdentifier("goToMainPage", sender: self)
}
}

Exact reason of Why we need Delegates /

Delegates an important concept in ObjC/SWIFT or any other coding language. I know that delegates are used to pass messages from one class to another class especially when we want to pass message back to a view controller from where we have just moved to some other view controller.
I was searching for more technical answer and searched a lot about this, and here is what I got what I feel might be the exact answer -
By the rules of MVC, we need a method to return a value. Where in a
called instance can we go back to the class calling it? With an
encapsulated class we can’t. There is no way to send that revised
model back to the original controller without breaking encapsulation
or MVC. The new view controller does not know anything about the class
that called it. We look stuck. If we try to make a reference directly
to the calling controller, we may cause a reference loop that will
kill our memory. Simply put, we can’t send things backwards.
But the explanation says something about
Where in a
called instance can we go back to the class calling it? With an
encapsulated class we can’t. There is no way to send that revised
model back to the original controller without breaking encapsulation
or MVC.
So exactly what does this para mean. Can any one please explain this in a more simple way taking the following code as reference -
VC2 -
import UIKit
protocol myDelegate : class
{
func sendItems(name:NSString)
}
class EnterViewController: UIViewController
{
weak var delegate: myDelegate?
#IBOutlet weak var nameTextfield: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendData(sender: AnyObject)
{
delegate?.sendItems(nameTextfield.text!)
self.navigationController?.popViewControllerAnimated(true)
}
VC2
import UIKit
class DisplayViewController: UIViewController,myDelegate
{
#IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func sendItems(name: NSString) {
self.nameLabel.text = name as String
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let destinationVC = segue.destinationViewController as! EnterViewController
if segue.identifier == "enterDetail"
{
destinationVC.delegate = self
}
}
}
Thanks.
In simple terms, Delegate is a representative of Class which works on behalf of class. If we compare in real world, delegates to foreign countries go to represent their government and have all controls and powers. Similarly, here delegates has all the control that an object of class will have and working on behalf of Class.
Now delegate in specific is an object assigned by class to notify the event. This can be acheived by NSNotification too. But the difference is Delegates can intercept the event but NSNotification cant.
Here in your code:
You have assigned DestinationVC's delegate to DisplayViewController.
Now DisplayviewController will notify all the event whatever you want to notify to Class DestinationVC and also intercept in between event.
In your case you are calling sendItems of DestinationVC.
I am sorry for my bad explanation but I guess you would have get the basic idea.

Passing data with delegates/protocols

I have a UICollectionView with a CollectionReusableView header. I want to pass a string from the collecitonview to the header, so that the header knows which data to load based on the string. I am trying to use delegates/protocols to do this, but keep getting "unexpectedly found nil while unwrapping an optional value." Here is my code:
protocol UserToQuery {
func thisUser(x: String)
}
class Profile: UICollectionViewController {
var ownProfile = true
var delegate:UserToQuery?
override func viewDidLoad() {
super.viewDidLoad()
if self.ownProfile == true {
let username = PFUser.currentUser()?.username
self.delegate!.thisUser(username!)
}
}
}
And here is the code for the Header view:
class ProfileHeader: UICollectionReusableView, UserToQuery {
var id1 = String()
var controller = Profile()
override func awakeFromNib() {
print(id1)
controller.delegate? = self
}
func thisUser(x: String) {
self.id1 = x
getProfileInfo()
}
func getUserData() {
// code here uses the id1 value to get data
}
}
My understanding of delegates/protocols is this: if you want to pass data (i.e., string), to another view, you make the view that receives the string conform to a protocol. This protocol includes a function that is used to pass the string, and when that function is called, it notifies the other view that the string is now available for use, and then you code what you want and use the string. Is that accurate?
In ProfileHeader, you have a variable, controller, which is creating a new instance of Profile, which is NOT the Profile view controller from your storyboard. This is why self.delegate! is nil in Profile.viewDidLoad().
I am going to make the assumption that ProfileHeader is a view in the Profile view controller. In your viewDidLoad, you should set the delegate to the ProfileHeader. See the example code below (I assume an outlet for the ProfileHeader view):
EDIT: ProfileHeader is not an outlet, as mentioned in the comments. Updated my answer to reflect that.
class Profile: UICollectionViewController {
var ownProfile = true
var delegate:UserToQuery?
override func viewDidLoad() {
super.viewDidLoad()
// Set the delegate!
self.delegate = ProfileHeader()
if self.ownProfile == true {
let username = PFUser.currentUser()?.username
// Delegate won't be nil now
self.delegate!.thisUser(username!)
}
}
}
}
As a general flow, the view controller should keep references to the view, not the other way around. So remove the controller property from your ProfileHeader view. The view shouldn't care what view controller is controlling it.
You have some misunderstandings about protocol/delegate, but it’s normal when you start iOS development.
First of all, why does the app crash :
The variable delegate is an optional UserQuery. It’s okay for a delegate to be optional, but it’s never set in your code, so when you call :
self.delegate!.thisUser(username!)
you try to force unwrapping a nil variable, which results in the crash.
Protocols
Now, let’s talk about the protocol/delegate relationship.
You have an UICollectionViewController subclass, which embeds an UICollectionView object. This UICollectionView will be contains a mix of header, footer and cell. Your ProfileHeader class will thus be displayed within your UICollectionView.
In order to populate an UICollectionView, you don’t need to create your own protocol : there are already two protocols for this :
UICollectionViewDataSource is the main protocol to conforms to, because it allows you to populate the collection view
UICollectionViewDelegate is used for further customization of your tableview, i.e. customizing the appearance and handling events.
Since your Profile class inherits from UICollectionViewControlleryou don’t have to named these protocols after your class name since UICollectionViewController already conforms to these protocols as written in Apple docs
You will have to override the delegate and protocol methods in order to display some data. My advice is, before using headers and footers, to use only UICollectionViewCell objects for start easily.
By overriding the method -collectionView:numberOfItemsInSection: and - collectionView:cellForItemAtIndexPath:, you will be able to populate the collection view.

Using A Delegate to Pass a var

I have been pulling my hair out trying to get this 'Delegate' thing to work in Swift for an App I am working on.
I have two files: CreateEvent.swift and ContactSelection.swift, where the former calls the latter.
CreateEvent's contents are:
class CreateEventViewController: UIViewController, ContactSelectionDelegate {
/...
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
override func viewDidLoad() {
super.viewDidLoad()
/...
contactSelection.delegate = self
}
func updateInvitedUsers() {
println("this finally worked")
}
func inviteButton(sender: AnyObject){
invitedLabel.text = "Invite"
invitedLabel.hidden = false
toContactSelection()
}
/...
func toContactSelection() {
let contactSelection = self.storyboard?.instantiateViewControllerWithIdentifier("ContactSelectionViewController") as ContactSelectionViewController
contactSelection.delegate = self
self.navigationController?.pushViewController(contactSelection, animated: true)
}
ContactSelection's contents are:
protocol ContactSelectionDelegate {
func updateInvitedUsers()
}
class ContactSelectionViewController: UITableViewController {
var delegate: ContactSelectionDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.updateInvitedUsers()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Stuff
self.delegate?.updateInvitedUsers()
}
}
What am I doing wrong? I am still new and don't fully understand this subject but after scouring the Internet I can't seem to find an answer. I use the Back button available in the Navigation Bar to return to my CreateEvent view.
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
This is instantiating a view controller directly, and the value never gets used. Since it looks like you're using storyboards, this isn't a good idea since none of the outlets will be connected and you'll get optional unwrapping crashes. You set the delegate of this view controller but that's irrelevant as it doesn't get used.
It also isn't a good idea because if you do multiple pushes you'll be reusing the same view controller and this will eventually lead to bugs as you'll have leftover state from previous uses which might give you unexpected outcomes. It's better to create a new view controller to push each time.
In your code you're making a brand new contactSelection from the storyboard and pushing it without setting the delegate.
You need to set the delegate on the instance that you're pushing onto the navigation stack.
It's also helpful to pass back a reference in the delegate method which can be used to extract values, rather than relying on a separate reference in the var like you're doing.
So, I'd do the following:
Remove the var contactSelection
Add the delegate before pushing the new contactSelection object
Change the delegate method signature to this:
protocol ContactSelectionDelegate {
func updateInvitedUsers(contactSelection:ContactSelectionViewController)
}
Change your delegate calls to this:
self.delegate?.updateInvitedUsers(self)

Resources