not initialized at super.init call - ios

New to iOS.
How should I resolve this:
#objc protocol NwNamew {
init(vm: ViewModel)
}
class ViewController: UIViewController, NwNamew {
var viewModel: ViewModel
required init(vm: ViewModel) {
self.viewModel = vm
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
getting error: Property 'self.viewModel' not initialized at super.init call

Before you call super.init(coder:) you have to have initialised all the variables in the class. As you aren't implementing the init(coder:) function in your class you can remove this super.init(coder:) call. This will get rid of your compile error.

You could also resolve this error by making the parameter as Optional, as so
var viewModel: ViewModel?
Optionals are parameters that can be nil and need to be unwrapped before usage.
You could read more here about Optionals:
Optionals

Related

Assigning viewModel on Controller Init?

I am using MVVM and want to assign my viewModel to the viewController on the Controllers init. I thought I would achieve this like so:
class LoginViewController: UIViewController, UITextFieldDelegate {
init(loginViewModel: LoginViewModel) {
self.loginViewModel = loginViewModel
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
However I get the error:
Super.init isn't called on all paths before returning from initializer
Is this not the correct route to take? also how would I init the viewModel correctly when it requires an object to do so, but it has to perform a network request first? Init with a blank object instance?
Thanks
edit: this is what im trying now
initWithViewAndViewModel:(loginView: LoginView, loginViewModel: LoginViewModel) {
super.init()
self.loginView = loginView
self.loginViewModel = loginViewModel
}
As a best practice,
Step 1:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Step 2:
init(loginViewModel : LoginViewModel) {
super.init(nibName: nil, bundle: nil)
initWithModel:(loginViewModel: LoginViewModel)
}
Step 3: implement initWithModel function
Try adding this if it works:
convenience init() {
self.init(loginViewModel: nil)
}
init(loginViewModel: LoginViewModel?) {
self.loginViewModel = loginViewModel
super.init(nibName: nil, bundle: nil)
}
Please go through this link it might help: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_324

Has no accessible initializers (UITableView) [duplicate]

Apologies if this has been asked before, I've searched around a lot and many answers are from earlier Swift betas when things were different. I can't seem to find a definitive answer.
I want to subclass UIViewController and have a custom initializer to allow me to set it up in code easily. I'm having trouble doing this in Swift.
I want an init() function that I can use to pass a specific NSURL I'll then use with the view controller. In my mind it looks something like init(withImageURL: NSURL). If I add that function it then asks me to add the init(coder: NSCoder) function.
I believe this is because it's marked in the superclass with the required keyword? So I have to do it in the subclass? I add it:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Now what? Is my special initializer considered a convenience one? A designated one? Do I call a super initializer? An initializer from the same class?
How do I add my special initializer onto a UIViewController subclass?
class ViewController: UIViewController {
var imageURL: NSURL?
// this is a convenient way to create this view controller without a imageURL
convenience init() {
self.init(imageURL: nil)
}
init(imageURL: NSURL?) {
self.imageURL = imageURL
super.init(nibName: nil, bundle: nil)
}
// if this view controller is loaded from a storyboard, imageURL will be nil
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
For those who write UI in code
class Your_ViewController : UIViewController {
let your_property : String
init(your_property: String) {
self.your_property = your_property
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported")
}
}
This is very similar to the other answers, but with some explanation. The accepted answer is misleading because its property is optional and doesn't expose the fact that your init?(coder: NSCoder) MUST initialize each and every property and the only solution to that is having a fatalError(). Ultimately you could get away by making your properties optionals, but that doesn't truly answer the OP’s question.
// Think more of a OnlyNibOrProgrammatic_NOTStoryboardViewController
class ViewController: UIViewController {
let name: String
override func viewDidLoad() {
super.viewDidLoad()
}
// I don't have a nib. It's all through my code.
init(name: String) {
self.name = name
super.init(nibName: nil, bundle: nil)
}
// I have a nib. I'd like to use my nib and also initialze the `name` property
init(name: String, nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle? ) {
self.name = name
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
// when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
// The SYSTEM will never call this!
// it wants to call the required initializer!
init?(name: String, coder aDecoder: NSCoder) {
self.name = "name"
super.init(coder: aDecoder)
}
// when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
// The SYSTEM WILL call this!
// because this is its required initializer!
// but what are you going to do for your `name` property?!
// are you just going to do `self.name = "default Name" just to make it compile?!
// Since you can't do anything then it's just best to leave it as `fatalError()`
required init?(coder aDecoder: NSCoder) {
fatalError("I WILL NEVER instantiate through storyboard! It's impossible to initialize super.init?(coder aDecoder: NSCoder) with any other parameter")
}
}
You basically have to ABANDON loading it from storyboard. Why?
Because when you call a viewController storyboard.instantiateViewController(withIdentifier: "viewController") then UIKit will do its thing and call
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
You can never redirect that call to another init method.
Docs on instantiateViewController(withIdentifier:):
Use this method to create a view controller object to present
programmatically. Each time you call this method, it creates a new
instance of the view controller using the init(coder:) method.
Yet for programmatically created viewController or nib created viewControllers you can redirect that call as shown above.
Convenience initializers are secondary, supporting initializers for a
class. You can define a convenience initializer to call a designated
initializer from the same class as the convenience initializer with
some of the designated initializer’s parameters set to default values.
You can also define a convenience initializer to create an instance of
that class for a specific use case or input value type.
They are documented here.
If you need a custom init for a popover for example you can use the following approach:
Create a custom init that uses the super init with nibName and bundle and after that access the view property to force the load of the view hierarchy.
Then in the viewDidLoad function you can configure the views with the parameters passed in the initialization.
import UIKit
struct Player {
let name: String
let age: Int
}
class VC: UIViewController {
#IBOutlet weak var playerName: UILabel!
let player: Player
init(player: Player) {
self.player = player
super.init(nibName: "VC", bundle: Bundle.main)
if let view = view, view.isHidden {}
}
override func viewDidLoad() {
super.viewDidLoad()
configure()
}
func configure() {
playerName.text = player.name + "\(player.age)"
}
}
func showPlayerVC() {
let foo = Player(name: "bar", age: 666)
let vc = VC(player: foo)
present(vc, animated: true, completion: nil)
}

Missing argument for parameter 'coder' in call in Swift

class SecondViewController:UIViewController {
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
//Custom logic here
}
}
Quite a newbie question:
a view controller(SecondViewController), inherent from UIViewController needs a designated init function like above.
In this case, how should I call it, given I am not sure what the value for "coder" should be? I used to call the controller as: SecondViewController(), but it gives:
Missing argument for parameter 'coder' in call
I understand coder parameter has to be provided, but want to ask what its value comes from.
Thanks for the answers from #Chackle. Finally the solution I figured out is below.
What I want:
Inherit my SecondViewController from UIViewController
Simply to initialize any new SecondViewController as SecondViewController()
Solution:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init() {
super.init(nibName: nil, bundle: nil)
//Do whatever you want here
}
"required init(coder aDecoder: NSCoder)" is a must if you create a subclass of UIViewController. And so is "super.init(nibName: nil, bundle: nil)", because it is the way how UIViewController does initialization.

Pass self as argument within init method in Swift 1.2

The following class has a 'let' property declared as implicitly unwrapped variable. This previously worked with Xcode 6.2:
class SubView: UIView {
let pandGestureRecognizer: UIPanGestureRecognizer!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.pandGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panAction:")
}
func panAction(gesture: UIPanGestureRecognizer) {
// ...
}
}
After updating to Xcode 6.3 (with Swift 1.2), the following compilation errors occur:
Property 'self.panGestureRecognizer' not initialized at super.init call
Immutable value 'self.panGestureRecognizer' may only be initialized once
Moving the following line before the super.init call:
self.pandGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panAction:")
gives the following error:
'self' is used before super.init call
The property 'panGestureRecognizer' requires no mutation, therefore it has to be declared as constant 'let'. Since it is a constant, it has to have an initial value upon declaration, or initialize it within the init method. To initialize it, it requires to pass 'self' in the 'target' parameter.
Other thread suggested to declare it as implicitly unwrapped optional, and initialize it after the super.init call. This previously worked until I updated to Xcode 6.3.
Does anybody know a proper implementation or a workaround for this case?
The Problem
The problem is your use of let - optionals declared as let aren't given a default value of nil (var is however). The following, introduced in Swift 1.2, wouldn't be valid otherwise since you wouldn't be able to give myOptional a value after declaring it:
let myOptional: Int?
if myCondition {
myOptional = 1
} else {
myOptional = nil
}
Therefore, you're getting the error 'Property 'self.panGestureRecognizer' not initialized at super.init call' because before calling super.init(coder: aDecoder), because panGestureRecognizer isn't nil; it hasn't been initialised at all.
The Solutions:
1. Declare panGestureRecognizer as a var, meaning it will be given a default value of nil, which you could then change after calling super.init(coder: aDecoder).
2. In my opinion, the better solution: don't use an implicitly unwrapped optional and declare panGestureRecognizer with an initial value of UIPanGestureRecognizer(). Then set the target after super.init is called:
class SubView: UIView {
let panGestureRecognizer = UIPanGestureRecognizer()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
panGestureRecognizer.addTarget(self, action: Selector("panAction:"))
}
}
You can't use self unless the class is initialized. And if you would like to use self for property initialization, it must be lazy. But lazy is not supported for let, just var.
That's because:
You must always declare a lazy property as a variable (with the var
keyword), because its initial value might not be retrieved until after
instance initialization completes. Constant properties must always
have a value before initialization completes, and therefore cannot be
declared as lazy.
It's kind of compromise and if you can live with private setter, you can do this:
class SubView: UIView {
private(set) lazy var panGestureRecognizer: UIPanGestureRecognizer = { [unowned self] in UIPanGestureRecognizer(target: self, action: "panAction:") }()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func panAction(gesture: UIPanGestureRecognizer) {
}
}
Or initialize panGestureRecognizer with just UIPanGestureRecognizer() and add target later.
A workaround for this specific case would be:
class SubView: UIView {
let pandGestureRecognizer: UIPanGestureRecognizer
required init(coder aDecoder: NSCoder) {
self.pandGestureRecognizer = UIPanGestureRecognizer()
super.init(coder: aDecoder)
self.pandGestureRecognizer.addTarget(self, action: "panAction:")
}
func panAction(gesture: UIPanGestureRecognizer) {
// ...
}
}
If you want to pass self to initializer of an object, you should declare your object as lazy. Because when this object is initialized, self is not ready yet.
lazy var viewModel = IntroViewModel(controller: self)
class IntroViewModel {
private weak var controller: IntroViewController?
init(controller: IntroViewController?) {
self.controller = controller
}
}
I had this problem for a different reason, it had nothing to do with Optionals or lazy. Just literally that the person object had to be initialized once.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Account {
static let shared = Account(person: Person(name: "Bobby")) // <-- person initialized once
let person: Person = Person(name: "Lio") // initialized again!
init(person: Person) {
self.person = person
}
}
It's quite interesting that Swift can catch this error

error when declaring a new object in a UIViewController in swift

I have created file called MyHelper.swift
and I created class inside it:
public class MyHelper{
//..... variables here
public init(list listOfViews: [UIView]){
self.listOfViews = listOfViews
self.time = 1.0;
}
}
then i declared an object in UIViewController like this
class ViewController: UIViewController {
var myHelper: MyHelper;
override func viewDidAppear(animated: Bool) {
myHelper = MyHelper(listOfViewsToAnimatin: listOfViews)
}
// ..... rest of the code
}
but i got error that says:
**
Class "ViewController" has no initializers.
**
I tried the default fixes suggested in xcode:
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
It caused another error.
then i tried this code from internet:
required init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
It says :
Property self.myHelper not initialized at super.init call
How on earth i can use object of MyHelper class inside UIViewController !?
This is Swift's compile time checking at work.
You'll need to either setup the MyHelper in the init method, or mark it as optional (note the question mark at the end of the var declaration):
class ViewController: UIViewController {
var myHelper: MyHelper?
required init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myHelper = MyHelper(listOfViewsToAnimatin: listOfViews)
}
// ..... rest of the code
}
You are initializing the MyHelper in viewDidAppear. It needs to be initialized in init (before super.init()) or you need to declare it as optional and set to nil.
var myHelper: MyHelper? = nil
You can make myHelper optional:
var myHelper:MyHelper?
When you use it, unwrap it first with:
if let myHelper = myHelper {
myHelper.yourFunction()
} else {
// self.myHelper == nil
}
Alternatively you can unwrap with !:
myHelper!.yourFunction()
But it will crash if myHelper is nil.

Resources