What is RLMCollection replacement in RealmSwift - swift

var objects:RLMCollection!
if selectedObject != nil {
objects = selectedObject.childobjects as RLMArray
} else {
objects = RealmObject.objectsInRealm(realm, "isFavourite == 1")
}
println(couplets.objectAtIndex(indexPath.row) as! RealmObject)
I had a senario, where I won't know whether I need to work with RLMArray or RLMResults, so I used RLMCollection.
How can I achieve the same in RealmSwift 0.93.1

In general, in Swift it's good enough to work with a CollectionType, which defines subscripting, enumeration, and many of the other 'array-like' methods that Results and List expose. (Though we are interested in adding a RealmCollectionType protocol, just haven't gotten around to it yet!)

Related

Does Swift know not to initialize an object if my set already contains it?

I have a global var notes: Set<Note> that contains notes initialized with downloaded data.
In the code below, does Swift know to skip the initialization of my Note object if notes already contains it?
for dictionary in downloadedNoteDictionaries {
let note = Note(dictionary: dictionary)
notes.insert(note)
}
I'm wondering because my app downloads dozens of notes per request and initializing a Note object seems rather computationally expensive.
If the answer to my question is no, then how could I improve my code's performance?
My Note class—which I just realized should probably be a struct instead—has the property let id: Int64 as its sole essential component, but apparently, you can't access an element of a set by its hash value? I don't want to use Set's instance method first(where:) because it has a complexity of O(n), and notes could contain millions of Note objects.
You cannot rely on Swift to eliminate the construction of a new Note in your code. Your Set needs to ask the Note for its hashValue, and may need to call == with your Note as an argument. Those computations require the Note object. Possibly if Swift can inline everything, it can notice that your hashValue and == depend only on the id property, but it is certainly not guaranteed to notice or to act on that information.
It sounds like you should be using an [Int64: Note] instead of a Set<Note>.
No, Swift will not avoid creating the new Note object. The problem here is trying to determine if an object already exists in a set. In order to check if an object already exists in the set, you must have some way to identify this object consistently and have that identification persist across future reads and writes to that set. Assuming we want to adopt Swift's hashing enhancements which deprecates the old methods for having to manually provide a hashValue for any object conforming to the Hashable, we should not use a Set as a solution to this problem. The reason is because Swift's new recommended hashing methods use a random seed to generate hashes for added security. Depending on hash values to identify an element in a set alone would therefore not be possible.
It seems that your method of identifying these objects are by an id. I would do as Rob suggests and use a dictionary where the keys are the id. This would help you answer existential questions in order avoid instantiating a Note object.
If you need the resulting dictionary as a Set, you can still create a new Set from this resulting dictionary sequence.
It is possible to pull out a particular element from a set as long as you know how to identify it, and the operations would be O(1). You would use these two methods:
https://developer.apple.com/documentation/swift/set/2996833-firstindex
https://developer.apple.com/documentation/swift/set/2830801-subscript
Here is an example that I was able to run in a playground. However, this assumes that you have a way to identify in your data / model the id of the Note object beforehand in order to avoid creating the Note object. In this case, I am assuming that a Note shares an id with a Dictionary it holds:
import UIKit
struct DictionaryThatNoteUses {
var id: String
init(id: String = UUID().uuidString) {
self.id = id
}
}
struct Note: Hashable {
let dictionary: DictionaryThatNoteUses
var id: String {
return dictionary.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Note, rhs: Note) -> Bool {
return lhs.id == rhs.id
}
}
var downloadedNoteDictionaries: [DictionaryThatNoteUses] = [
DictionaryThatNoteUses(),
DictionaryThatNoteUses(),
DictionaryThatNoteUses()
]
var notesDictionary: [String: Note] = [:]
var notesSet: Set<Note> = []
// Add a dictionary with the same id as the first dictionary
downloadedNoteDictionaries.append(downloadedNoteDictionaries.first!) // so now there are four Dictionary objects in this array
func createDictionaryOfNotes() {
func avoidCreatingDuplicateNotesObject(with id: String) {
print("avoided creating duplicate notes object with id \(id)") // prints once because of the duplicated note at the end matching the first, so a new Note object is not instantiated
}
downloadedNoteDictionaries.forEach {
guard notesDictionary[$0.id] == nil else { return avoidCreatingDuplicateNotesObject(with: $0.id) }
let note = Note(dictionary: $0)
notesDictionary[note.id] = note
}
}
createDictionaryOfNotes()
// Obtain a set for set operations on those unique Note objects
notesSet = Set<Note>(notesDictionary.values)
print("number of items in dictionary = \(notesDictionary.count), number of items in set = \(notesSet.count)") // prints 3 and 3 because the 4th object was a duplicate
// Grabbing a specific element from the set
// Let's test with the second Note object from the notesDictionary
let secondNotesObjectFromDictionary = notesDictionary.values[notesDictionary.values.index(notesDictionary.values.startIndex, offsetBy: 1)]
let thirdNotesObjectFromDictionary = notesDictionary.values[notesDictionary.values.index(notesDictionary.values.startIndex, offsetBy: 2)]
if let secondNotesObjectIndexInSet = notesSet.firstIndex(of: secondNotesObjectFromDictionary) {
print("do the two objects match: \(notesSet[secondNotesObjectIndexInSet] == secondNotesObjectFromDictionary)") // prints true
print("does the third object from dictionary match the second object from the set: \(thirdNotesObjectFromDictionary == notesSet[secondNotesObjectIndexInSet])") // prints false
}

