Initialize protocol in a class - ios

protocol Engine {
func setSpeed(speed: Double)
}
struct Car: Engine {
let speed: Double
func setSpeed(speed: Double) {
self.speed = speed
}
}
class RandomViewController: UIViewController {
let engine: Engine
}
I saw a video of someone who demonstrated protocol oriented in Swift and I was taken with it. The person did something like this and seemed to get no errors.
I get the error Class 'RandomViewController' has no initializers. Where am I off? Could someone correct me and point me at the right direction?
EDIT: The video is https://youtu.be/-g53kYDIpP4?t=611
EDIT2: Didn't listen through the video.

It's because you declare constant which is not Optional and don't initialise it.
let engine: Engine
You should declare it with var and make it optional or write initializer in you RandomViewController and init your constant there.
UPDATE
Example
protocol Engine {
mutating func setSpeed(speed: Double)
}
struct Car: Engine {
var speed: Double = 0
mutating func setSpeed(speed: Double) {
self.speed = speed
}
}
class ViewController: UIViewController {
let engine:Engine
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
engine = Car(speed: 5);
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
engine = Car(speed: 5);
super.init(coder: aDecoder)
}
}

Change it as following and initialize it before you use it:
var engine: Engine!

The problem was I had variables inside my struct on a global scale which cause the problem. Whenever I tried to use the protocol as a type it complained about it need to be initialized.
I had to dissolve the protocol and struct and assemble it a different way that solved my issue.

Related

How to subclass a UIViewController and add properties in swift?

I want to make a new kind of view controller in Swift that has an additional property that must be explicitly initialized. It doesn't make any sense for this property to be nil or have a default value and the controller will only be initialized programmatically. I tried defining it like this:
class MyController : UIViewController {
var prop: Int
init(prop: Int) {
self.prop = prop
super.init()
}
required init(coder aDecoder: NSCoder?) {
fatalError("don't serialize this")
}
}
I tried running this but it crashed because super.init() tries to run the nib constructor which isn't defined, so I tried adding that:
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
But now the compiler complains that prop isn't being initialized. And this is my question: how can I initialize prop correctly here? I don't want a default value, and anything I set will override the correct value that I set in the other initializer.
I kinda hacked around it by setting some default value in the nib init, but then having my first init do this
self.prop = prop
super.init()
self.prop = prop
But other than being really weird and ugly, that makes me worried that now it is possible to initialize my view controller from a nib and end up with the default value, which would be bad.
What is the correct and idiomatic way to do this in Swift?
At some point the view controller must be initialized by calling init(nibName:bundle:) or init(coder:)
Try this:
class MyViewController: UIViewController {
var prop: Int
init(prop: Int, nibName nibNameOrNil: String? = nil, bundle nibBundleOrNil: Bundle? = nil) {
self.prop = prop
super.init(nibName:nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Try the following
class MyController: UIViewController {
var prop: Int
required init?(coder aDecoder: NSCoder) {
fatalError()
}
init(prop: Int) {
self.prop = prop
super.init(nibName: nil, bundle: nil)
}
}

Problems getting around 'Super.init isn't called on all paths before returning from initializer swift.'

I'm trying to implement a BPM counter into a basic iOS app.
I'm using this implementation in an XCode project, connecting the addTap method to an onscreen button.
However, I receive the error.
Super.init isn't called on all paths before returning from initializer swift.
So following this answer I found by searching here, I realised I have to call super.init() as defined in the UIViewController class.
I've done that, as shown below - but now I'm getting another compiler error
Cannot convert value of type 'NSCoder.Type' to expected argument type 'NSCoder'
What I have at the minute is pasted below, TIA
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var display: UILabel!
private let timeOutInterval: TimeInterval
private let minTaps: Int
private var taps: [Date] = []
init(timeOut: TimeInterval, minimumTaps: Int) {
timeOutInterval = timeOut
minTaps = minimumTaps
super.init(coder:NSCoder)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addTap() -> Double? {
let thisTap = NSDate()
if let lastTap = taps.last {
if thisTap.timeIntervalSince(lastTap) > timeOutInterval {
taps.removeAll()
}
}
taps.append(thisTap as Date)
guard taps.count >= minTaps else { return nil }
guard let firstTap = taps.first else { return nil }
let avgIntervals = thisTap.timeIntervalSince(firstTap) / Double(taps.count - 1)
return 60.0 / avgIntervals
}
#IBAction func button(_ sender: Any) {
self.display.text = String(addTap())
}
}
Your issue is with the line:
super.init(coder:NSCoder)
The parameter needs to be an instance of NSCoder, not the class itself. But you shouldn't even be calling that initializer.
Change that line to:
super.init(nibName: nil, bundle: nil)

Instantiate ViewModel data with Generic Protocols

So basically I'm trying to define one generic viewmodel protocol from which I would be able to define all other viewmodels ... but data in them would need to stay generic ... lets say I define protocol like below:
protocol GenericVMProtocol {
associatedtype T
var items: [T] { get }
}
Next, I conform my VM to it and define data:
class VM: GenericVMProtocol {
typealias T = String
var items: [String] = ["A", "B" , "C"]
}
For now everything is working as expected, but problems happen when I want to conform my VC to VMProtocol like so:
class VC: UIViewController {
var vm: GenericVMProtocol
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
vm = VM()
super.init(nibName: nil, bundle: nil)
}
func items() {
print(vm.items)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
Im getting error:
Protocol 'GenericVMProtocol' can only be used as a generic constraint
because it has Self or associated type requirements
... And I must say I'm pretty clueless if I'm missing some logic or my thinking is wrong .. so I would appreciate any help! Tnx :)
This is a PAT (protocol associated type) problem you cannot directly use GenericVMProtocol as type because again it has associated type requirements in your case
I dont know if the following will help you but the correct way to use a protocol with generic constraint is as follow :
protocol GenericVMProtocol {
associatedtype T
var items: [T] { get }
init()
}
class VM: GenericVMProtocol {
typealias T = String
var items: [String] = ["A", "B" , "C"]
required init() {}
}
class VC<T>: UIViewController where T:GenericVMProtocol {
var vm: T
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
vm = T()
super.init(nibName: nil, bundle: nil)
}
func items() {
print(vm.items)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}

How do I pass arguments to the resolve method when using Swinject?

I have a test project that I'm trying to pass an argument to the resolve method in a Swinject project.
Here is an example of what my Swinject storyboard extetion file has in it.
import Swinject
extension SwinjectStoryboard {
class func setup() {
let mainDm = MainDM()
defaultContainer.register(MainDM.self) { _ in
mainDm
}
defaultContainer.registerForStoryboard(ViewController.self) { r, c in
c.dm = r.resolve(MainDM.self)
c.container = defaultContainer
}
defaultContainer.register(GetMessageAction.self) { _, delegate in
GetMessageAction(dm:mainDm, delegate: delegate)
}
}
}
in my ViewController I'm trying to do the following to resolve the GetMessageAction
#IBOutlet weak var myText: UILabel!
var dm:MainDM!
var container:Container!
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(3), target: self, selector: #selector(ViewController.getMessage), userInfo: nil, repeats: false)
}
func getMessage() {
let action:GetMessageAction? = container.resolve(GetMessageAction.self, argument: self)!
action?.execute()
}
I get the following message when my getMessage function runs
fatal error: unexpectedly found nil while unwrapping an Optional value
As resolving with arguments is dependent on exactly matching types of arguments, you need to downcast passed object:
container.resolve(GetMessageAction.self, argument: self as GetMessageActionDelegate)!
Assuming that GetMessageActionDelegate is the type of delegate passed in constructor GetMessageAction(dm:delegate:).
The swift file of the ViewController you have created in your Storyboard must declare init(NSCoder), it is actually not mentioned in the README.md, I'm thinking about opening an issue regarding this...
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
You can take a look at my open source project using exactly this technique, I am setting up the dependencies using the extension of SwinjectStoryboard here for example the LoadingDataVC.
extension SwinjectStoryboard {
class func setup() {
defaultContainer.register(HTTPClientProtocol.self) { _ in
HTTPClient()
}.inObjectScope(.Container)
defaultContainer.register(APIClientProtocol.self) { r in
APIClient(
httpClient: r.resolve(HTTPClientProtocol.self)!
)
}.inObjectScope(.Container)
defaultContainer.register(ImagePrefetcherProtocol.self) { _ in
ImagePrefetcher()
}.inObjectScope(.Container)
defaultContainer.registerForStoryboard(GameVC.self) { r, c in
c.imagePrefetcher = r.resolve(ImagePrefetcherProtocol.self)
}
defaultContainer.registerForStoryboard(LoadingDataVC.self) { r, c in
c.apiClient = r.resolve(APIClientProtocol.self)
c.imagePrefetcher = r.resolve(ImagePrefetcherProtocol.self)
}
}
}
Once you have the required init it should work! :)
Use either of the following methods of a storyboard to get a view controller registered by registerForStoryboard.
instantiateViewControllerWithIdentifier
instantiateInitialViewController
https://github.com/Swinject/Swinject/blob/v1/Documentation/Storyboard.md
https://github.com/Swinject/SwinjectStoryboard/issues/5

