How to create optional List(array) of NSData objects in Realm? - ios

I need to save pictures but Realm documentation says that it is not possible to have NSData List, let alone optioanal. How do you think, will it work if create entity what will be contain property of NDData, and make optional list of them? Somebody faced with same problem?

If you'd like to create optional type of object type (String, NSDate or NSData), you can just declare the properties as optional, like the following:
class Person: Object {
dynamic var name: String? = nil
}
If you want to declare a variable of optional type for Int, Float or Double, you should wrap the value with RealmOptional<T>, like the following:
class Person: Object {
let age = RealmOptional<Int>()
}
Please see also https://realm.io/docs/swift/latest/#optional-properties

Related

How to force update CoreData Codefiles (Property Extension) in Swift 5?

i am a bit confused with CoreData. In my xcdatamodeld I am able to create new properties and change some things about them, like whether they are optional or not.
For example, I created a property for my entity and called it descrip, the type is a String and I checked the Optional Field.
At some other point I try to create a new Object of this Entity and safe it to CoreData:
let newEntity = Entity(context: context)
newEntity.name = name
newEntity.descrip = descrip // descrip is an optional String
newEntity.filename = filename
I get the following error message: Value of optional type 'String?' must be unwrapped to a value of type 'String'
Then, I tried to create the NSManagedObjectSubclasses for my Entity. I discovered, that while all properties have been added and the type of all is set correctly, the ? indicating the Optionality of the property was messed up completely.
import Foundation
import CoreData
extension Entity {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Entity> {
return NSFetchRequest<Entity>(entityName: "Entity")
}
#NSManaged public var name: String? // Should not be optional and was set so in xcdatamodeld
#NSManaged public var descrip: String // Should be optional and set to optional in xcdatamodeld
#NSManaged public var filename: String
}
Is there any way to force update this code file? Or how can I use optional strings to fill out my new Entity's optional values?
The error in that assignment is not a Core Data problem, it's pure Swift. You've declared descrip as String, yet you are trying to assign a String? value. That's not legal in Swift-- you must unwrap an optional before assigning it to a non-optional. It would be the same if you weren't using Core Data. For example this would produce the same error:
let foo: String? = "hello"
let bar: String = foo
The differences between the data model and the generated code happen because Swift optionals and Core Data optionals are completely different things. Core Data only cares if something is optional when you save changes. Swift cares about whether something is optional all the time. It's different because Core Data was around for a long time before Swift existed, so it doesn't always match up with what Swift expects.

Can't perform methods of objects stored in Array[Any]

I want to store objects of different types in an array.
The program below is only a minimum demo. In the anyArray:[Any] an instance of Object1 is stored. The print statement prints out the expected object type. In the following line the test of the stored object's type returns true. This means, during run time the correct object type is known and every thing seems to be fine.
class Object1 {
var name = "Object1"
}
var anyArray:[Any] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
//print("Name:\((anyArray[0]).name)")
Console output:
Object1
Test result:true
However, if I try to print out the name property of the object, I get an error message from the editor:
Value of type 'Any' has no member 'name'
Well, at compile time the object's type is unknown. That's why the compiler complains. How can I tell the compiler that it is OK to access the properties of the stored object?
The difference comes from the difference from Type Checking in:
runtime, or
compile time
The is operator checks at runtime whether the expression can be cast to the specified type. type(of:) checks, at runtime, the exact type, without consideration for subclasses.
anyArray[0].name doesn't compile since the Type Any doesn't have a name property.
If you're sure anyArray[0] is an Object1, you could use the downcast operator as!:
print("\((anyArray[0] as! Object1).name)")
To check at runtime if an element from anyArray could be an Object1 use optional binding, using the conditional casting operator as?:
if let:
if let object = anyArray[0] as? Object1 {
print(object.name)
}
Or use the guard statement, if you want to use that object in the rest of the scope:
guard let object = anyArray[0] as? Object1 else {
fatalError("The first element is not an Object1")
}
print(object.name)
If all objects in your array have a name property, and you don't want to go through all the hoops of optional binding repeatedly, then use a protocol. Your code will look like this:
protocol Named {
var name: String {get set}
}
class Object1: Named {
var name = "Object1"
}
var anyArray:[Named] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
print("Name:\(anyArray[0].name)")
Notice that anyArray is now an array of Named objects, and that Object1 conforms to the Named protocol.
To learn more about protocols, have a look here.
You object is still of type Any. You just checked if it can be of type Object1, but you did not cast it. If you want the object as Object1, you need to cast it.
Also if multiple classes can have name, you need to use Protocol like #vadian has mentioned in his comment and cast it to that protocol.
protocol NameProtocol {
var name: String {get set}
}
class Object1: NameProtocol {
var name = "Object1"
}
if let testResult = anyArray[0] as? NameProtocol {
print(testResult.name)
}
Edit: "I want to store objects of different types in an array". The solution that you have marked as correct will not work if all the objects that you have do not conform to the protocol.