How can convert Mirror To Orginal class or struct using swift language

i have an question about the Mirror reflection .
i convert my struct to mirror to iterate through all the properties to get values and after i iterate through it and change the values in properties i need to convert mirror again to the original struct with values which i edited but i can't , is swift language have way to do this conversion ?
the code below
//MARK:- loop get tags
func getTags(filter: Any){
let getTags = Mirror(reflecting: filter)
for (tag) in getTags.children {
if let getTag = tag.value as? String {
if let _ = Int(getTag) {
}else {
if getTag != "" && getTag != "All" {
arrayOfTags.append(getTag)
}
}
}// if let
}// end for loop
}
thanks
You can't construct a struct without hardcore memory manipulation. You could create objets with functions that are still available from Objective C. You could set the property with the setValue forKey function. Your objects needs to be derived from NSObject.
Doing this and taking into account all scenario's is quite a challenge. There is a CocoaPod library that could help with this. Have a look at EVReflection You could create a dictionary from your object and an object from your dictionary.
it's been asked a while a go.. but just in case I think you may be looking for https://cocoapods.org/pods/EVReflection

Storing a simple array in Realm without creating a managed List in Swift

I have an array of data that doesn't need to be a managed List, meaning I don't need Realm to create a new model for the items with links and the ability to query on the items, etc. I just want a simple array, typically of primitives that don't inherit from Object anyway, that will be persisted with my main object.
The only solution I can think of is to use NSData and NSKeyedArchiver/NSKeyedUnarchiver. Is that the best/only way to do this? Should I just use List even if I don't think I'll need it — what's the best practice for this situation?
Realm doesn't support arrays of primitives (although that functionality is coming soon), so the most straightforward solution is to use a List filled with model objects that just wrap your primitives. There's nothing wrong with archiving to and from NSData and storing the data in your Realm model, though, if you feel that better suits your particular use case.
Here's how I decided to deal with this:
var instructions: [String] {
get {
if _instructions == nil {
_instructions = NSKeyedUnarchiver.unarchiveObject(with: instructionsData) as? [String]
}
return _instructions!
}
set {
instructionsData = NSKeyedArchiver.archivedData(withRootObject: newValue)
_instructions = newValue
}
}
fileprivate var _instructions: [String]? = nil
dynamic var instructionsData = Data()
override static func ignoredProperties() -> [String] {
return ["instructions", "_instructions"]
}
That way I can use the array as I normally would and still have it persisted in a simple way (without having to create an actual List and having to manage a bunch of new models/objects).

NSPlaceholders and Swift

I'm looking at the .selection of an Array Controller and I want to know when the selection is multiple values or none. In ObjC we'd do this by getting the selection as an id and checking it's raw equality with the various placeholder markers: NSMultipleValuesMarker, NSNoSelectionMarker, and NSNotApplicableMarker.
But this doesn't work in Swift:
let currentValue = eventsArrayController.selection.valueForKey("enabled")
if let markerVal = currentValue {
if markerVal == NSMultipleValuesMarker {
// this doesn't compile, AnyObject's can't be compared with ==
}
switch markerVal {
case NSNoValueMarker:
// this doesn't compile, an AnyObject is not a valid pattern
}
}
How do I test the value of placeholder markers in Swift?
In Swift, == is meant to check for value equality (that is, if two objects contain the same data, regardless of whether they share the same memory location or not). Use the === operator to check for reference equality (which tests if two objects share the same memory location).

