I have a ParentController and ChildController as below. The ParentController holds an array of [AnyObject]? and the child class holds an array of specific models (i.e. Merchant). The parent class has methods that references the results array, hence I've declared it as [AnyObject]?.
Is there a way that I can override the results array in ChildController?
Rationale: I had tried results.append() originally in ChildController, but apparently that causes issues due to downcasting. The problem was sporadic, so hard to explain. However, after about a week of research and trial & error, I think the only way to solve this is to create a separate array in the ChildController to hold data, but still be accessible by methods in the parent class. Thoughts?
class ParentClass: UIViewController {
var results: [AnyObject]?
//has methods that reference results array
}
class ChildClass: ParentClass {
var merchants: [Merchant]?
override var results: [AnyObject]? {
get {
return self.merchants as [AnyObject]?
//this doesn't work, gives EXC_BAD_ACCESS error
}
set {
super.results = self.merchants as [AnyObject]?
}
}
}
Related
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?
I need to "read" ViewController, which was sent as an argument to a function, as a VC of the specific class. So I need something like that (we get a class also from a function arguments):
let vc = vc_from_func_args as! type_from_func_args
I can pass a class to let's say isMemberOfClass() by doing that:
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
But I can't do the same thing with "as" expression. It gives me an error:
klass is not a type
How can we pass class (type?) after "as" as a variable?
Given your comments, this is exactly what protocols are for. If you want a thing you can call pop on, then make that a requirement for your function. If it's easy to list all the things you need, then just put them in your protocol:
protocol Stackable {
var parent: UIViewController {get set}
var child: UIViewController {get set}
}
func push(vc: Stackable) {
// do the work
}
If you really need this to be a UIViewController that also happens to be Stackable, that's fine, too:
func pop<VC: UIViewController where VC: Stackable>(vc: VC) {
// do the work
}
Then just mark your view controllers as conforming to Stackable:
class SomeViewController: UIViewController, Stackable {
var parent: UIViewController
var child: UIViewController
...
}
If you find yourself doing a lot of as! or AnyClass, you're probably on the wrong track in Swift.
How about something like that...
if let checkedClass: MyFirstClass = vc_from_func_args as? MyFirstClass {
//It only hits here if it is MyFirstClass
}
if let checkedClass: MySecondClass = vc_from_func_args as? MySecondClass {
//It only hits here if it is MySecondClass
}
if let checkedClass: MyThirdClass = vc_from_func_args as? MyThirdClass {
//It only hits here if it is MyThirdClass
}
Also you are tying to instantiate a little bit wired :-)
Change this
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
To something like this
vc.isMemberOfClass(MyClass)
You don't need to create an instance to check if another object is kind of a class :-) But just use my code from above... Its even better than this one
I discovered the same issue a little while ago, and ended up writing a downcast global function, and a protocol called Castable that includes the asType function:
protocol Castable: class {
func asType<T>(t: T.Type, defaultValue: T?, file: StaticString,
function: StaticString, line: UWord) -> T?
}
Basically, you can take any class object and write myObject.asType(newType) and it performs the cast. If the cast fails, it logs the failure to the console, reporting the types you were casting to and from, and the file name, method name, and line number where the method was called. I use it for debugging.
At any rate, the way that the asType and downcast functions are written, you can pass the type that you are casting to as a named variable, which is what your original question wanted to do.
The complete code for downcast, the Castable protocol, and the asType function are available at this link.
I have a custom swift class like this
class NichedHelper: NSObject {
private var _theController:UIViewController? = nil
var theController:UIViewController? {
get {
return self._theController
}
set {
self._theController = newValue
}
}...
it has an implementation function like this and _theController passing a Lobb class that inherit UIViewController
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController!
abil.bottomConst.constant = -80
}
it throw error 'AnyObject' does not have a member named 'bottomConst'.
since i don't know what the english word for this kind of technique, so that will be my first question.
my second question, is it possible if i am sure Lobb class (or other class) have a variable called bottomConst, how can i access it from class NichedHelper?
you have declared the _theController as private , remove that just declare as
var _theController:UIViewController!
// this is how we roll in swift ;) bye bye Objective-C
I don't know exactly what you are trying to do and why you have two UIViewController instances. So I'm not able to answer your first question but regarding your second one, you have to cast the object to a UIViewController object:
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController as! UIViewController
abil.bottomConst.constant = -80
}
This at least should make the compiling error away, if you have the bottomConst attribute declared as a variable of UIViewControllers in an extension (since they do not have this variable normally.
Well, i change from passing the UIViewController to NSLayoutConstraint
I have the following closure:
class BISSettingController : XLFormViewController {
class func initializeForm() -> XLFormDescriptor {
var form : XLFormDescriptor
var section : XLFormSectionDescriptor
var row : XLFormRowDescriptor
form = XLFormDescriptor()
row = XLFormRowDescriptor(tag: "tag", rowType: XLFormRowDescriptorTypeButton, title: "Title")
row.action.formBlock = {[weak self](sender: XLFormRowDescriptor!) -> Void in
self?.deselectFormRow(sender)
...
}
}
}
I want to use self as weak reference inside the closure. But when I build the code I get the following error:
'weak' cannot be applied to non-class type 'BISSettingController.Type'
How can I solve this to make it work?
The problem is that this is a class method (class func). In a class method, self means the class. There is no need for memory management on a self representing the class; the class cannot "leak", because it persists for the life of the app anyway. Thus, you cannot describe a reference to a class as weak.
Based on the error message you're getting, it sounds like self is not an object, so you don't need the access list at all.
Can you post more information on the object that contains this code?
EDIT:
Looking at your updated question, it seems that the code you're using is in a class method, not an instance method. That code doesn't seem to make sense from a a class method however.
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.