How to pass realm.objects(SomeObject.self).filter() to a function that needs Results<Object> - ios

This question is in conjunction with RealmSwift Cannot cast Results<SomeOjbect> to Results<Object>
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 framework: https://github.com/danielgindi/ChartsRealm, which pull data from Realm and draw charts. The framework is written in Swift, and can be used in Objc as well, so it combines RLMObject and Object types.
It has a function like:
public convenience init(results: Results<Object>?, xValueField: String?, yValueField: String, label: String?)
which takes Results<Object>?, however I cannot get my filter results as Results<Object> type. e.g.
realm.objects(SomeObject.self).filter("city=0")
it's Results<SomeObject>, and cannot be converted to Results<Object>, described in RealmSwift Cannot cast Results<SomeOjbect> to Results<Object>
How can I solve it?
Because the demo in ChartsRealm framework, it simply read all objects from Realm in + (RLMResults *)allObjectsInRealm:(RLMRealm *)realm;, but in real world, we usually need to filt the results first.
If there's really nothing I can do, I could accept modifying the framework function parameters, filing pull requests for the framework, just to make it work.

Results<Object> does not make any sense as a type, and the methods in ChartsRealm should be generic methods which take a Results<T>.
Because in this specific case all that ChartsRealm does with the Results<Object> is use ObjectiveCSupport.convert() to get a RLMResults, an unsafeBitCast() to Results<Object> should work fine. You could also just call ObjectiveCSupport.convert() yourself and pass ChartsRealm a RLMResults rather than a Swift Results object.

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.

How to use Generics to handle dictionaries and arrrays

I am new to using generics but as according to my requirement I need to use it.
I have like 10 apis in which
4 returns array of custom objects(like multiple Person object data([Person]))
4 returns simple a object(like Company object data(Company))
two return simple dictionary
so what I'm trying to do is to create a common Response class
class Response<T>: NSObject {
#objc var responseData = T
}
But it is giving error on this line.
How should I suppose to use it so that it fulfills the requirements.
first of all generics cannot be represented in objc. so you need to use only Swift
class Response<T> {
var responseData: T!
}
you can use then T like this:
let response = Response<[String]>()
so response.responseData will be an array of String

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.

Instatiate Realm Object from string in swift 3

I would like to know if it is possible to instantiate a realm object based on a string that is the class name of the realm object but without knowing what that string will be until it is provided.
For example:
for(_, object) in json["AllObjects"]{
let objectType = self.getRealmObjectBasedOnString(type: className, params: object.stringValue)
self.objectList.append(objectType)
}
Here I go through a json that I get and want to create a realm object from each json object in the array. The problem is that this method will be called several times and each time the only thing that will change is the className variable. So I would like to keep this logic in only one method instead of creating several methods with same logic or a huge and complicated if else that determines the realm object to be created.
Here is getRealmObjectBasedOnString
func getRealmObjectBasedOnString(type: String, params: String) -> Object{
switch type {
case "classA":
return ClassA(JSONString: params)!
case "classB":
return ClassB(JSONString: params)!
default:
return DefaultClass(JSONString: params)!
}
}
Can someone explain why this does not work and whether it is possible to accomplish what I want?
You can use NSClassFromString to get Realm object type from string, but keep in mind that Swift uses modules for nemespacing, so you'll need to add your app's module name before your class name.
guard let objectType = NSClassFromString("YourAppModuleName.\(json["className")") else {
// handle unexpected class here
}
let objectList = realm.objects(objectType)

Declare an array of Int in Realm Swift

How can I declare an array of integers inside RLMObject?
Like :
dynamic var key:[Int]?
Gives the following error :
Terminating app due to uncaught exception 'RLMException', reason: ''NSArray' is not supported as an RLMObject property. All properties must be primitives, NSString, NSDate, NSData, RLMArray, or subclasses of RLMObject. See https://realm.io/docs/objc/latest/api/Classes/RLMObject.html for more information.'
Lists of primitives are not supported yet unfortunately. There is issue #1120 to track adding support for that. You'll find there some ideas how you can workaround that currently.
The easiest workaround is create a object to hold int values. Then the model to have a List of the object.
class Foo: Object {
let integerList = List<IntObject>() // Workaround
}
class IntObject: Object {
dynamic var value = 0
}
Fortunately arrays of primitive types are now supported in Realm 3.0 and above. (Oct 31 2017)
You can now store primitive types or their nullable counterparts (more specifically: booleans, integer and floating-point number types, strings, dates, and data) directly within RLMArrays or Lists. If you want to define a list of such primitive values you no longer need to define cumbersome single-field wrapper objects. Instead, you can just store the primitive values themselves!
class MyObject : Object {
#objc dynamic var myString: String = ""
let myIntArray = List<Int>()
}
Source: https://realm.io/blog/realm-cocoa-reaches-3-0/
The accepted offer is very costly in term of memory.
You might get a List of very big "n" of objects.
It's not a matter of right and wrong but I think it's good to write here a different workaround.
Another approach:
I decided to use a single string to represent an Int array.
In my Realm class I defined a variable:
dynamic var arrInt: String? = nil
And use it very easily:
let arrToSave = [0, 1, 33, 12232, 394]
<MY_CUSTOM_REALM_CLASS>.arrInt = arrToSave.map { String(describing: $0) }.joined(separator: ",")
And the way back:
let strFetched = <MY_CUSTOM_REALM_CLASS>.arrInt
let intArray = strFetched.components(separatedBy: ",").flatMap { Int($0) }
Will be happy to hear your feedback, as I think this approach is better.
As the error message states, you have to use RLMArray - or rather it's swift equivalent List.
See: Realm docs

Resources