Can't set nil to an optional property with custom setter in Swift

I have swift class with various properties, some of which have optional types.
class UserObject: PFUser{
//optional property
var optionalPhotoURL:String? {
get {
if let optionalPhotoURL:String = objectForKey("optionalPhotoURL"){
return optionalPhotoURL
}
//not needed but just for emphasis
return nil
}
//I am unable to set UserObject.optionalPhotoURL = nil with this setters
set {
if let newPhotoURL:String = newValue! {
setObject(newPhotoURL, forKey: "optionalPhotoURL")
}else{
self.optionalPhotoURL = nil
}
}
}
}
I am unable to set optionalPhotoURL as nil, if it already had a previous value assigned to it.
I guess my question is, how do i "unset" an optional property with custom setter?
Update
These setters all crash
set {
if let newPhotoURL:String = newValue {
setObject(newPhotoURL, forKey: "optionalPhotoURL")
}else{
self.optionalPhotoURL = nil
}
}
and this
set {
if (newValue != nil) {
setObject(newValue!, forKey: "optionalPhotoURL")
}else{
self.optionalPhotoURL = nil
}
}
What you have here is a computed property.
Swift properties can either be computed or stored. We can observe value changes in our stored properties by using didSet and willSet but here we still have a stored property.
In your case, since you have overridden set and get*, you don't have a stored property, you have a computed property. If you want a computed property to have a backing storage, you must create that independently.
So you may want something like this:
class FooClass {
private var storageProperty: String?
var accessProperty: String? {
get {
return self.storageProperty
}
set {
self.storageProperty = newValue
// or whatever logic you may like here
}
}
}
*: You can't override set without also overriding get. You can however override get without overriding set--this makes a readonly computed value.
Moreover, it's important that we implement our storage properties in this way over relying on key-value coding.
For starters, setObject(forKey:) approach doesn't even work on pure Swift types. This will only work on objects which inherit from Objective-C types. It's an inherited method from NSObject's compliance to NSKeyValueCoding protocol. Why the base object of Objective-C conforms to so many protocols is beyond me... but it does and there's nothing we can do about it.
If we have a code base in which some of our objects are inheriting from Objective-C objects (which basically any project will have, UIViewController, etc), and some of our objects are pure Swift objects (which you will tend to have when you're creating your own Swift classes from scratch), then our Swift objects will not be able to implement this same pattern. If we have some objects of both types, we'll either have to implement the pattern I show above for all of them (and then we have consistency) or we'll have to implement one pattern for some types and another for other types (Swift structs would definitely have to implement the above pattern, you can't just make them inherit from NSObject) (and then we have inconsistency, which we don't like).
But what's far worse about setObject(forKey:) is that the first argument of this method always will be of type AnyObject. There is no type safety to the method at all. Where things are stored via setObject(forKey:) is based purely on the key which we use. When we use setObject(forKey:), we take a pile of type-safety advantages that Swift gives us and we throw them out the window. If we don't care to leverage the advantages Swift gives us over Objective-C, why are we writing it in Swift at all?
We can't even make the stored property private when we use setObject(forKey:). Anyone who knows the key can call setObject(forKey:) and set that object to whatever they want. And that includes objects which are not strings. All we have to do to introduce a crash to this codebase is write a class extension or subclass which has a key collision on a different type other than what you've used (so maybe an NSData value for the same key). And even if it doesn't happen to be a different type, simply duplicating the same key for multiple properties is going to introduce bugs... and bugs that are hard to debug.
Never set a value of a computed property in its set scope by calling itself !
This causes an infinite loop and the app will crash.
I don't know which API setObject:forKey belongs to, but in the case of nil you are supposed to remove the object
set {
if let newPhotoURL = newValue {
setObject(newPhotoURL, forKey: "optionalPhotoURL")
} else {
removeObjectForKey("optionalPhotoURL")
}
}
Your property optionalPhotoURL is a computed property, it does not store any values:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
You might want to create an additional property which actually stores the value. However, why do you want to set it to nil, since you are not deleting they object in case of nil.