How to simplify code - ios

I have the following code:
class MySuperClass : UIViewController {
var model: ModelA!
}
class ModelA {
var aBool = true
}
class ModelB: ModelA {
var boolBelongsToB = true
}
class MySubclass: MySuperclass {
func testFunction() {
let theBool = (model as! ModelB).boolBelongsToB // Simplify this
}
}
var aSubclass = MySubclass()
var aModelB = ModelB()
aSubclass.model = aModelB
What I want to do is simplify having to use the code (model as! ModelB) everytime I want to access my model in MySubclass. How can I do this?

Why not just create a computed property in your subclass Y which returns the correct type. Like
var modelB: ModelB {get { return model as! ModelB }}
Instead of getting it then as "(model as! ModelB)" you can just get it as "modelB".

Related

(MVVM) Cannot use instance member 'model' within property initializer; property initializers run before 'self' is available

I want access to SampleMoel's 'head' at ViewModel
How to fix this error?
this is MVVM(not Use combine, RxSwift ... only use Uikit)
SampleViewModel.swift
class SampleViewModel {
var model: SampleModel?
let changeData = Observer(model?.head) //Line error
init() {
self.model = SampleModel()
}
func changeLabel(_ tf: String) {
self.changeData.value = tf
}
}
SampleModel.swift
struct SampleModel {
var head = "initValue"
}
Modify your implementation as follows:
class SampleViewModel {
private let model: SampleModel
let changeData: Observer<String>
init(model: SampleModel) {
self.model = model
changeData = Observer(model.head)
}
func changeLabel(_ tf: String) {
self.changeData.value = tf
}
}

How to find a class instance name in other class in swift?

Imagine a situation that we have two classes like these:
class MyClass {
func printInstanceName() {
print(what?);
}
}
class User {
var firstObject: MyClass!
var secondObject: MyClass!
}
I will instantiate an object of class User like this:
let user = User();
user.firstObject = MyClass()
user.secondObject = MyClass()
How should i implement the printInstanceName that the output of code below
user.firstObject.printInstanceName()
user.secondObject.printInstanceName()
be
firstObject
secondObject
The purpose of doing this is, i want to know from which instance of MyClass in User, printInstanceName is called!
You can get variable name only in this way
class MyClass {
func printInstanceNameIn(user: User) {
let mirror = Mirror(reflecting: user)
for (label, value) in mirror.children {
if let value = value as? MyClass, value === self {
print(label!)
}
}
}
}
class User {
var firstObject: MyClass!
var secondObject: MyClass!
}
let u = User()
u.firstObject = MyClass()
u.secondObject = MyClass()
u.firstObject.printInstanceNameIn(user: u)
Use String(describing: type(of: self)) to print the class name.
class MyClass {
func printInstanceName() {
print(String(describing: type(of: self)))
}
}
Don't use force-unwrapped variables unnecessarily. You can create an init(myObject:) instead.
class User {
var myObject: MyClass
init(myObject: MyClass) {
self.myObject = myObject
}
}
Create User instance like,
let user = User(myObject: MyClass())
user.myObject.printInstanceName()
You cant get property name from MyClass, only from User.
class MyClass { }
class User {
var myObject: MyClass!
func printInstanceName() {
let mirror = Mirror(reflecting: self)
for (label, value) in mirror.children {
if type(of: value) == Optional<MyClass>.self {
print(label!)
}
}
}
}
let u = User()
u.printInstanceName()
I know this is not what you're exactly looking for but it might helps:
for child in Mirror(reflecting: user).children {
print(child.label)
}

Return Class based on Generic in Swift

