I have two models that I am trying to subclass in swift using RealmSwift
Model 1: Users
#objcMembers class User: Object, Codable {
dynamic var id: Int = 0
dynamic var username: String = ""
dynamic var email: String = ""
dynamic var counts: Counts = Counts(follows: 0, followed_by: 0)
convenience init(id: Int, username: String, email: String, counts: Counts) {
self.init()
self.id = id
self.username = username
self.email = email
self.counts = counts
}
}
class Counts: Codable {
let follows: Int
let followed_by: Int
init(follows: Int, followed_by: Int) {
self.follows = follows
self.followed_by = followed_by
}
}
Here I created a model which inherits codable because I am using Swift 4 new decoding method. This model works fine. I can go add a user and read it fine. So doing this works fine:
let newUser = User(id: 1, username: "user1", email: "user1#gmail.com", counts: Counts(follows: 100, followed_by: 500000000))
RealmService.shared.write(newUser)
Now I am trying to create a model called Friend which has a field that inherits from User
Model 2: Friend
#objcMembers class Friend: Object {
dynamic var user: User = User(id: 0, username: "", email: "", counts: Counts(follows: 0, followed_by: 0))
dynamic var dateAdded: Date = Date(timeIntervalSinceNow: 0)
convenience init(user: User, dateAdded: Date = Date(timeIntervalSinceNow: 0)) {
self.init()
self.user = user
self.dateAdded = dateAdded
}
}
When I do this and load up my app I get a SIGBART and it highlights my initialization of realm : let realm = RealmService.shared.realm
When I remove my Friend Model the app works fine.
How do you properly subclass in Realm?
You cannot declare properties as normal Swift properties whose types are other Realm subclasses. What you are looking for is a many-to-one relationship.
Many-to-one relationships have to be declared optionals and you can't provide a default value
#objcMembers class Friend: Object {
dynamic var user: User?
dynamic var dateAdded: Date = Date()
convenience init(user: User, dateAdded: Date = Date()) {
self.init()
self.user = user
self.dateAdded = dateAdded
}
}
Unrelated, but when you want to create a Date object representing the current time, just call the default, empty initializer of Date, it will do that for you and it's much shorter than Date(timeIntervalSinceNow: 0).
Related
I'm pretty new to xCode so this could have an obvious answer.
I've just learned about environment objects and created the following one:
import SwiftUI
class Data: ObservableObject {
#Published var types = [Type]()
#Published var customers = [Customer]()
#Published var templates = [SubscriptionTemplate]()
#Published var subscriptions = [Subscription]()
#Published var giftCodes = [Giftcode]()
}
As you can see the object contains an array of objects. One of these is a customer array. The customer object looks like this:
import SwiftUI
class Customer: Identifiable, Codable{
var id: Int
var firstname: String
var lastname: String
var address: String
var plz: Int
var place: String
var credit: Int
init(id: Int, firstname: String, lastname: String, address: String, plz: Int, place: String, credit: Int) {
self.id = id
self.firstname = firstname
self.lastname = lastname
self.address = address
self.plz = plz
self.place = place
self.credit = credit
}
}
extension Customer: Equatable {
static func == (lhs: Customer, rhs: Customer) -> Bool {
return lhs.id == rhs.id
}
}
In my project, I implemented an API call to update the customer. This works like a charm, but after updating, I also want to fetch the customer objects with the following method:
API().fetchCustomers { (customers) in
self.data.customers = customers
}
After updating an object this doesn't work. The environment object doesn't update, but after creating a new object or fetching the data initial, it works.
What is the difference between the update and the create / fetch?
Make Customer a value type (ie. struct):
struct Customer: Identifiable, Codable{
var id: Int
// ... other code
How do I initialize an empty object in swift? This is what I have but it wants the parameters too
var userInfo:User = User()
init(email: String, isVerified: String, profileImageURL: String, reputation: String, twitterHandle: String, userName: String) {
self._email = email
self._isVerified = isVerified
self._profileImageURL = profileImageURL
self._reputation = reputation
self._twitterHandle = twitterHandle
self._userName = userName
}
Create the class/struct with optional properties like this
struct Employee {//struct or class
var name: String?
var number: String?
var position: String?
}
Then you can create an object without any value, with some value
let emp = Employee()
let emp = Employee(name: nil, number: nil, position: nil)
let emp = Employee(name: "abc", number: nil, position: "xyz")
By creating a init method with default values parameter can be ignored while creating an object
struct Employee {
var name: String?
var number: String?
var position: String?
init(name: String? = nil, number: String? = nil, position: String? = nil) {
self.name = name
self.number = number
self.position = position
}
}
let emp = Employee()
let emp = Employee(name: "abc", number: "124", position: "xyz")
let emp = Employee(name: "abc", position: "xyz")
let emp = Employee(number: "124")
let emp = Employee(name: "abc", number: "124")
I assume you are creating empty User objects so that the information can be filled in later on. There are two problems I can see with this: one, object properties will all have to be variables and second, it's easy to make mistakes and pass the wrong information since the object does not always correspond to a real entity.
A better approach would be to define a set of parameters that are mandatory for a User object to be defined, and let every other parameters either be optional parameters, or give them default values. For example, if we choose username and email to be mandatory, and let profile image be optional, and isVerified to have a default value of false:
class User {
var userName: String
var email: String
var isVerified: Bool
var profileImageURL: String?
init(userName: String, email: String, isVerified: Bool = false) {
self.userName = userName
self.email = email
self.isVerified = isVerified
}
}
I am trying to create nested queries in realm. I will paste my models in and explain what I mean.
Parent Model
#objcMembers class Group: Object {
dynamic var uuid: String = ""
dynamic var admin: User?
convenience init(uuid: String, admin: User) {
self.init()
self.uuid = uuid
self.admin = admin
}
}
Child Model
#objcMembers class Message: Object {
dynamic var uuid: String = ""
dynamic var group: Group?
dynamic var message: String = ""
convenience init(uuid: String, group: Group, from: User, message: String) {
self.init()
self.uuid = uuid
self.group = group
self.message = message
}
}
What I am trying to do is filter messages that are in a group with uuid x
All the answers I have seen are outdated.
What I have right now is
let result = RealmService.shared.realm.objects(Message.self).filter("group.uuid =
0E81CDEF-B63F-4DBE-9900-B486D40F4EC9")
What is the correct way of doing this?
Figured it out:
let result = RealmService.shared.realm.objects(Message.self).filter("group.uuid = '2C5E1738-1167-40CB-BE43-C415FD5E6E5D'")
Queried value has to be wrapped in ''
I would like to reduce the length of init method.
struct Person {
var id: Int
var firstName: String
var lastName: String
var vehicle: String
var location: String
var timeZone: String
init (id: Int, firstName: String, lastName: String, vehicle: String, location: String, timeZone: String ) {
self.firstName = firstName
self.lastName = lastName
self.vehicle = vehicle
self.location = location
self.timeZone = timeZone
}
}
Below is an instance of Person I am creating. I have to pass in the value of every single variable inline.
let person = Person(id: 22, firstName: "John", lastName: "Doe", vehicle: "Chevy", location: "Dallas", timeZone: "CST")
Question: How can I shrink the length of init? In Obj-C I used to create a data model class. Populate it's variables and then pass the entire class, reducing the length of the init method.
i.e.
Person *person = [Person new];
person.id = 22;
person.firstName = "John";
person.lastName = "Doe";
person.vehicle = "Chevy";
person.location = "Dallas";
person.timeZone = "CST"
Person *person = [Person initWithPerson:person];
What's an equivalent way in Swift to reduce the length of init without having to initialize every single variable inline? I know tuples is one way, is there any other best practice?
Just remove the initializer!
struct Person {
let id: Int
let firstName: String
let lastName: String
let vehicle: String
let location: String
let timeZone: String
}
Now you can use the memberwise initializer
Person(
id: 87112,
firstName: "Walter",
lastName: "White",
vehicle: "2004 Pontiac Aztek",
location: "Albuquerque",
timeZone: "UTC-07:00"
)
Structure types automatically receive a memberwise initializer if they do not define any of their own custom initialisers.
The Swift Programming Language
DO NOT use var
As you can see I replaced var with let.
Unless you need to change some properties of a Person after the value has been created, I suggest you to use let. Otherwise you are free to use var. This way the compiler will prevent unwanted changes.
DO NOT use Optionals
I don't know the business logic of your app, however if a Person must have all that 6 properties always populated, don't make them optionals. Otherwise every time you need to use a Person value the compiler will force you to check if that optional has a value.
DO NOT use Implicitly Unwrapped Optionals
Seriously. There are a few cases where they are useful and a model value is not one of them
Using a struct you actually don't need an initializer
struct Person {
var id : Int?
var firstName: String?
var lastName: String?
var vehicle: String?
var location: String?
var timeZone: String?
}
var person = Person()
person.id = 22
person.firstName = "John"
person.lastName = "Doe"
person.vehicle = "Chevy"
person.location = "Dallas"
person.timeZone = "CST"
You can do the same with non-optionals
struct Person {
var id = 0
var firstName = ""
var lastName = ""
var vehicle = ""
var location = ""
var timeZone = ""
}
Consider also the benefit of an initializer to declare (read-only) constants
struct Person {
let id : Int
let firstName : String
let lastName : String
let vehicle : String
let location : String
let timeZone : String
}
In this case you have to use the implicit memberwise initializer.
let person = Person(id: 22, firstName: "John", lastName: "Doe", vehicle: "Chevy", location: "Dallas", timeZone: "CST")
Like it was mentioned in the comments, an initializer will be created for you, and it'll look like this:
Person(id: Int?, firstName: String?, lastName: String?, vehicle: String?, location: String?, timeZone: String?)
However, you can also do this:
var person = Person()
person.id = 100
person.firstName = "Name"
...
Like you used to do in ObjC. Note that person was declared as var, because if it was declared as let, you wouldn't be able to mutate it.
Having array of employee object, called employees and now I want to create another array called filteremployees which is having only id and date of birth value.
Using
let filteremployees = employee.map({ $0.id})
I can get array which contains only id value but i want to have id as well dateOfBirth
class Employee {
var id: Int
var firstName: String
var lastName: String
var dateOfBirth: NSDate?
init(id: Int, firstName: String, lastName: String) {
self.id = id
self.firstName = firstName
self.lastName = lastName
}
}
Try this:
let employees : [Employee] = ...
let list: [(Int, NSDate?)] = employees.map { ($0.id, $0.dateOfBirth) }
You must explicitly declare the type of list otherwise you get this error from the compiler
Type of expression is ambiguous without more context.
Tested with Xcode 7 Playground and Swift 2.0.
Hope this helps.
You could try using the same map method and returning a tuple of your expected values:
let filter employees: [(Int, NSDate?)] = employee.map({ ($0.id, $0.dateOfBirth) })
Alternatively, and I think this is a better solution, create a new value type and create that with only your required values
struct FilteredEmployee {
let id: String
let dateOfBirth: NSDate?
init(employee: Employee) {
id = employee.id
dateOfBirth = employee.dateOfBirth
}
}
And then you can map the initialiser over the array
let filteremployees = employee.map { FilteredEmployee($0) }