Realm Swift - Convert an array of results into an array of Ints - swift

I want to convert the results from my Realm data into Int.
Here is an example of how I want to use this.
let results = realm.objects(Data.self)
print(results)
However the result is type Results<Data> and cannot be converted into a Int but the results is an Int.
Just to be clear I want an array of Int from my results

You can simply use Array(realm.objects(RealmType.self)), which will convert the Results<RealmType> instance into an Array<RealmType>.
However, there are other serious flaws with your code. First of all, neither of the last two lines will compile, since firstly realm.objects() accepts a generic input argument of type Object.Type and Data doesn't inherit from Object. You can't directly store Data objects in Realm, you can only store Data as a property of a Realm Object subclass.
Secondly, myArray[results] is simply wrong, since results is supposed to be of type Results<RealmType>, which is a collection, so using it to index an Array cannot work (especially whose Element type is different).

It appears that based on the number of results from the database, you want to select an object from the array.
You can get the number of items in an array with .count. Be certain that an object exists at the specified index in the array, or your application will crash!
let numberOfResults = results.count
if myArray.count > numberOfResults {
let object = myArray[numberOfResults]
print(object)
}

Related

Swift generic collection of Element cannot convert to collection of Any

I'm facing a problem I don't understand.
In my project, I want to make a collection of elements with some customised methods (like an update from a server). But when I try to group all these collections in an array, I get an error: "Cannot convert value of type MyCollection<someElement> to expected argument type MyCollection<Any>"
What I don't understand is that the same code with Array is working... Array isn't a collection?
// My collection which would contain an update method
class MyCollection<Element> {
var object:Element? = nil
}
let x = MyCollection<Int>()
var list = [MyCollection<Any>]()
list.append(x) //Cannot convert value of type 'MyCollection<In>' to expected argument type 'MyCollection<Any>'
let a = Array<Int>()
var lista = [Array<Any>]()
lista.append(a) //Doesn't get error at all...
I know I can do this with an array of the specific type but by grouping all of MyCollection in an array, I wish to use a code like :
func update() {
for e in list { // array of MyCollection<Any>
e.update()
}
}
Thank you in advance for your help ;)
Being able to convert from SomeType<Subtype> to SomeType<Supertype> is called covariance. In Swift, Array<T> is covariant on T by "compiler magic", and you can't do the same for your own types.
The type checker hardcodes conversions from Array to Array if there is a conversion from T to U. Similar rules exist for Optional and Dictionary. There's no mechanism for doing this with your own types.
Your own generic types are always invariant, meaning that there is never a conversion between SomeType<T> to SomeType<U>, as long as T and U are different types.
Let's imagine what would happen if the conversion on MyCollection were allowed. You could do:
let myCollectionInt = MyCollection<Int>()
let myCollectionAny: MyCollection<Any> = myCollectionInt // suppose you can do this
myCollectionAny.object = "string" // myCollectionAny.object is of type Any?, so this should be ok
We've set myCollectionAny.object to "string", but MyCollection is a reference type, so myCollectionInt.object should also be "string". But myCollectionInt.object is an Int?!
Of course this type-unsafety is also a problem with arrays, but the language designers have decided that casting arrays is a common enough thing to do, that disallowing it would do more hard than good.

Convert Realm list of Strings to Array of Strings in Swift

I'm just starting up with RealmSwift, and I'm trying to store an array of Strings in Realm. It doesn't work, so now I'm using List<String>() as an alternative. However, how do I convert these Realm Lists back to [String] again? And if I can't do that, are there any alternatives?
Thanks
However, how do I convert these Realm Lists back to [String] again
You can simply cast List to Array, because List has Sequence Support:
let list = List<String>()
let array = Array(list)
Bear in mind that by converting to an array you'll lose the 'dynamic' quality of a Realm collection (i.e. you'll receive a static array, whereas keeping the original List will provide automatic updating should the source change). But you can create an array by using an extension, e.g.:-
extension RealmCollection
{
func toArray<T>() ->[T]
{
return self.compactMap{$0 as? T}
}
}
Then use:-
let stringList = object.strings.toArray()
Where object is the realm object, and strings is your field.
Here are the details. how to assign an array in the realm list model.
jim.dogs.append(objectsIn: someDogs)

Array values optional, or not?

