How to change Swift property visibility in subclass - swift

Is there a way to change Swift property visibility in subclass without introducing new property?
What I would like to do is (having property initialized to default value is not mandatory requirement):
public class MyBaseClass
{
private var abc: Int = 0
}
public class MyClass: MyBaseClass
{
public override var abc: Int = 0 // this cannot be compiled
}
Above code shows compiler error:
Cannot override with stored property 'abc'
The only way I can currently solve this is introducing another property but that is not what I prefer to do:
public class MyClass: MyBaseClass
{
public var abcd: Int
{
get
{
return abc
}
set
{
abc = newValue
}
}
}

Swift has two types of properties: stored properties and computed properties. You can override both of them but the overriden version cannot be a stored property, you have to override using computed properties.
Note that a stored property is just a chunk of memory but a computed property is a set of two methods - a getter and a setter. You cannot override a chunk of memory with another chunk of memory but you can override a method.
See Inheritance - Overriding, section Overriding Properties
You can override an inherited instance or type 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.
You can provide a custom getter (and setter, if appropriate) to override any inherited property, regardless of whether the inherited property is implemented as a stored or computed property at source. The stored or computed nature of an inherited property is not known by a subclass—it only knows that the inherited property has a certain name and type. You must always state both the name and the type of the property you are overriding, to enable the compiler to check that your override matches a superclass property with the same name and type.
You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override. You cannot, however, present an inherited read-write property as a read-only property.
And a Note: under
If you provide a setter as part of a property override, you must also provide a getter for that override. If you don’t want to modify the inherited property’s value within the overriding getter, you can simply pass through the inherited value by returning super.someProperty from the getter, where someProperty is the name of the property you are overriding.
which tells us exactly what to do:
public class MyClass: MyBaseClass {
public override var abc: Int {
get {
return super.abc
}
set {
super.abc = newValue
}
}
}
Note that the following would also work. We just have to make sure we have a computed property:
public class MyClass: MyBaseClass {
public override var abc: Int {
didSet {
}
}
}

This looks better
public class MyClass: MyBaseClass {
override public var abc: Int {
get {
return super.abc
}
set {
super.abc = newValue
}
}
}
Some claims that this is a bug of the compiler, it doesn't synthesize correctly overridden properties.

Try the following:
public class MyClass: MyBaseClass {
private var abc: Int = 0
}
public class MyClass: MyBaseClass {
override init() {
super.init()
self.abc = 0
}
}

Related

Why does accessing a String parameter of a subclassed NSManagedObject with a parent relationship crash?

I have generated classes for two core data entities. The first is called Address and is an abstract entity. The second is called Person, and it inherits from Address. I've added a few example managed attributes for the purpose of this test. And i've added a non-managed String property to the Person class. Accessing the string property of the Person class will crash. Why does this crash?
The Address and Person classes are automatically generated by Xcode, with the exception of the extra parameter: let foo = "Foo"
If i modify the code to make Person inherit from NSManagedObject directly instead of Address, then the code works and doesn't crash.
Automatically generated Address class:
#objc(Address)
public class Address: NSManagedObject {
}
extension Address {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Address> {
return NSFetchRequest<Address>(entityName: "Address")
}
#NSManaged public var street: String?
#NSManaged public var city: String?
}
Automatically generated person class with the exception of the "foo" parameter:
#objc(Person)
public class Person: Address {
public let foo = "Foo" //added this parameter
}
extension Person {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: "Person")
}
#NSManaged public var name: String?
}
problem code
let person = Person(context: context)
print(person.foo) //doesn't crash, but prints empty line instead of value
print("VALUE:\(person.foo):") //crashes with Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
UPDATE:
if foo is defined as
public let foo: String? = "Foo"
then the print statements don't crash, instead they interpret the value as 'nil' and print that.
So my question becomes: Why is this value which is assigned as a constant being reset to nil under the covers?
I have two hand-waving explanations why you are getting nil:
Managed objects don't function very well until they are inserted.
Your foo is a what I would call a constant stored property. I made up the name because, red flag, I cannot find any examples of it in the Swift book chapter on Properties
Put these two together and you get an edge case that doesn't work.
That being said, I'm kind of surprised that your foo setting does not work, because foo is not a managed property (that is, it is not in the data model). If I make such a constant stored property in a regular, non-managed object…
public class Animal {
public let foo: String! = "Foo"
}
it reads back later as expected.
So, if you can accept that this edge case just doesn't work in Core Data, you can move on to several more normal ways that do work.
One way is to declare foo as a var and assign a value to in awakeFromInsert() which is, as I alluded to earlier, after insertion. In Core Data, awakeFromInsert() is one of your friends…
#objc(Person)
public class Person: Address {
public var foo: String!
override public func awakeFromInsert() {
foo = "Foo"
}
}
Another way that works is as a computed property…
#objc(Person)
public class Person: Address {
public var foo : String { return "Foo" }
}
And, finally, the most logical way, since foo is constant for all instances, is to make it a type property…
#objc(Person)
public class Person: Address {
static var foo: String = "Foo"
}
but of course if you do this you must reference it as Person.foo instead of person.foo.

how to set 'setter' of an computed property to private?

I know how to set 'setter' of an stored property to private (e.g. public private(set) var name: String = "John") but how do we set 'setter' of an computed property to private? In this case the 'setter' for the variable 'age'. When I tried to put an keyword private in front of set(newAge){}, XCode display an error. So is it possible to set 'setter' of an computed property to private?
public class Person {
public private(set) var name: String = "John"
var age: Int{
get {
return 10
}
set(newAge){ // how to set this setter to private so to restrict modification
}
}
}
You do it the same way as for a stored property:
private(set) var age: Int{
get {
return 10
}
set(newAge) {
// setter code here
}
}

