Is there a way to reuse model within itself? - swift

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).

Related

How to store properties in generic struct?

I have a Codable struct that is part of my app, RemoteData. I’m building a reusable package that will fetch the data and store it in UserDefaults. The data fetching, DataFetcher class has a Codable generic parameter. I am subclassing DataFetcher to pass in RemoteData as the generic param.
// in my app
struct RemoteData: Codable {
var experimentOne: [Variant<[Page]>]
var experimentTwo: [Variant<Bool>]
var experimentThree: [Variant<String>]
}
All of the properties in RemoteData will be arrays of type Variant<T> where T is Codable:
// in my package
public struct Variant<T: Codable>: Codable, VariantProtocol {
public var experimentName: String
public var variantName: String
public var percent: Int
public var value: T
}
I’d like to be able to save this data in UserDefaults. I’d like to perform some filtering on the Variant array to see if this user should see that configuration. I’d like to save the data so that each experiment name is the key and the single variant the user should see is the value rather than the whole array. Although if the whole array is the only option, I’d be ok with that too.
However, since my DataFetcher doesn’t know what the properties are since it is just taking in a generic I don’t think I can do that. My first thought was to create a protocol that RemoteConfig confirms to and that the DataFetcher generic also conforms to.
// in my package, but subclassing in my app to provide url
open class DataFetcher<T: Decodable> {
var remoteConfig: T?
var url: URL
public init(url: String) {
self.url = url
}
func fetchAndSaveData() { ... }
}
That doesn’t work because I then need to specify T in Variant and I will only be able to have Variant arrays of one type.
I’m stuck here and not sure how to move forward.

Is there a way to change the Data Type in Realm Database?

As you can see the image, I totally mess up the Data Type (The red circle). Is there a way to change the Data Type into Integer?
EDIT
I want to change the data type from a String to an Int and I have existing data so I can't start with a fresh realm and just change the var type.
class Item: Object {
#objc dynamic var name: String?
#objc dynamic var itemid: String?
#objc dynamic var cateid: String?
}
I may have misunderstand the question but you appear to have existing data stored as as a String and you want to 'convert' all of those to an Int.
You cannot directly change the type to another type and have the stored data changed as well. If you do, it will be flagged with an error.
Error!
Migration is required due to the following errors:
- Property 'Item.itemid' has been changed from 'string' to 'int'.
You need to incorporate a migration block to 'convert' the string value to an Int. Assuming we add a new Int property to our object `item_id', something along these lines will migrate your strings to int's and in the case where the string is not a valid it, it will be assigned a value of 0
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
migration.enumerateObjects(ofType: Item.className()) { oldObject, newObject in
let stringValue = oldObject!["itemid"] as! String
newObject!["item_id"] = Int(stringValue) ?? 0
}
}
})
Also, as soon as Realm is accessed, the object models are written to the Realm file. So a simple matter of
let items = realm.object(Item.self)
Will store that model even if no data was ever written. If, after that line, the var type is changed from a String to an Int, it will throw the migration error.
Deleting the Realm and starting from scratch is one option if that's the case, and as mentioned above, a migration block.
If this is brand new model that has never been used, then as the comments and other answer suggest, just change the String to an Int.
Simply change the String to Int in your Object model. Please note that the Realm documentation says:
String, NSDate, and NSData properties can be declared as optional or non-optional using the standard Swift syntax.
So unlike the String in your previous model, you will not be able to declare your Int as optional. You have two options:
Declare a default value:
class Item: Object {
#objc dynamic var name: String?
#objc dynamic var itemid: Int = 0
#objc dynamic var cateid: Int = 0
}
Declare it as a RealmOptional:
class Item: Object {
#objc dynamic var name: String?
#objc dynamic var itemid = RealmOptional<Int>()
#objc dynamic var cateid = RealmOptional<Int>()
}
For more information on each solution please see this SO answer and the Realm documentation.

Changing a struct with one type to another type

I have two structs with the same fields. What is the best way to merge them.
struct Type1{
var variable1:String?
var variable2:Double?
var variable3:String?
var notImporant:String?
}
struct Type2{
var variable1A:String?
var variable2A:String?
var variable3A:String!
}
What is the best way to convert type2 to type1? I am getting a return from an API and parsing it using codable but there are two different structs and I need to get one struct. The data is the same, it is just mapped differently in terms of types. Some of the structs have more info and others have less.
Just make a copy constructor in both structs like so:
struct Type2 {
var variable1A:String?
var variable2A:String?
var variable3A:String!
init(_ otherType: Type1) {
variable1A = otherType.variable1
variable2A = otherType.variable2
variable3A = otherType.variable3
}
}
You cannot cast two unrelated structs. What you can do is define a common Protocol for the two of them, and use them in places where you don't care which underlying object it is.
protocol CommonProtocol {
var variable1: String? { get }
var variable3: String? { get }
}
struct Type1: CommonProtocol {
var variable1:String?
var variable2:Double?
var variable3:String?
var notImporant:String?
}
struct Type2: CommonProtocol {
var variable1A:String?
var variable2A:String?
var variable3A:String!
}
Then, in whichever place you're currently stuck with a type1 instead of a type2, have that function just accept a CommonProtocol instead, and you can use either.
Note that, while both of your types have a variable2, one of them is a Double? while the other is a String?. There are a few different ways to approach that, which I leave to you. I just left it out of the protocol.
On another note, it's Swift standard to capitalize the names of structs (Type1, Type2). In certain instances, you can run into problems if you don't, so I suggest you do.

How to reflect not managed properties of NSManagedObject

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.

Class or struct for hierarchy model?

I understand difference between class and struct in Swift. Now I'm wondering what to use for hierarchy model.
To define a class is pretty simple (setting connections on properties set is now irrelevant).
class XYClass {
var title: String
var subinstances: [XYClass]
weak var superinstance: XYClass?
}
But it looks like pretty fine model for struct. Especially if I need to instantiate a lots of these and frequently. But I'm wondering if I can somehow safely point to superinstance or I need to store whole object graph to every instance on every change... Should I use class or struct and if struct, how to define it?
You are making a linked list. If you were to try to form a linked list of structs of a single type, memory management would not be feasible, and the compiler would stop you dead in your tracks. This won't compile:
struct XYClass {
var title: String
var subinstances: [XYClass]
var superinstance: XYClass?
}
The compiler has spotted the problem. You cannot refer to an instance of a struct as a property of that struct. (The compiler calls this a "recursive value type".)
Thus, for your situation, you must use a class, because only then can you get a weak reference and avoid a retain cycle. Only a reference to a class can be weak (and only if the reference is typed as an Optional).
This will compile, and will give your linked list coherent memory management:
class XYClass {
var title: String = ""
var subinstances: [XYClass] = []
weak var superinstance: XYClass?
}