Best way to implement 1-many in CloudKit? - one-to-many

I am trying to design a 1-many relationship between Advisors and Students in CloudKit. My problem is that in my model classes, I need Student to be able to refer to a Teacher -- so I have a teacher property of type Teacher. But in CloudKit, to establish the relationship, I need a CKRecord.Reference. So now I have two properties, each of which is relevant in different contexts.
Is there some standard way of doing this? I'm used to Backendless, that hides most of this detail, but I'd like to try Apple's approach so I don't need to worry about user management.
Thanks for any info ...
class Teacher : Equatable, CKRecordValueProtocol {
var id: Int
var lastName: String
var firstName: String
var students: [Student]
// other stuff omitted
}
class Student : Equatable {
var id: Int!
var lastName: String!
var firstName: String!
var teacher: Teacher! // so we can point to the teacher in our model
var teacherReference: CKRecord.Reference! // so we can point to records in CloudKit
var gpa: Double!
// other stuff omitted
}

Related

Kotlin get all property value of data class

Is there a syntactic sugar in Kotlin to iterate on each field/property value of a data class?
Sample:
data class User(
var firstName: String = DEFAULT_VALUE_STRING,
var middleName: String = DEFAULT_VALUE_STRING,
var lastName: String = DEFAULT_VALUE_STRING
)
val user = User()
Then check if any of the property's value is empty, considering all of it is String data type with something like this
if (user.properties.any{ it.isBlank() }) {
// TODO ...
}
Probably the closest you'll get is checking all the values of all the generated componentX() functions (since they're only created for the constructor parameter properties, the "data" in a data class) but yeah that involves reflection.
If I were you, I'd create an interface with a properties property and make all your data classes implement that - something like this:
import kotlin.reflect.KProperty0
interface HasStringProperties {
val properties: List<KProperty0<String>>
}
data class User(
var firstName: String = "",
var middleName: String = "",
var lastName: String = ""
) : HasStringProperties {
override val properties = listOf(::firstName, ::middleName, ::lastName)
}
fun main() {
val user = User("Funny", "", "Name")
println(user.properties.any {it.get().isBlank()})
}
So no, it's not automatic - but specifying which properties you want to include is simple, and required if you're going to access it on a particular class, so there's an element of safety there.
Also, because you're explicitly specifying String properties, there's type safety included as well. Your example code is implicitly assuming all properties on your data classes will be Strings (or at least, they're a type with an isBlank() function) which isn't necessarily going to be true. You'd have to write type-checking into your reflection code - if you say "I don't need to, the classes will only have String parameters" then maybe that's true, until it isn't. And then the reflection code has to be written just because you want to add a single age field or whatever.
You don't actually have to use property references in Kotlin either, you could just grab the current values:
interface HasStringProperties {
val properties: List<String>
}
data class User(
var firstName: String = "",
var middleName: String = "",
var lastName: String = ""
) : HasStringProperties {
// getter function creating a new list of current values every time it's accessed
override val properties get() = listOf(firstName, middleName, lastName)
}
fun main() {
val user = User("Funny", "", "Name")
println(user.properties.any {it.isBlank()})
}
It depends whether you want to be able to reference the actual properties on the class itself, or delegate to a getter to fetch the current values.
And of course you could use generics if you want, list all the properties and use filterIsInstance<String> to pull all the strings. And you could put a function in the interface to handle a generic isEmpty check for different types. Put all the "check these properties aren't 'empty'" code in one place, so callers don't need to concern themselves with working that out and what it means for each property

How to assign value to NSManaged object in Swift?

I need to write test case and in the test case, I need to mock an object of the following class and put them into an array. e.g. moviesArray = [MovieCoreData]. I am wondering how I can instantiate this?
#objc(MovieCoreData)
class MovieCoreData {
static var genre: Genre = "Comedy"
#NSManaged var actor: String
#NSManaged var released: Bool
#NSManaged var name: String?
public static func mapping() -> [FieldName: String] {
return [
"actor": "actor",
"released": "released",
"name": "name"
]
}
}
For example, I tried to write
moviesArray = [MovieCoreData(actor: "Johnny Depp", released: True, name: "Pirate Captain"]
and
moviesArray.first.actor = "Johnny Depp"
moviesArray.first.released = "true"
moviesArray.first.name = "Pirate Captain"
none of them worked, I admit I don't know much about Objective-C and NSManaged, can someone tell me how to create an array of MovieCoreData with fake data?
The #NSManaged attribute belongs to Core Data.
The class must be a subclass of NSManagedObject and instances must be created with regard to the NSManagedObjectContext to take advantage of its functionality

Re-use mapping code for immutable data class in Kotlin

Updated: added some clarifications from the comments
I would like to use the same 'mapping' code for the primary constructor and copy() method of an immutable data class. How can I do this without creating an empty object first, and then using copy() on it?
The issue with how it is now is that if I add a new attribute with default value to Employee and EmployeeForm it would be easy to only add it in one of the two mapping functions and forget about the other (toEmployeeNotReusable / copyEmployee).
These are the data classes I'd like to map between:
#Entity
data class Employee(
val firstName: String,
val lastName: String,
val jobType: Int,
#OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL], fetch = FetchType.EAGER)
private val _absences: MutableSet<Absence> = mutableSetOf(),
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0 // prevents #Joffrey's answer from working
) {
init {
_absences.forEach { it.employee = this }
}
val absences get() = _absences.toSet()
fun addAbsence(newAbsence: Absence) {
newAbsence.employee = this
_absences += newAbsence
}
#Entity
#Table(name = "absence")
data class Absence(
// ... omitted fields
) {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "employee_id")
lateinit var employee: Employee
}
}
data class EmployeeForm(
var firstName: String = "",
var lastName: String = "",
var jobType: Int = 0
) {
// not reusable
fun toEmployeeNotReusable(): Employee {
return Employee(firstName, lastName, jobType)
}
// works but hacky
fun toEmployee(): Employee {
return copyEmployee(Employee("", "", 0))
}
fun copyEmployee(employee: Employee): Employee {
return employee.copy(
firstName = firstName,
lastName = lastName,
jobType = jobType
)
}
}
While mutability would be fine, in my case, I'd be interested to know how this would be possible.
One way to avoid listing the attributes 4 times would be to declare Employee as an interface instead, and use the "mutable" version, the form, as the only data class implementing it. You would have the "read-only" view using the interface, but you would technically only use the mutable instance behind the scenes.
This would follow what Kotlin designers have done for List vs MutableList.
interface Employee {
val firstName: String
val lastName: String
val jobType: Int
}
data class EmployeeForm(
override var firstName: String = "",
override var lastName: String = "",
override var jobType: Int = 0
): Employee {
fun toEmployee(): Employee = this.copy()
fun copyEmployee(employee: Employee): Employee = this.copy(
firstName = firstName,
lastName = lastName,
jobType = jobType
)
}
However, this implies that the form has all fields of an employee, which you probably don't want.
Also, I would personally prefer what you had done in the beginning, listing twice the field would not be a problem, just write tests for your functions, and when you want to add functionality, you'll add tests for that functionality anyway.
You should be able to do this using reflection: check list of properties in Employee and EmployeeForm, call the constructor by the matching names (using callBy to handle default parameters). The drawback, of course, is that you won't get compile-time errors if any properties are missing (but for this case, any test would probably fail and tell you about the problem).
Approximate and untested (don't forget to add the kotlin-reflect dependency):
inline fun <reified T> copy(x: Any): T {
val construct = T::class.primaryConstructor
val props = x::class.memberProperties.associate {
// assumes all properties on x are valid params for the constructor
Pair(construct.findParameterByName(it.name)!!,
it.call(x))
}
return construct.callBy(props)
}
// in EmployeeForm
fun toEmployee() = copy<Employee>(this)
You can make an equivalent which is compile-time checked with Scala macros, but I don't think it's possible in Kotlin.

Non null relationships in primary constructor of entity

I'm creating application with Spring Data Neo4j and Kotlin. I use standard kotlin way to declare entities (class with primary constructor). Everything worked fine until I wanted to create simple, one-to-many and mandatory relationship between my entities. When I'm calling .findAll() on my repository I get Parameter specified as non-null is null: method ...model.Campaign.<init>, parameter client.
I tried to call .findAll(depth = 1) to load related entities to my entity but that didn't help.
#NodeEntity
class User(var name: String)
{
#Id #GeneratedValue
var id: Long? = null
}
#NodeEntity
class Campaign(
var name: String,
#Relationship(type = "CLIENT", direction = Relationship.OUTGOING)
var client: User)
{
#Id #GeneratedValue
var id: Long? = null
}
interface CampaignRepository : Neo4jRepository<Campaign, Long>
//...
campaignRepository.save(Campaign("C1", user))
campaignRespository.findAll()
Of course, I can just declare var client: User? as nullable and everything is fine. But, since in my model I will have both mandatory and optional relationships I want to know if there's a way to overcome this.
I found a solution, but not very elegant:
#NodeEntity
class Campaign(
var name: String,
client: User?)
{
#Id #GeneratedValue
var id: Long? = null
#Relationship(type = "CLIENT", direction = Relationship.OUTGOING)
lateinit var client: User
init
{
client?.let { this.client = it }
}
}

Define common properties without inheritance

Is there a way of defining common properties without using inheritance in Kotlin?
For example
If I have two classes that both require an "id" property.
class Dog() {
var id: UUID?
}
class Cat() {
var id: UUID?
}
The general JAVA way to solve this is introduce a super class
class Animal() {
var id: UUID?
}
class Dog: Animal()
class Cat: Animal()
But now "Dog" and "Cat" are of type "Animal". What if I introduce a "Chair" class that also requires a unique identifier.
Essentially what I want to the ability to create a set of properties I can include in a number of different classes for programming convenience only. I don't want all the problems associated with inheritance.
You can, of course, use an interface instead of a base class:
interface HasId {
val id: UUID
}
data class Dog(override val id: UUID) : HasId
data class Cat(override val id: UUID) : HasId
However, the above is still using inheritance. If you have more common properties that would be used in multiple classes it may be a sign that they should be grouped together to form a separate value object e.g.
data class Address(val city: String, val street: String, val country: String)
class Person(val name: String, val address: Address)
class School(val name: String, val address: Address, val studentsCount: Int)
And if you want to treat Person and School uniformly with regards to address property you can still use the interface to denote the common attribute:
interface HasAddress {
val address: Address
}
class Person(val name: String,
override val address: Address) : HasAddress
class School(val name: String,
override val address: Address,
val studentsCount: Int) : HasAddress
It might be possible that delegation will suit your needs:
interface WithId {
var id: Int
}
class IdStorage : WithId {
override var id: Int = 0
}
class Dog(withId: WithId) : WithId by withId {
constructor() : this(IdStorage()) {}
}
class Cat(withId: WithId) : WithId by withId {
constructor() : this(IdStorage()) {}
}
This code is rather verbose, but what it allows you to do is:
Avoid using superclass just for the sake of having id property, which allows you to extend other classes if you need
Usage of interface, which guarantees other pieces of code that your class has id
Allows to move implementation of your properties (or functions) to separate class, hence no need for duplicate code in case of complex property/function implementation
Allows implementing multiple properties/functions in a separate class
As was mentioned in the comments:
interface Animal {
var id: UUID?
}
class Dog: Animal
class Cat: Animal