6

I know how to create singleton class in swift. The best and easy way to create singleton class is the following:

class Singleton {
    static let sharedInstance = Singleton()
}

But I don't need singleton for any normal class. I need to create singleton for a viewcontroller class. So I'm using this code create singleton

class AViewController:UIViewController {

    static let sharedInstance = AViewController()

    required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

}

it gives me error near AViewController()

Missing argument for parameter 'coder' in call

Looks like it want me to initialize with init(coder: NSCoder). But what parameter or value should I pass through the coder?

Poles
  • 3,585
  • 9
  • 43
  • 91
  • have you checked with `class Singleton : NSObject` this? – PiyushRathi Dec 14 '16 at 07:01
  • Why didn't you put the problematic code in your question? – rmaddy Dec 14 '16 at 07:03
  • `required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }` – Bhavin Ramani Dec 14 '16 at 07:04
  • 'required public init?(coder aDecoder: NSCoder){ super.init(coder: aDecoder) }' Add this in your code – Shobhakar Tiwari Dec 14 '16 at 07:04
  • @rmaddy : Please see my updated question. – Poles Dec 14 '16 at 07:06
  • @BhavinRamani : I already implemented this. – Poles Dec 14 '16 at 07:07
  • Do you use storyboard? – Ahmad F Dec 14 '16 at 07:08
  • 6
    View controllers are not generally good candidates for singletons. If you really want to do this, we'd probably need to know more about how the view hierarchy for this view controller is created (a scene in a storyboard? NIB? programmatically created?). But, if you're interested, you might want to explain why you want to use a singleton and we might be able to suggest better patterns. – Rob Dec 14 '16 at 07:08
  • @AhmadF : Yes. The viewcontroller is in storyboard. – Poles Dec 14 '16 at 07:09
  • 1) Why would you want a singleton view controller? 2) Please post more relevant code. Do you have any `init` methods? At least show the method signatures. – rmaddy Dec 14 '16 at 07:09
  • @Rob : I want to use this viewcontroller as a sidepanel. And yes the viewcontroller is in storyboard. I have created singleton for viewcontroller in Objective C. Now I want to convert it to swift. – Poles Dec 14 '16 at 07:14
  • @rmaddy : I have a `init` method. Please see the updated question. – Poles Dec 14 '16 at 07:16
  • @Poles If you had already implemented `required init` method, then why are you asking ***what parameter or value should I pass through the coder?*** – Bhavin Ramani Dec 14 '16 at 07:16
  • @BhavinRamani : Because I implemented `init? (coder aDecoder: NSCoder)` and this want me to pass a coder value. – Poles Dec 14 '16 at 07:21
  • And this side panel, itself, has its own scene in the storyboard? And just because this is a side panel, that doesn't suggest that a singleton is a good pattern, even if that's what you were doing before... – Rob Dec 14 '16 at 07:25
  • Possible duplicate of [Using the Swift Singleton](http://stackoverflow.com/questions/34865278/using-the-swift-singleton) – Federico Malagoni Dec 14 '16 at 10:13
  • @FedericoMalagoni : Your link doesn't explain you to create singleton from viewcontroller. Remove duplicate flag from my question. – Poles Dec 14 '16 at 10:53

3 Answers3

14

If you really wanted to have singleton for a view controller corresponding to some scene, you'd probably do something like:

class SecondViewController: UIViewController {

    static let shared = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Foo")

}

In this example, the storyboard was Main.storyboard and the storyboard identifier for the scene in question was Foo. Obviously, replace those values for whatever was appropriate in your case.

Then your other view controller that was invoking this could do something like:

@IBAction func didTapButton(_ sender: Any) {
    let controller = SecondViewController.shared
    show(controller, sender: self)
}

I wouldn't recommend singletons for view controllers. View controllers (and their views) should be created when needed and be allowed to be deallocated when they're dismissed. And you're losing many storyboard benefits (by which you see the logical flow between scenes with segues between them). And, if you use this view controller in different contexts, you're inviting problems stemming from the view controller hierarchy falling out of sync with the view hierarchy. I really would discourage you from using singletons for view controllers.

But if you were going to do it, you could do something like that...

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I need to have same controller/view in 3 tabs of `UITabBarController`. Should I use `singleton`? or seperate instace for each would be recommended? Please share your suggestion for this case. – iRiziya May 14 '18 at 05:37
  • 1
    First, I should say that I think view controllers are poor use case for singletons in general. Second, if you have three tabs, I'm assuming there's something different about each (e.g. different data even if the class for the view controller is the same), so I'd definitely suggest separate instances for each. – Rob May 15 '18 at 21:09
  • Ok..thank you for the response and yeah..I had to go with separate instances – iRiziya May 16 '18 at 04:37
  • @Rob: I used your suggestion and it works perfectly fine. My use case is that I am going to the ViewController A where I wait for the connection to be established. Meanwhile I can go back or change to another ViewController. So when I come back to A I will have a new instance and all new subscriptions to my relays. So I thought about singleton for my VC. I don't see any misbehaviour now but I wanna avoid it. My app is gonna be on Apple store so I don't want them to reject it because of not following the standards. So do you have any suggestion? – msc87 Jul 04 '20 at 13:34
  • 1
    @msc87 1. Apple doesn’t care about the design patterns you use in your code. They only care about what you do, not how you do it. 2. If you want to keep VC A alive, that doesn’t necessarily mean that you need to jump to singleton pattern. You just need to keep a reference to it. 3. If you’re trying to keep ongoing connection request going, this code doesn’t belong in the view controller anyway, so the question isn’t really keeping VC A alive, but rather network layer. That’s how I’d do it. But, again, Apple doesn’t care about code elegance and beautiful architectures, so do whatever you want. – Rob Jul 04 '20 at 19:06
  • @Rob: The network layer is separate and it is the one which oversees the connection. The state of VC A changes according to the connection status. So, if VC A shows a view for the connecting state, when the connection changes to the connected state, it shows another view. This is done with the relays, so if I go back and forth, then I have more than one subscription to those relays. That's what I want to avoid. – msc87 Jul 05 '20 at 18:32
  • Totally agree with Rob. Can't the VC listen to the connection status? See https://developer.apple.com/library/archive/samplecode/Reachability/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007324 & https://medium.com/@dkw5877/reachability-in-ios-172fc3709a37 – Gamma-Point Aug 29 '20 at 22:19
3

Try to do:

AppDelegate:

Add a reference to the ViewController, so you can access it globally, like so:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    private var viewController: ViewController?

    func getViewController() -> ViewController {
        if viewController == nil {
            // make sure that the name of the storyboard is "Main"
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            // make sure that you named the viewcontroller in storyboard (Storyboard ID), it is the identifier
            viewController = storyboard.instantiateViewController(withIdentifier: "ViewControllerStoryboardID") as! ViewController
        }

        return viewController!
    }


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    // ....
}

AnotherViewController (Usage):

Now you can access it via "AppDelegate", like so:

class AnotherViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()


        let appDelegate = UIApplication.shared.delegate as! AppDelegate

        let vc = appDelegate.getViewController()
    }

    // ...
}

Hope this helped.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
2

I would recommend something like:

enter image description here

Senocico Stelian
  • 1,378
  • 15
  • 23