I am trying to follow the Model View ViewModel format for SwiftUI, but am running into an issue with UUID. I have an object of type TimeCode that has an attribute id of type UUID (TimeCode is created in xcdatamodels as a CoreData model). In order to create a TimeCodeViewModel object, I need to assign the id attribute in TimeCodeViewModel with the same UUID from the original TimeCode object. I therefore created this class definition to do so:
class TimeCodeViewModel: Identifiable {
var id: UUID
var fullName = ""
init(timeCode: TimeCode) {
self.id = timeCode.id
self.fullName = timeCode.fullName
}
// class methods
}
However, I get a compile time error saying that UUID is a get-only property. This makes sense, since you shouldn't be able to reassign the unique ID of an object to a different object, but in this case I am actually trying to describe the same object. Is it possible to assign self.id with the same UUID?
I guess another approach could be to make the UUID a string and then assign it to the view model, but is it then possible to convert the string back into a UUID? For example, I want to fetch the original TimeCode from CoreData using the UUID from the TimeCodeViewModel so I can save edits to other attributes of the TimeCode.
It would be interesting to see how the TimeCode Class looks like. I don't think that the id is set correctly. If you want a unique identifier as a String add the following to generate one:
var id: String = UUID().uuidString
You can share the string and therefore reference to the same object.
EDIT:
Regarding the new information, changing the class to the following might be an idea:
class TimeCodeViewModel: Identifiable {
var id: UUID {
return timeCode.id
}
var fullName = ""
private var timeCode: TimeCode
init(timeCode: TimeCode) {
self.timeCode = timeCode
self.fullName = timeCode.fullName
}
// class methods
}
Related
import FirebaseFirestoreSwift
import Firebase
// I created this object so that i can map the users data and access it threw this object. example I could say thigs like user.username
// The decodable protocall will read the data dictonary and looks for the exact name for the keys/property names I have listed in the data dictonary, this makes life easier when working with objects and downloading information from an api
struct User: Identifiable, Decodable {
// Im able to delete the uid field out of firebase because this will read the documentID from firebase and store it in this id property, so that I dont have to dupicate that data in the actual body of the object
#DocumentID var id: String?
let username: String
let fullname: String
let profileImageUrl: String
let email: String
let stats: UserStats
// This is a computed property saying if the currently logged in user's id is equal to the id on my object (#DocumentID)
var isCurrentUser: Bool { return Auth.auth().currentUser?.uid == id }
}
struct UserStats: Decodable {
let followers: Int
let following: Int
}
Add ? at the end of each variable.
#FirestoreQuery does little error handling when it comes to decoding.
Also, if you are not using #FirestoreQuery use do try catch instead of try?
In the following example code, I create a struct and a class with similar members. With the struct I can initialize an instance by any number of the members into its constructor, and the rest will default. With a class, I have to specify every version of init I want to use. Seems like I must be missing some way to do it with a class though -- is there any way to do this? It looks like in 2016 there was not, but I know Swift has changed a ton since then. I'm hoping there is a way now.
import Foundation
struct FooStruct {
var id: UUID = UUID()
var title = ""
}
// these statements both work fine
let a = FooStruct(id: UUID())
let a2 = FooStruct(title: "bar")
class FooClass {
var id: UUID = UUID()
var title = ""
}
// these statements both give the same error:
// Argument passed to call that takes no arguments
let b = FooClass(id: UUID())
let b2 = FooClass(title: "bar")
What you are seeing with Structure types is what is called a memberwise initializer. Swift does not provide one of these to Class types because of the more complex way Classes are initialized, due to their inheritance model.
Swift provides a default initializer—different than a memberwise initializer—for any structure or class that provides default values for all of its properties and doesn’t provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.
you could just use this:
class FooClass {
var id: UUID = UUID()
var title = ""
init(id: UUID = UUID(), title: String = ""){
self.id = id
self.title = title
}
}
and this will work:
let b = FooClass(id: UUID())
let b2 = FooClass(title: "bar")
I have an app that stores User (UserModel) Friend list. if a friend clicks one user, its type is the same type (UserModel). In Swift it wouldnt allow using the model recursively, giving me this error:
"Value type 'OwnerModel' cannot have a stored property that recursively contains it"
import Foundation
struct OwnerModel: Codable {
var ownerId: Int
var ownerEmail: String
var ownerUserName: String
var ownerCommonName: String
var ownerBirthDate: String
var ownerCountry: String
var ownerBdayReminderId: Int
var ownerIsVerified: Bool
var ownerIsOnline: Bool
var ownerIsEventGreeted: Bool
var ownerIsBirthdayGreeted: Bool
var ownerAllowGreeting: Bool
var ownerFriends: OwnerModel
}
Is there a way I can reuse the OwnerModel under ownerFriends?
This can't work because structs are value types. So each OwnerModel would have to have a OwnerModel inside it, which would have to have an OwnerModel inside it, which would have to have an OwnerModel inside it.... This can never resolve. Since you've marked this Codable, try to write the JSON you expect to encode this to.
That said, ownerFriends seems plural, which would suggest [OwnerModel], and that's not a problem, since you could have zero of them:
struct OwnerModel: Codable {
...
var ownerFriends: [OwnerModel]
}
Remember again, however, that structs are value types. So each OwnerModel is just a value. It's not a reference to any other object. If you want to refer to other owners, you may want to store IDs rather than the actual object (or use classes in order to create references).
I am searching how to get not managed property names and types of a NSManagedObject subclass.
here is few sample code to help me to ask my question :
#objc(Operation)
public class Operation : NSManagedObject {
#NSManaged var name: String
#NSManaged var amount: NSNumber
}
#objc(Account)
public class Account: NSManagedObject {
#NSManaged var bic: String
#NSManaged var number: String
#NSManaged var operations: Set<Operation>
#NSManaged var servicesSubscriptions: Set<ServiceSubcription>
// and more.
}
extension Account
{
public var lastOperation : Operation {
get
{
return self.operations.last
}
set(value)
{
self.operations.insert(value)
}
}
}
I have found many ways to get property names using reflect() function. reflect() do not work with NSManagedObject at all. (like this simple one)
edit
I have found examples with class_copyPropertyList function, that retrieve correctly property names, but don't found yet how to get types. Thank to Tom Harrington comment. (see that sample)
I have found many ways to get Attributes (or relations) of managed objects using NSEntityDescription. (like this one). Which work and get back bic and number, but not lastOperation.
edited
updated code sample to match better to reality
So my question is :
How to get back my lastOperation property, and its type, dynamically at run time ?
edit, what i am trying to do
I am parsing json, dnamically using reflection.
I need the type (or type name) of a property knowing only its name (i have "lastOperation", and need to get back Operation, or "Operation"). Once i get the type i can instanciate an object, then populate its own properties, using same mechanism (recursively).
Thank you for any help
When you get the list of properties using class_copyPropertyList, you can iterate through the list to look at each property in turn:
var propertyCount : UInt32 = 0
let properties = class_copyPropertyList(Account.self, &propertyCount)
for var i=0; i<Int(propertyCount); i++ {
let property = properties[i]
let propertyName = String(UTF8String: property_getName(property))
....
}
The type of each property is contained in one of the property attributes, as a string:
let propertyType = property_copyAttributeValue(property, "T")
let propertyTypeString = String(UTF8String: propertyType)
For your lastOperation property the string will look something like #\"Operation\". You'll have to clean up that string a little to get Operation.
I wrote a blog post a while ago describing something similar to what you're trying to do. The code is in Objective-C but all the functions, methods, etc are the same.
There is no way that I know of when it comes to a NSManagedObject. However, I would suggest creating title as a transient property inside of your model and then it will show up as part of the entity description.
As Realm doesn't support optionals, which are not Object subclasses, I'm trying to wrap a string into StringObject:
final class StringObject: Object {
dynamic var value: String = ""
convenience init?(_ value: String?) {
self.init()
if let value = value {
self.value = value
} else {
return nil
}
}
}
And use it like this:
final class Person: Object {
dynamic var firstName: String = ""
dynamic var lastName: StringObject? // can be optional
}
But this solution has a nasty side effect: as StringOptional values will be stored in their own table within the database, there will be countless duplicate values every time a StringObject is created. I tried making StringObject's value a primary key, but upon Person object's creation I receive an error:
Can't set primary key property 'lastName' to existing value 'Doe'
Which means that internally Realm does not upsert relationships.
Is there a better way to store optionals?
We actually released a beta of Realm that had support for optional strings and data properties, and it will hopefully be released more widely soon! In the meantime, you can try out the beta at https://github.com/realm/realm-cocoa/issues/628#issuecomment-106952727.