How to declare generic base class? - swift

I have a case which is:
class BaseMvpController<V, P: BasePresenter>: UIViewController { }
I need to make the base class to be of generic type instead of UIViewController.
At some point, I need it to be UIViewController , UITableViewController ..etc
for Example :
My Base Class
class BaseMvpController<V, P: BasePresenter>: UIViewController {
typealias View = V
private(set) var presenter: P!
// MARK: - Initializers
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override public init(nibName: String?, bundle: Bundle?) {
super.init(nibName: nibName, bundle: bundle)
}
deinit {
guard let view = self as? P.View else {return}
if let presenter = presenter {
presenter.detachView(view)
}
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
presenter = createPresenter()
}
override func viewWillAppear(_ animated: Bool) {
guard let view = self as? P.View else {
preconditionFailure("MVP ViewController must implement the view protocol `\(View.self)`!")
}
super.viewWillAppear(animated)
if !presenter.isAttached {
presenter.attachView(view)
}
}
// MARK: - MVP
/// Override and return a presenter in a subclass.
func createPresenter() -> P {
preconditionFailure("MVP method `createPresenter()` must be override in a subclass and do not call `super.createPresenter()`!")
}
}
My AController
class AController : BaseMvpController <AView, APresenter> { }
If we thought it was a kind of UIViewController its works normally
My BController
class BController : BaseMvpController <AView, APresenter> { }
If we thought it was a kind of UITableViewController, This is case I can not override numberOfItemInRaw and dequeCell ...etc , because base controller inherit from UIViewController
I want to make Base controller works with all controller (UIViewController, UITableViewController, UICollectionViewController .. etc).
How can i do this

You can 'hide' generic inside another class.
E.g.,
class Generic<T, U> { }
class BV: UIViewController {
var generic: Generic<Int, String>? = nil
}

Related

ios XCTest failed Could not find a storyboard named LaunchScreen in bundle NSBundle

I'm trying to run a simple test case where I want to test my view controller but I'm getting this error
Could not find a storyboard named 'LaunchScreen' in bundle NSBundle
I'm running a cocoapods lib through the example folder
this is my test case:
import XCTest
#testable import Welcome
class WelcomeViewControllerTests: XCTestCase {
var viewModel: WelcomeViewModelProtocol!
var viewController: WelcomeViewController!
override func setUpWithError() throws {
viewModel = WelcomeViewModel()
viewController = WelcomeViewController(viewModel: viewModel)
viewController.viewDidLoad()
}
override func tearDownWithError() throws {
viewModel = nil
viewController = nil
}
func testLoadLaunchScreen() throws {
viewController.loadLaunchScreen()
}
}
This is my viewController:
import UIKit
public class WelcomeViewController: UIViewController {
private var viewModel: WelcomeViewModelProtocol
public override func viewDidLoad() {
super.viewDidLoad()
loadLaunchScreen()
let string = viewModel.fetchExample()
debugPrint(string)
}
public init(viewModel: WelcomeViewModelProtocol) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func loadLaunchScreen() {
let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "LaunchScreen")
navigationController?.isNavigationBarHidden = true
navigationController?.pushViewController(vc, animated: false)
}
}

How to navigate push view controller from UIView in Swift

How to navigate PushViewController from UIView class to New View Controller, Till now what I tried with delegate/ protocol but still no success from button click, below is the swift code what I tried to navigate from uiview class
protocol MyViewDelegate {
func didTapButton()
}
class Ticket : UIView {
static let instance = Ticket()
#IBOutlet var parentView: UIView!
#IBOutlet weak var ticketView: UIView!
var delegate : MyViewDelegate?
override init (frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("Ticket", owner: self, options: nil)
}
#IBAction func btnHistoryTicket(_ sender: ThemeButtonTicket) {
}
#IBAction func btnNewTicket(_ sender: ThemeButtonTicket) {
func buttonTapAction() {
delegate?.didTapButton() // Expect to navigate
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showTicket(){
UIApplication.shared.windows.first{$0.isKeyWindow}?.addSubview(parentView)
}
}
class ViewController: UIViewController, MyViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let ticket = Ticket()
ticket.delegate = self
}
func didTapButton() {
let vc = kMainStoryboard.instantiateViewController(withIdentifier: "SubmitTicketVC") as! SubmitTicketVC
self.navigationController?.pushViewController(vc, animated: true)
}
}

Delegate keeps returning nil

