Swift - Initializing model object with properties across multiple views - ios

A client wants me to initialize a model object, but the issue I'm facing is that the properties I need are located across five view controllers. At view controller 1 I define propertyA for my object. View controller 2 I define propertyB, and so on. On the final view I get to see a summary of what i've chosen and from there I can finally create my object.
I'm doing this incredibly long and inefficient at the moment where I have the same optional variables for each property across many views. Any help would be great.

For this scenario you should try Singleton pattern
Create class having all properties and create its singleton also:
public class Property {
public var propertyA:AnyObject? /// define your appropriate data type
public var propertyB:AnyObject?
public var propertyC:AnyObject?
public var propertyD:AnyObject?
public var propertyE:AnyObject?
static let sharedInstance = Property()
}
To Set Values in different-2 view controller follow below pattern. Here I am assuming that you can set property any place in view controller. For now I am considering to viewDidLoad you can change value to any part of view controller
class ViewController1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Property.sharedInstance.propertyA = "X" as AnyObject //// Value should be of same data type of propertyA
}
}
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Property.sharedInstance.propertyB = "Y" as AnyObject //// Value should be of same data type of propertyA
}
}
etc
finally at the end you will get values of all assigned parameters
print(Property.sharedInstance.propertyA)
print(Property.sharedInstance.propertyB)
print(Property.sharedInstance.propertyC)
print(Property.sharedInstance.propertyD)
print(Property.sharedInstance.propertyE)

Related

iOS: MVVM-C ViewController super class

I have MVVM-C arch. Each UIViewController has a ViewModel and CoordinatorDelegate to notify the Coordinator when navigation needs to be performed. The code that create the VC repeat itself, and I though it would be great to create a super class to unify all static funcs that create the VC. Like this:
import UIKit
class MVVMCViewController: UIViewController {
weak var coordinatorDelegate: CoordinatorDelegate?
var viewModel: Modelling?
static func initVC(storyboard: Storyboard,
coordinatorDelegate: CoordinatorDelegate?,
viewModel: Modelling?) -> Self {
let viewController = Self.instantiate(in: storyboard)
viewController.coordinatorDelegate = coordinatorDelegate
viewController.viewModel = viewModel
return viewController
}
}
All CoordinatorDelegateProtocols will inherit from CoordinatorDelegate and all ViewModels will be inheriting from Modelling
But the subclassing does not work smoothly.
Any ideas?
Hi this model wouldn't work fine.
MVVMCViewController has hardcoded protocols as variable type, so You should have the same in your childVC.
To make it work as u want MVVMCViewController show be generic (but can be a lot of issues with it), like
class MVVMCViewController<T: Modelling, U: CoordinatorDelegate>: UIViewController {
weak var coordinatorDelegate: U?
var viewModel: T?
}
or add just casted properties to ConnectViewController
class ConnectViewController: MVVMCViewController {
weak var coordinatorDelegate: CoordinatorDelegate?
var viewModel: Modelling?
var currentDelegate: ConnectViewControllerCoordinatorDelegate? {
coordinatorDelegate as? ConnectViewControllerCoordinatorDelegate
}
var currentVM: ConnectViewModel? {
viewModel as? ConnectViewModel
}
}
Your superclass MVVMCViewController defines two properties coordinatorDelegate and viewModel. If you just need to access them in your child class ConnectViewController, just access it normally. You don't need to define it again.
Also, in your parent class, you have weak var coordinatorDelegate: CoordinatorDelegate?. But in your child class (ConnectViewController), you redeclare the property with a different type (ConnectViewControllerCoordinatorDelegate?). That is illegal, even if it is a subclass of CoordinatorDelegate.
Hence, either
Rename the property in child class to avoid the conflict
Keep the name and the type, but add an override keyword for the property if you plan to add additional functionality in your child class
Do not declare the property again at all in your child class if you don't need to add additional functionality to it. Just access it directly.
Refer to how inheritance works in Swift over here: https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html

Cant typecast variable to specific child class involving Generics