Autofac registering all subtypes keyed according to a property

The following scenario:
public enum ChildType
{
Type1,
Type2,
Type3
}
public abstract class MyParentClass
{
public abstract ChildType Id { get; }
}
public class Child1 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type1; } }
}
public class Child2 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type2; } }
}
public class Child3 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type3; } }
}
and i would like to use autofac to register all the subtypes using their id as a key, so something like:
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(type => type.IsSubclassOf(typeof(MyParentClass)))
.Keyed<MyParentClass>(c => c.Id)
.SingleInstance();
now obviously the above doesn't work, but is there some way to achieve that without registering each subclass separately? I want to then be able to access them by the enum, i.e. at runtime when i don't know what the value of the enum will be:
public static MyParentClass GetSubClassByEnum(ChildType id)
{
AutofacHostFactory.Container.ResolveKeyed<MyParentClass>(id);
}
Unfortunately you probably won't be able to get exactly this setup working because it's sort of a chicken/egg problem - you want to resolve an object based on information that won't be available... unless you resolve the object.
One way to get this working is to use attributes rather than properties. Attributes are available before the type is instantiated so you could store the info there and achieve the desired result.
Autofac has attribute metadata support that allows you to create custom attributes to provide this sort of information. You could create an attribute that gets inherited and only allows one instance per class. Apply it with the default value to your base class, then when you need to override apply a new attribute on the derived class.
There is plenty of documentation with examples on the Autofac doc site showing how to work with this.

Restrict property type

Good day,
I have a class which represents a collection. The collection has a property of type Type which allows you to specify the data type of each element's meta data object. Each time an element is added to the collection a new instance of the assigned meta data object is created with Activator.CreateInstance(Type type) from within the collection class.
What I need is to restrict the meta data object's type to a type that implements a specific intreface. Example:
publlic class Collection
{
public Type MetaDataType;
// other code
}
public class CollectionImplementation
{
// some properties
public CollectionImplementation()
{
Collection c = new Collection();
// valid assignment
c.MetaDataType = typeof(ValidMetaClass);
// invalid assignement
c.MetaDataType = typeof(InvalidMetaClass);
}
// some functions
}
public class ValidMetaClass : IMetaInterface
{
// valid meta class code
}
public class InvalidMetaClass
{
// invalid meta class code
}
public interface IMetaInterface
{
// interface code
}
Is something like this possible?
Thank you in advance to any and all contributors; I appreciate any and all input.
Kind regards,
me
Try using generics and type constraints on your constructor instead of setting the public field MetaDataType in your Collection class.
public CollectionImplementation<T>(T MetaDataType) where T : NameOfInterface
{
}
You can use restrictions on generic types.
public class Collection<T> where T : IMetaInterface
{
T MetaDataType;
}
Or you could just throw an exception when adding elements like:
if( !( addedElement is IMetaInterface )) throw new Exception("Please don't do this");

ObservableCollection of generic ViewModel class

I'm creating MVVM application and in Model section I have simple base abstract class Animal and class Dog which derives from it:
public abstract class Animal
{
public int Age { get; set; }
}
public class Dog : Animal
{
public string Name { get; set; }
}
ViewModel section containts UI-friendly VM classes of them:
public abstract class AnimalVM<T> : ViewModelBase where T : Animal
{
protected readonly T animal;
public int Age
{
get { return animal.Age; }
set
{
animal.Age = value;
OnPropertyChanged("Age");
}
}
protected AnimalVM(T animal)
{
this.animal = animal;
}
}
public class DogVM : AnimalVM<Dog>
{
public string Name
{
get { return animal.Name; }
set
{
animal.Name = value;
OnPropertyChanged("Name");
}
}
public DogVM(Dog dog) : base(dog) { }
}
Suppose I have another VM class which contains ObservableCollection<AnimalVM>. The problem is how to create that kind of property which allow me to store there different types of Animal? I want to achieve something like this:
public class AnimalListVM : ViewModelBase
{
// here is a problem, because AnimalVM<Animal> isn't compatible with DogVM
readonly ObservableCollection<AnimalVM<Animal>> animals;
public ObservableCollection<AnimalVM<Animal>> Animals
{
get { return animals; }
}
public AnimalListVM(IList<Animal> animals)
{
//this.animals = ...
}
}
I can change ObservableCollection<AnimalVM<Animal>> property to ICollection property and then create list of AnimalVM using some dictionary Animal -> AnimalVM wrapper and Activator.CreateInstance() - it works but when I try to extend AnimalListVM adding another property SelectedAnimal which will be binded in sample View to e.g. DataGrid control I have another problem with type of that kind of property SelectedItem. It can't be of type AnimalVM<Animal> because when I have DogVM object in my Collection it won't fit with this and throw an exception.
Everything will be clear if only I had non-generic AnimalVM but I don't want to copy and paste similar properties in every DogVM, CatVM, BirdVM class derived from AnimalVM. How can I achieve this?
Ok, I've found a solution and of course it's very simple: just create another, non-generic abstract base class for your generic abstract base class and then derive your generic class from that newly created non-generic class. In that case you also must rewrite properties from non-generic class to generic class (to be more specific override them), but you do this only once, so you don't have to copy and paste the same code in every generic derived ViewModel (in our example in every DogVM, CatVM, BirdVM, etc.).