Could you explain why:
when I access an array value using array.first it's optional
when I access from an index value it is not?
Example:
var players = ["Alice", "Bob", "Cindy", "Dan"]
let firstPlayer = players.first
print(firstPlayer) // Optional("Alice")
let firstIndex = players[0]
print(firstIndex) // Alice
(The short answers to this question are great, and exactly what you need. I just wanted to go a bit deeper into the why and how this interacts with Swift Collections more generally and the underlying types. If you just want "how should I use this stuff?" read the accepted answer and ignore all this.)
Arrays follow the rules of all Collections. A Collection must implement the following subscript:
subscript(position: Self.Index) -> Self.Element { get }
So to be a Collection, Array's subscript must accept its Index and unconditionally return an Element. For many kinds of Collections, it is impossible to create an Index that does not exist, but Array uses Int as its Index, so it has to deal with the possibility that you pass an Index that is out of range. In that case, it is impossible to return an Element, and its only option is to fail to return at all. This generally takes the form of crashing the program since it's generally more useful than hanging the program, which is the other option.
(This hides a slight bit of type theory, which is that every function in Swift technically can return "crash," but we don't track that in the type system. It's possible to do that to distinguish between functions that can crash and ones that cannot, but Swift doesn't.)
This should naturally raise the question of why Dictionary doesn't crash when you subscript with a non-existant key. The reason is that Dictionary's Index is not its Key. It has a little-used subscript that provides conformance to Collection (little-used in top-level code, but very commonly used inside of stdlib):
subscript(position: Dictionary<Key, Value>.Index) -> Dictionary.Element { get }
Array could have done this as well, having an Array.Index type that was independent of Int, and making the Int subscript return an Optional. In Swift 1.0, I opened a radar to request exactly that. The team argued that this would make common uses of Array too difficult and that programmers coming to Swift were used to the idea that out-of-range was a programming error (crash). Dictionary, on the other hand, is common to access with non-existant keys, so the Key subscript should be Optional. Several years using Swift has convinced me they were right.
In general you shouldn't subscript arrays unless you got the index from the array (i.e. using index(where:)). But many Cocoa patterns make it very natural to subscript (cellForRow(at:) being the most famous). Still, in more pure Swift code, subscripting with arbitrary Ints often suggests a design problem.
Instead you should often use Collection methods like first and first(where:) which return Optionals and generally safer and clearer, and iterate over them using for-in loops rather than subscripts.
if you want to use subscript and you don't want to have a crash, you can add this extension to your code:
extension Collection {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
and then use it:
let array = [0, 1, 2]
let second = array[safe:1] //Optional(1)
let fourth = array[safe:3] //nil instead of crash
The behavior of first and index subscription is different:
first is declared safely: If the array is empty it returns nil, otherwise the (optional) object.
index subscription is unsafe for legacy reasons: If the array is empty it throws an out-of-range exception otherwise it returns the (non-optional) object
This is because with first, if the Array is empty, the value will be nil. That is why it is an optional. If it is not empty, the first element will be returned.
However, with a subscript (or index value), your program will crash with an error
fatal error: Index out of range
If it is out of range (or is empty) and not return an optional. Else, it will return the element required.
There are default behavior of array property. Array is generic type of Element. When you try to access using first it return as optional.
public var first: Element? { get }
This is available in Array class.

Swift - Detecting whether item was inserted into NSMutableSet

This is more for interest rather than a problem, but I have an NSMutableSet, retrieved from UserDefaults and my objective is to append an item to it and then write it back. I am using an NSMutableSet because I only want unique items to be inserted.
The type of object to be inserted is a custom class, I have overrode hashCode and isEqual.
var stopSet: NSMutableSet = []
if let ud = UserDefaults.standard.object(forKey: "favStops") as? Data {
stopSet = NSKeyedUnarchiver.unarchiveObject(with: ud) as! NSMutableSet
}
stopSet.add(self.theStop!)
let outData = NSKeyedArchiver.archivedData(withRootObject: stopSet)
UserDefaults.standard.set(outData, forKey: "favStops")
NSLog("Saved to UserDefaults")
I get the set, call mySet.add(obj) and then write the set back to UserDefaults. Everything seems to work fine and (as far as I can see) there don't appear to be duplicates.
However is it possible to tell whether a call to mySet.add(obj) actually caused an item to be written to the set. mySet.add(obj) doesn't have a return value and if you use Playgrounds (rather than a project) you get in the output on the right hand side an indication of whether the set was actually changed based on the method call.
I know sets are not meant to store duplicate objects so in theory I should just trust that, but I was just wondering if the set did return a response that you could access - as opposed to just getting the length before the insert and after if I really wanted to know!
Swift has its own native type, Set, so you should use it instead of NSMutableSet.
Set's insert method actually returns a Bool indicating whether the insertion succeeded or not, which you can see in the function signature:
mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element)
The following test code showcases this behaviour:
var set = Set<Int>()
let (inserted, element) = set.insert(0)
let (again, newElement) = set.insert(0)
print(inserted,element) //true, 0
print(again,oldElement) //false,0
The second value of the tuple returns the newly inserted element in case the insertion succeeded and the oldElement otherwise. oldElement is not necessarily equal in every aspect to the element you tried to insert. (since for custom types you might define the isEqual method in a way that doesn't compare each property of the type).
You don't need to handle the return value of the insert function, there is no compiler warning if you just write insert like this:
set.insert(1)

Swift 3: Only continue processing Dictionary value if it's of type Array

I am receiving a data structure over the wire that's of type [String: AnyObject]. The reason for AnyObject is simply because the value can be of type Array or Dictionary. My condition is straight forward:
if let data = list["foo"], data.count > 0 {
// do stuff
}
My problem is, I only want that condition to pass if data is an Array. The condition I have fails because count property seems to work on a Dictionary as well (It probably counts the number of keys in the dictionary).
What's the best way to handle this?
You can cast data as an Array in your if statement:
if let data = list["foo"] as? [Any], data.count > 0 {
// do stuff
}
This will make sure that data is an Array before doing any operations on it.