in my view controller, i have set up like this.
protocol MenuDelegate {
func updateIndexOfMenuExpanded(index: Bool)
}
class ViewController: UIViewController,UITableViewDataSource, UITableViewDelegate {
var delegate : MenuDelegate?
func performaction() -> Void{
delegate!.updateIndexOfMenuExpanded(false)
}
}
and in my baseviewcontroller
class BaseViewController: UIViewController, MenuDelegate{
func updateIndexOfMenuExpanded(index: Bool){
self.menuIsExpanded = index
}
}
please help. thank you.
You have to set the delegate first.
let viewController = ViewController()
let baseViewController = BaseViewController()
viewController.delegate = baseViewController
It would also be wise to make the delegate a weak reference and to not force unwrap with !.
class ViewController: UIViewController,UITableViewDataSource, UITableViewDelegate {
weak var delegate : MenuDelegate?
func performaction() {
delegate?.updateIndexOfMenuExpanded(false)
}
}
Delegate is used when you want to pass data between viewcontrollers.this aproach is one to one
Here is the answer how to pass data using delegate
in viewcontroller
define protocol in view controller
protocol ViewController1BackClicked {
func btnBackClicked(str : String)
}
class ViewController1: UIViewController {
var strTitle : String?
var delegate : ViewController1BackClicked?
override func viewDidLoad() {
super.viewDidLoad()
if strTitle != nil{
title = strTitle
}
}
override func viewWillDisappear(animated: Bool) {
delegate?.btnBackClicked("Krutarth")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
now Protocol is created.to pass data in another view controller
viewcontroller 1 we want to access data
func btnBackClicked(str: String) {
title = str
}
output : Krutarth
this is example how to use protocol

Setting delegate of another class with screen view to self

I'm fairly new at iOS programming. I have this setup:
ViewController view on IB, with class ViewController
SecondController view on IB, with class secondController
I have protocol:
protocol SecondControllerDelegate {
func getSomething() -> String
}
and I have delegate variable on SecondController:
class secondController: UIViewController {
var delegate: SecondControllerDelegate?
#IBOutlet weak var labelStatus: UILabel!
override func ViewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonTouch(sender: AnyObject) {
labelStatus.text = delegate?.getSomething()
}
func try () {
labelStatus.text = "testing"
}
}
Now, according to the hints everywhere, in order so I can call delegate?.getSomething() at SecondController.buttonTouch(), I need to set like this on viewController:
class ViewController: UIViewController, SecondControllerDelegate {
override func viewDidLoad () {
super.viewDidLoad()
SecondController.delegate = self
}
func doSomething () -> String {
return "testing"
}
}
But this generates error 'SecondController.type' does not have a member named 'delegate'.
Some other websites say:
class ViewController: UIViewController, SecondControllerDelegate {
var secondController = SecondController()
override func viewDidLoad () {
super.viewDidLoad()
secondController.delegate = self
}
func doSomething () -> String {
return "testing"
}
}
With this, there are no error. But if I do something on the second screen that should call the delegate, it doesn't call the delegate, like the SecondController is two different objects (one is created by StoryBoard, one is created manually within the ViewController), i.e. the labelStatus that should have changed to "testing", doesn't change at all. But it changes if function try() is called. How am I supposed to do this?
EDIT: I forgot to mention that I used NavigationController, and segue to transition from first screen to second screen.
Because you try to learn how to build a delegate in Swift, I have written you a plain delegate example below
protocol SecondViewControllerDelegate {
func didReceiveInformationFromSecondViewcontroller (information: String)
}
class ViewController: UIViewController, SecondViewControllerDelegate {
func openSecondViewController () {
if let secondViewControllerInstance: SecondViewController = storyboard?.instantiateViewControllerWithIdentifier("SecondViewController") as? SecondViewController {
secondViewControllerInstance.delegate = self
navigationController?.pushViewController(secondViewControllerInstance, animated: true)
}
}
func didReceiveInformationFromSecondViewcontroller(information: String) {
////Here you get the information, after sendInfoToViewController() has been executed
}
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
func sendInfoToViewController () {
delegate?.didReceiveInformationFromSecondViewcontroller("This ist the information")
}
}
UPDATE
Following the same thing in using Storyboard Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let secondViewControllerInstance: SecondViewController = segue.destinationViewController as? SecondViewController {
secondViewControllerInstance.delegate = self
}
}

'UIStoryboard.type' does not have member named 'centerViewController'

I am following THIS tutorial but I didn't downloaded starter project file from that tutorial because I want to make it differently but I am stuck here since an hour because I got this Error:
'UIStoryboard.type' does not have member named 'centerViewController'
Here I am trying to add subView (CenterViewController) into ContainerViewController.
Here is my code for ContainerViewController.swift
import UIKit
import QuartzCore
class ContainerViewController: UIViewController, CenterViewControllerDelegate {
var centerNavigationController: UINavigationController!
var centerViewController: CenterViewController!
override init() {
super.init(nibName: nil, bundle: nil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
centerViewController = UIStoryboard.centerViewController()
//'UIStoryboard.type' does not have member named 'centerViewController'
centerViewController.delegate = self
// wrap the centerViewController in a navigation controller, so we can push views to it
// and display bar button items in the navigation bar
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChildViewController(centerNavigationController)
centerNavigationController.didMoveToParentViewController(self)
}
}
This is my CenterViewController.swift
import UIKit
#objc
protocol CenterViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
class CenterViewController: UIViewController {
var delegate: CenterViewControllerDelegate?
#IBAction func tableTapped(sender: AnyObject) {
}
}
And this is my AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let containerViewController = ContainerViewController()
window!.rootViewController = containerViewController
window!.makeKeyAndVisible()
return true
}
Can anyOne give me any Idea what I am missing?
You are missing the extension the tutorial author has provided that makes the UIStoryboard.centerViewController() method exist. The code is at the bottom of ContainerViewController.swift in his downloadable starter project, and I've copied it down below as well:
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func leftViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("LeftViewController") as? SidePanelViewController
}
class func rightViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("RightViewController") as? SidePanelViewController
}
class func centerViewController() -> CenterViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? CenterViewController
}
}
Add this to the bottom of your ContainerViewController.swift and it should work. (That is, as long as you have these view controllers set up in the right storyboard files with the right identifiers.)