Obtaining inverse relationships from a generic List in Realm - ios

I would like to create a generic Realm List consisting of different class types (but all of superclass Object).
class Parent: Object {
var children = List<Object>()
}
class Child1: Object {
let parents = LinkingObjects(fromType: Parent.self, property: "children")
}
class Child2: Object {
let parents = LinkingObjects(fromType: Parent.self, property: "children")
}
Linking instances of Child1 and Child2 to a Parent object works fine, however, the inverse relationships gives the following error:
- Property ‘Parent.children’ declared as origin of linking objects property ‘Child1.parents’ links to a different class.
- Target type 'RealmSwiftObject' doesn't exist for property ‘children’.
How could it be ensured that the parents property is determined correctly? I can imagine that it should be filtered for the class type where it belongs to (i.e. Child1 or Child2) but I don't know how to do that?

Unless there is a workaround I am not aware of, it is not possible to store different types in a List.
Lists contain other Objects of a single type and have an interface very similar to a mutable Array. (https://realm.io/docs/swift/latest/#to-many-relationships)

Related

Generics types in VIPER subclassing. Cannot assign value of type 'ChildType' to type 'ParentType<Any>?'

I have VIPER architecture and I want to create parent VIPER search component that works with generic type. The idea is simple so I have search bar and I have table view. For example I can show drinks or foods names in table view. Depends on which generic data type I specified I want to show or foods or drinks.
I found very good example that solves my issue with generic view controllers. But I want to create something similar for swift generic VIPER architecture.
I will skip describing all VIPER classes like (Router, Interdictor and etc).
So I have parent view controller:
BaseSearchViewController: UIViewController {
var presenter: BaseSearchPresenter<Any>?
}
and child:
FoodSearchViewController: BaseSearchViewController {
}
This is a parent presenter specified in BaseSearchViewController
class BaseSearchPresenter<T> {
var items [T]
}
also I have child food search presenter with a specific type Food I would like to display in my table:
class FoodSearchPresenter: BaseSearchPresenter<Food> {
}
When I try to configure my VIPER:
let viewController = FoodSearchViewController(...) // init VC
let presenter = FoodSearchPresenter()
viewController.presenter = presenter // assigning child FoodSearchPresenter instance to a BaseSearchViewController presenter variable leads to this error:
Cannot assign value of type 'FoodSearchPresenter' to type 'BaseSearchPresenter<Any>?'
Here is a repo with the issue.
I left comments about how to attack this problem, but to your specific case, the answer is that BaseSearchViewController needs to be generic.
class BaseSearchPresenter<T> {
var items: [T] = []
}
// BaseSearchViewController must be generic in order to vary its Element
class BaseSearchViewController<Element>: UIViewController {
var presenter: BaseSearchPresenter<Element>?
}
struct Food {}
class FoodSearchPresenter: BaseSearchPresenter<Food> {}
class FoodSearchViewController: BaseSearchViewController<Food> {}
let viewController = FoodSearchViewController()
let presenter = FoodSearchPresenter()
viewController.presenter = presenter
To my point about starting concrete, I mean start with FoodSearchViewController as its own thing. Don't inherit from anything except UIViewController. Then build a second view controller that would make sense in this app and would need something similar. Then extract the commonality between them. This will tend to drive you in the right direction.

Downcast set of type X that are subclass of of Y

Note: although the question has a CoreData example, it's not related to CoreData, it's just an example
We are working on a Swift project with CoreData as a caching layer.
We make use of Notifications in our mainViewController a lot to listen to the changes after our NSManagedObjectContext has new changes.
This is working great until we added new entities with the following hierarchy:
Entity Vehicle is a base class with some attributes.
Entity Car is a subclass of Vehicle with specific attributes and a toMany relationship to Human entity.
Entity Human is a base class with specific attributes, and it has a relationship the Car.
The problem is in the following:
when a new Car object is added, the notification fires, and in the mainViewController, we need to check if it's of type Car, like this:
if let insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<Car> {
print("we have some cars") // this will never execute
}
The type downcast Set<Car> will never evaluate to true because the Set has elements of type Car and also Human.
What I want:
Check if the Set has NSManagedObject subclass of type Car or Human as I downcast it.
What I tried to do:
downcast it to NSManagedObject, and check if the Set contains Car by the adding the following where condition:
insertedObjects.contains(Car), but it has a compile-time error:
Cannot convert value of type '(Car).Type' to expected argument type 'NSManagedObject'
Let me know if you have any question instead of just downvoting.
Not sure about the type casting (I think I remember doing it the same way and it worked, although it was with an array), but checking if there is a car in the set is different:
set.contains { (element) -> Bool in
return element is Car
}
Or shorter (more concise) version of the same call:
set.contains(where: { $0 is Car })
First downcast the inserted object to Set<NSManagedObject>.
To check if any car has been inserted, use
if let insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject> {
if insertedObjects.contains(where: { $0 is Car }) {
print("we have some cars")
}
}
To get the inserted car objects as a (possibly empty) array,
use flatMap():
if let insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject> {
let insertedCars = insertedObjects.flatMap { $0 as? Car }
}
Your approach
if insertedObjects.contains(Car)
does not compile because
func contains(_ member: Set.Element) -> Bool
expects an instance of the element type as argument.
As shown above, you can use the predicate-based variant
func contains(where predicate: (Element) throws -> Bool) rethrows -> Bool
instead.

How to set a NSManaged variable?

I have a NSManagedObject class with two relationships: courseAand courseB.
These relationships should be represented in a dynamic variable. How is it possible to change this variable from outside the class?
#objc(Universtity)
public class Universtity: NSManagedObject {
dynamic var name: String {
get {
let name = self.courseA?.name
return name!
}
}
}
For example from within a ViewController like University.name = University.courseB.name ?
I was thinking about a Notifikation, but this seems maybe a little more complicated as it could be.
And if there is no other way, how should I implement the observer inside the University class?
Thank you for every idea.
Looking at your code, you have declared a "computed" or "ready-only" variable. This is a variable whose value comes from another variable or combination of variables.
I can't see your data model, so it's not clear if you have defined a name parameter in the Core Data model. Regardless, if you have the logic is somewhat confused, because the getter you have defined means any value it may hold would be ignored anyway. You would need to define a setter to set self.courseA.name if you want to ensure the value can be written to. You don't need to worry about key-value coding notifications, because they will be triggered by the Core Data Managed Object.
public class Universtity: NSManagedObject {
dynamic var name: String {
get {
let name = self.courseA?.name
return name!
}
set(newValue) {
courseA!.name = newValue
}
}
}
Also the pattern you have used to force unwrap a non-optional value in your getter isn't optimal. I haven't edited this because that is another discussion, but I would suggest asking yourself the question am I sure why I am doing this? for every "?" and "!" you use.

Swift Generics - Changing Values and Passing Between View Controllers

I'd like to use a variable of type Any in order to pass different classes to a child view controller. For example, I might have Table, Chair and Plate objects. In my child view controller, I'd like to change the value of one of their properties (e.g. Table.legs was 4, change that to 3), and for the parent view controller to be able to read that from the child VC. I'll use a Protocol to update the parent VC that can pop the child after reading the updated object.
In order to work out how the passing of generics might work, I wrote this code in a playground:
class Table {
var legs: Int
var material: String
init(legs: Int, material: String) {
self.legs = legs
self.material = material
}
}
var anObject: Any?
// set up the Table
let aTable = Table(legs: 4, material: "Oak")
// set anObject to be the Table
anObject = aTable
// get the object and change it
let bTable = anObject as! Table
bTable.legs = 3
// get the original object and cast it as a Table
let cTable = anObject as! Table
print(cTable.legs) // prints 3
I believe from this, I should be able to do what I describe above without any issues, because the original object reference (anObject) is updated whenever I update a variable referencing it.
My question is this - are there any pitfalls I should be aware of when adopting this approach? It appears that rather than creating a copy of an object, swift will always create a pointer to the original object; are there any situations when that does not hold true?
Appologies if this is seen as a fairly basic question, but this is all fairly new to me - many thanks in advance!
Class are reference types as you noticed, if you assign an instance of the class to a variable, it keep the reference (the pointer in memory) to the instance and not the value copy.
Struct are value types, if you copy the instance of the structure to another variable, it's just copied to the variable.

What is the difference between a property and a variable in Swift?

From a few initial tutorials, I see that properties belong to a Class and are essentially 'global variables' as used in the C++ world (coded in this years ago). I also see variables as more of a 'local' entities only used / storing information within a method.
Then I came across this Quora thread: https://www.quora.com/Apple-Swift-programming-language/What-is-the-difference-between-a-property-and-a-variable
Now I see properties being able to execute code associated with their invocation. This is very cool, but also opened up a whole bunch of other questions for me.
Are there other simple and clear ways to remember the distinction between a property and a variable?
Properties belong to an object, whereas variables do not. A variable can be declared without having to be associated with a particular class, or other object. A property must be associated with a particular object (i.e.: a class, enum, or struct)
Local variables are just things that you work with. You have full control over these, and if you change a variable in a function, nothing outside of your function is ever gonna know. If I write a framework and you use it, and I decide to change something about a function's local variables, your app that uses my framework will keep working just as if nothing changed.
Classes, on the other hand, describe a contract. When you use a class, you have access to everything they publicly advertise. This means that if I write a framework and you use it, if I ever change or remove a public member on a class, your code will break if you were previously using that member.
For this reason, in many languages, it's bad practice to mark instance variables as public. Instance variables having no logic attached, if I want at some point to trigger something when a field is changed or if I want to remove the field entirely (and instead report a value in a sub-object or something), then I'm stuck with changing the public contract (turning the field in a pair of get/set methods, for instance), and possibly breaking your code.
Swift makes properties an indirection for this reason. Swift properties can be treated as dumb values for the most part, but if you ever need to change from a stored value to a computed value or something, you can do it without changing your class's interface. That way, you don't break existing code that relies on the property.
Swift variable, constant, Property
[Swift types]
variable - named storage of address. Every variable has a type which defines a memory size, attributes and behaviours
Swift variable and constants
constant is a variable but can not be modified after definition.
//definition
var <name> = <initial_value>
//type annotation
var <name>: <Swift_type> [= <initial_value>] // [] is optional
//var - variable
var myVariable1 = 11
var myVariable2: Int
myVariable2 = 12
//let - constant
let myConstant1 = 21
let myConstant2: Int
myConstant2 = 22
Global and local variable
Global variable is a variable which is defined out of function, class.
Local variable is: variable inside a type context(class, struct, enum)[About], inside a function, function parameter
Property
property - associate value with a type context. It is a variable + bounded getter/setter. It has field syntax but uses methods(getter/setter) under the hood.
Stored properties and computed properties
They can belong to instance(instance property) or type(type property):
Stored property (class, structure)
Computed property (class, structure, enum)
Stored property - is a local variable -> variable inside a type context. Swift stored property does not support instance variable like Objective-C.
variable stored properties - var
constant stored properties - let
It supports property observers (willSet, didSet)
Computed property - provide getter and optional setter to calculate a value every time
public class Person {
var firstName = "John"
var lastName = "Wick"
var fullNameComputedProperty: String {
get {
return "\(firstName) \(lastName)"
}
//optional
set {
let arr = newValue.split(separator: " ")
firstName = String(arr[0])
lastName = String(arr[1])
}
}
var addressStoredProperty: String {
//Property Observers
willSet {
print("old address:\(addressStoredProperty)")
print("new address:\(newValue)")
//addressStoredProperty is not updated yet
}
didSet {
print("old address:\(oldValue)")
print("new address:\(addressStoredProperty)")
}
}
}
Lazy Stored property
Property is calculate during first access to it(on demand)
only var lazy because let must have a value during initialization
Init/customize stored property by closure
Official doc
You are able to init/setup/customise a stored property with a help of closure
() at the end executes the closure immediately and assign a value to stored property(calculate and return a value).
in initializing case it is not possible to access to any instance variable or function because it has not initialized yet
in initializing case it will be executed only once for every object or if you use static - once for the class[Example]
Examples
func testStoredPropertyWithClosure() {
class ClassA { }
class ClassB {
static let staticStoredProperty: ClassA = {
//is called only when you access to it like ClassB.staticStoredProperty
print("init staticStoredProperty")
return ClassA()
}()
var storedProperty: ClassA = {
print("init storedProperty")
//self.foo() //Error: Class declaration cannot close over value 'self' defined in outer scope
return ClassA()
}()
func foo () {
storedProperty = {
print("customize storedProperty")
return ClassA()
}()
}
}
let b = ClassB()
b.foo()
ClassB.staticStoredProperty
}
closure stored property vs Computed property
closure stored property is called once and can be changed after initialization(if it is var)
Computed property is calculated every time when it is called
[Java variable, property...]

Resources