Data passing in Swift UITableViewController Initializer

I am facing a weird problem. I have a Custom class FormDataSource like below
class FormDataSource{
var Sections: Array<SectionDataProvider>?;
var Rows:Array<Array<RowDataProvider>>!;
var formData: Dictionary<String, Any?>!;
init(sections:Array<SectionDataProvider>?, withRows:Array<Array<RowDataProvider>>){
self.Sections = sections;
self.formData = [String:Any?]();
self.Rows = withRows;
for rowArray in self.Rows
{
for row in rowArray
{
self.formData.updateValue(row.actualData, forKey: row.keyForRowData);
}
}
}
}
And a UITableViewController subclass FormViewController like below
class FormViewController:UITableViewController{
var formDataSource:FormDataSource!;
init(with DataSource :FormDataSource) {
self.formDataSource = DataSource;
println("count: \(self.formDataSource.Rows.count)"); //prints 3 correctly
super.init(style: UITableViewStyle.Plain);
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil);
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder);
}
override func viewDidLoad() {
super.viewDidLoad();
println("count: \(self.formDataSource.Rows.count)"); //crash at this line
}
}
when I am creating a new instance of the FormViewController It crashes inside viewDidLoad giving
"fatal error: unexpectedly found nil while unwrapping an Optional value".
The code is given below:
var dataSource = FormDataSource(sections: nil, withRows:rows);
let joinView:FormViewController = FormViewController(with: dataSource);
self.navigationController?.pushViewController(joinView, animated: true);
I guess the cause might be that the variable I am passing is being destroyed. Can anyone please tell me what am I doing wrong?
If you built your TableViewController with InterfaceBuilder then either
init(coder)
or
init(nibName)
is called for initialization.
That means that your formData property is not being initalized and it results in that error.
Set breakpoints in the various init functions to see which one gets called.

Resources