RealmSwift Cannot cast Results<SomeOjbect> to Results<Object>

RealmSwift version: latest master branch
So I have a Realm Object like:
import RealmSwift
class SomeObject: Object
{
#objc dynamic var datetime = ""
#objc dynamic var city = 0
convenience init(city: Int, datetime: String)
{
self.init()
self.city = city
self.datetime = datetime
}
}
There is a func call like
static func createlineData(from results: Results<Object>?) -> LineChartData
Now I fetch some results and pass to createLineData:
let realm = try! Realm()
let results = realm.objects(SomeObject.self).filter("city = \(city.rawValue)")
let lineData = createlineData(from: results as? Results<Object>)
compiler warns me that the type cast will always fail:
Cast from Results<"SomeObject"> to unrelated type Results<"Object"> always fails
I am confused since SomeObject is just a subclass. How can I fix it? Thanks in advance.
UPDATE:
What I want to do is that, the param of
static func createlineData(from results: Results<Object>?) -> LineChartData
can never be changed, so I need to make a query to filt based on city which is enum, pass them into createlineData(from results: Results<Object>?), and access other properties like datetime later in createlineData, from Results<Object>
In Swift, each generic class represents its own type and even if you have a generic class where the generic type parameter is a subclass of your other generic class having the superclass as its generic parameter, the two generic classes won't be related through inheritance.
This is why you cannot cast Results<SomeObject> to Results<Object> even though SomeObject is a subclass of Object.
Here's a simple example representing the same issue with a generic class:
class A{}
class B:A{}
class GenericClass<T> {
let val:T
init(val:T) {
self.val = val
}
}
let genericA = GenericClass<A>(val: A())
let genericB = GenericClass<B>(val: B())
let upcasted = genericB as? GenericClass<A> //warning: Cast from 'GenericClass<B>' to unrelated type 'GenericClass<A>' always fails
Moreover, the Results type in Realm is a homogenous collection type, so you cannot store different subclasses of Object in the same Results object, so casting to Results<Object> wouldn't make sense anyways. If you need to store objects from different Realm model classes in the same collection, you will need to sacrifice the self-updating nature of Results and stick with storing your objects in an Array for instance.

How to check for core data attribute type at runtime?

I want to check the type of attributes datatype at runtime in Swift. Like while inserting I want to check if the particular attribute in an entity accepts value of date type or string type. How this can be achieved in Swift.
You can always use entity's attribute description which is of type NSAttributeDescription to find out the correct type of the property that is defined in model.
If say you have a subclass of NSManagedObject, Person. Then, you could use example from following code to check the type before inserting,
#objc(Person)
class Person: NSManagedObject {
#NSManaged
var name: String
#NSManaged
var age: NSNumber
#NSManaged
var dateOfBirth: Date
}
let person = NSEntityDescription.insertNewObject(forEntityName: "Person", into: context) as! Person
if let attribute = person.entity.attributesByName["name"],
attribute.attributeType == .stringAttributeType {
// use your code here for custom logic
print("name is string")
}
if let attribute = person.entity.attributesByName["age"],
attribute.attributeType == .dateAttributeType {
// use your code here for custom logic
print("age is date")
}
It's type(of:).E.g.,
let test: Int = 0
if type(of: test) == Int.self {
print("found")
}
In general, you should know what your model is before you write your code.
So introspecting on a readonly model seems a little silly. I can't think of any reason why you would ever want to do this, but I'm sure you have a good reason that you aren't sharing.
You can look at a managedObject entity class method (on your subclass) which is a NSEntityDescription. Or you can get all the entity descriptions directly from your model object (context.persistentStoreCoordinator.managedObjectModel.entites) or if you know the entity's name you can use context.persistentStoreCoordinator. managedObjectModel.entitiesByName["EntityName"]. The entity description will tell you all about the entities properties. You can look through each of the attributes and get a NSAttributeDescription which will tell you the type for that attribute.

Swift. Refer to instance variables by string name

I've got a number of user properties in a user viewcontroller class ie
//user vars
var email: String?
var givenName: String?
var familyName:String?
var phone: String?
var dob: NSDate?
In a method within that class i retrieve user data from coredata and set the user text fields with that data in a loop
for i in 0 ..< userTextFields.count {
let field = userTextFields[i]
let fieldName = userTextFieldKeyNames[i]
let fieldText = currentUser.valueForKey(fieldName) as? String
field.text = fieldText
}
the fieldName variable in the loop matches the class's ivars above. Is there a way i can reference the ivars within the loop by matching it with the fieldName string so I can set the values of the ivars with the fetched coredata values ie in a literal sense saying something like the following ...
if self.property.name.text == fieldName {
self.property.value == fieldText
}
ie somehow resolving the various property names withing the class ... or is this bad design? .... if so any suggestions on achieving the same result in a better way
Not Swift-ish, as it's bypassing compile time type checking. Best option is to keep them all in a dictionary, and use a protocol to define allowed data types in the dictionary, but even that is rather poor

Resources