I have two custom class objects. I have to show a view which reads the data from both the objects depending on what class object we passed.
How can I achieve this?
Thank you,
You can set your myObject as AnyObject and after you can check object is from class A or class B.
class viewc : UIViewController {
var myObject: AnyObject?
override func viewDidLoad() {
super.viewDidLoad()
if let object = myObject as? ClassA {
print("Object is of class- ClassA")
}else if let object = myObject as? ClassB {
print("Object is of class- ClassB")
}
}
}
You cannot do it like var object = <T>() as its not the correct format or var object = T() as Non-nominal type 'T' does not support explicit initialization.
You can use it like this:-
class ABC<T>: UIViewController{
var object: T? = nil
}
Hope it helps :)
You can use a generic type.
class ControllerTimeline<T>: UIViewController {
var myObject :T?
...
}
If you need T to have some constraint (example: could be only NSObject)
class ControllerTimeline<T: NSObject>: UIViewController {
var myObject :T?
...
}
If you want to use only A or B, you can create a common parent for A and B+
class ControllerTimeline<T: C>: UIViewController {
...
Or let A and B implement a common protocol and type something like this
class ControllerTimeline<T: MyProtocol>: UIViewController {
...

"Generic parameter 'Value' could not be inferred" for `observe`

I have a class that is generic, like:
class Row<T> {
...
}
If I have an instance of Row where T is ExampleClass, I want to be able to do:
row.bind(to: \ExampleClass.category)
Then in bind I want to start observing ExampleClass.category for my ExampleClass instance that I have in the class.
I've implemented:
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<T, Value>) {
if let model = model as? NSObject {
model.observe(targetKeyPath, options: [.new ,.old], changeHandler: { _, change in
Log.info?.msg("Now we have some stuff: \(change)")
})
}
}
This gives me the error: Generic parameter 'Value' could not be inferred.
How is that possible? The T is solved but why can't Value be inferred? It should come from the parameter targetKeyPath.
Full code for repoducable:
class Row<T> {
let model: T
init(model: T) {
self.model = model
}
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<T, Value>) {
if let model = model as? NSObject {
model.observe(targetKeyPath, options: [], changeHandler: { _, _ in
})
}
}
}
How I'd like to use the example class above:
class Person: NSObject {
#objc var age: NSNumber?
}
class Book: NSObject {
#objc var title: String?
}
let row1 = Row(model: Person())
let row2 = Row(model: Book())
row1.bind(to: \Person.age)
row2.bind(to: \Book.title)
You're probably over-thinking this. The problem is that model is known only to be an NSObject. You can reproduce simply like this:
class ExampleClass:NSObject {
#objc dynamic var category = ""
}
let model = NSObject()
model.observe(\ExampleClass.category) { _,_ in }
Same error. If you change model to be an ExampleClass, the problem goes away.

When using MVVM, how do I extend the ViewModel in child ViewControllers?

I have a hierarchy of this sort:
class OneViewModel {
}
class OneViewController {
var viewModel = OneViewModel()
}
class TwoViewModel : OneViewModel {
}
class TwoViewController : OneViewController {
var viewModel = TwoViewModel() // ???
}
I know that overriding property types is not allowed in Swift, but it doesn't make sense for the child ViewController to carry two ViewModels, how would one go about solving this? Thanks!
this should work
class OneViewModel {
}
class OneViewController {
var viewModel = OneViewModel()
}
class TwoViewModel : OneViewModel {
}
class TwoViewController : OneViewController {
override init() {
super.init()
self.viewModel = TwoViewModel()
}
}
you can set your viewModel instance to be TwoViewModel class anywhere.
I use this:
class OneViewController {
var viewModel: OneViewModel {
return storedViewModel
}
lazy var storedViewModel: OneViewModel = {
type(of: self).viewModelProvider()
}()
class func viewModelProvider() -> OneViewModel {
return OneViewModel()
}
}
class TwoViewController {
override var viewModel: TwoViewModel {
return storedViewModel as! TwoViewModel
}
override class func viewModelProvider() -> OneViewModel {
return TwoViewModel()
}
}
It's somewhat verbose to setup, but is easy and clear to use afterwards.

Resources