Note: Sorry could not come-up with better title than this, so please
suggest a better one if you come across one after reading the question
I have a BasePresenter class, That should take BaseInteractor and BaseRouter as its init arguments, and each child class of BasePresenter should be able to specify subclass of BaseInteractor and BaseRouter in their implementation
So I have declared my BasePresenter as
open class PRBasePresenter<T: PRBaseInteractor, R: PRBaseRouter> {
var interactor: T!
var router: R!
let disposeBag = DisposeBag()
convenience init(with router : R, interactor : T) {
self.init()
self.router = router
self.interactor = interactor
}
}
So now PRBaseCollectionsPresenter which is a child of PRBasePresenter declares its interactor and router as
class PRBaseCollectionsPresenter: PRBasePresenter<PRBaseCollectionsInteractor, PRBaseCollectionRouter> {
//other code here
}
Obviously PRBaseCollectionsInteractor is a subclass of PRBaseInteractor and PRBaseCollectionRouter is a subclass of PRBaseRouter
Everything works till here fine. Now comes the issue. Every ViewController should have presenter as a property. So I have a protocol which mandates that with
protocol PresenterInjectorProtocol {
var presenter : PRBasePresenter<PRBaseInteractor, PRBaseRouter>! { get set }
}
And my BaseViewController confirms to PresenterInjectorProtocol
public class PRBaseViewController: UIViewController,PresenterInjectorProtocol {
var presenter: PRBasePresenter<PRBaseInteractor, PRBaseRouter>!
//other code...
}
Now lets say I have ChildViewController, it will obviously get presenter because of inheritance, but obviously child would want to have its specific presenter than having a generic one. And obviously in Swift when you override a property you cant change the type of the variable. So the only way is
class PRBaseTableViewController: PRBaseViewController {
var tableSpecificPresenter: PRBaseCollectionsPresenter {
get {
return self.presenter as! PRBaseCollectionsPresenter
}
}
//other code goes here
}
This gives me a warning
Cast from 'PRBasePresenter!' to
unrelated type 'PRBaseCollectionsPresenter' always fails
And trying to ignore it and running will result in crash :(
How can I solve this problem? What am I doing wrong? Or is this approach completely wrong?

Trouble mocking singleton for unit testing in Swift

Hello I am trying to mock one of the singletons I use to test that various view controllers actually call properly it's methods.
I have the singleton declared as such
public class ModelsManager {
static let sharedInstance = ModelsManager()
private init() {}
[...]
}
In the view controllers that use the singleton, it is set to a lazy computed property as such:
class MyViewController: UIViewController {
lazy var Models = {
return ModelsManager.sharedInstance
}()
[...]
}
I am trying to mock the ModelsManager singleton in my XCTestCase as such:
[...]
func testSomething() {
let vc = MyViewController(nibName: "MyView", bundle: nil)
var mockModelsManager = ModelsManagerMock.sharedInstance
vc.Models = mockModelsManager
[... do something that calls a function in ModelsManager...]
expect(mockModelsManager.flag) == true // Using Nimble here
}
class ModelsManagerMock: ModelsManager {
var flag = false
override func test() {
flag = true
}
}
In the expect() assertion I am getting Value of type 'ModelsManager' has no member 'flag'
What am I missing here?
EDIT
It appears that what I was missing was ModelsManagerMock.sharedInstance still returns IRModelsManager() from the superclass. Due to the fact that static can't be overwritten by subclasses, how do I get around this?
The correct solution must involve not subclassing your singleton. Creating a singleton with a private init method prohibits you from subclassing this method.
If the goal is to test the current functionality of the singleton, why do you want to add additional functionality to it? The key point of a singleton is that there should only ever be one. If you want to support more than one, you shouldn't make it a singleton, even if it's just for testing.

Overriding a var in Swift

I'm trying to learn Swift by following different tutorials, but I have come into a problem. I have the following code:
Class Vehicle {
var seats = 5
var maxSpeed = 100
}
class Bike : Vehicle{
override var seats = 3
}
But I keep getting an error regarding trying to override var seats, stating Cannot override with a stored property 'seats'.
What am I doing wrong? I thought I could override inherited variables?
You don't actually need to override the property, just initialize with a different value, which can be done in the initializer:
class Vehicle {
var seats = 5
var maxSpeed = 100
}
class Bike : Vehicle{
override init() {
super.init()
self.seats = 3
}
}
As for overriding properties, the lang reference states that:
You can override an inherited instance or class property to provide your own custom getter and setter for that property, or to add property observers to enable the overriding property to observe when the underlying property value changes.

Swift superclass/subclass view controller variables

I have a superclass UIViewController that contains this:
var results: [AnyObject] = []
I also have other methods in the superclass that's based on the content of results array.
I then have subclasses that inherit from this superclass, by loading data from REST API to populate results array with various types of data.
I currently don't override results array in subclass' viewDidLoad() or any other method. I simply clear the array using results = [] and then load the array with a specific class type, such as a custom class Product or String.
results.append(Product())
I've been intermittenly getting crashes, but I can't tell why. I suspect it's due to the way the inheritance is working. Am I doing this correctly? Should I be overriding results in an init() method of the subclass or using `override var?
I think typecasting can work here.
Try to do this in your subclasses:
class SubClass: SuperClass {
private var _results: [Product] = []
override var results: [AnyObject] {
get {
return _results
}
set {
_results = newValue as! [Product]
}
}
}
But Product must be a class and not struct or enum.
Also as String does not conform to AnyObject you cannot save Strings in results array.

Resources