Realm object in array? - arrays

Question
How can I create an array of objects containing Realm objects?
Code
let realm = try! Realm()
let data: [A] = realm.objects(A)
Error
Cannot invoke 'objects' with an argument list of type '(Object.type)'

How can I create an array of objects containing Realm objects?
From your code sample, I'll further assume that you want to make an array from a Realm Results, not just "standalone" Realm objects.
Since Results conforms to SequenceType, you can use SequenceType.map() to convert it into an array:
let arrayFromResults = results.map({ $0 })
Note, however, that this is almost always the wrong pattern to use.
From your tweet on the same topic, a preferable way to do this would be to encode what you want to display on screen as a Realm query:
self.results = realm.objects(A).filter("poppedOff == NO")
And "popping off" an object (whatever that means) would update the poppedOff property of that object.
Since Realm Results are auto-updating, this won't risk getting out of sync with the contents of the Realm, unlike the array conversion approach, which would have to be updated on every Realm change notification.

Related

Error when trying to set array in userdefaults: Thread 1: "Attempt to insert non-property list object

I have solved the issue now, thanks for your help. I shouldn't have tried to save arrays with UITextViews, but I should have saved their text as strings instead. Here was the original question:
I have tried a lot, and googled a lot, but I can't solve this problem on my own. Whenever I try to save an array in userdefaults, it just is not working. I get the following error:
Thread 1: "Attempt to insert non-property list object (\n "<UITextView: 0x14001f800; frame = (0 0; 355 180); text = 'D'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600003f01d10>; layer = <CALayer: 0x6000031c83e0>; contentOffset: {0, 0}; contentSize: {355, 30}; adjustedContentInset: {0, 0, 0, 0}>"\n) for key content"
I don't know what a non-property list object is. And I do not know how to solve the problem. Below is the lines of code that do not work.
var contentList: [Any] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
contentList.append(label)
defaults.setValue(contentList, forKey: "content")
If I take out the last line of code by turning it into a comment everything runs just fine. How should I replace that line of code? I essentially want to save an array of UITextViews and make it larger every time I call a fucntion (this code is part of a larger function). The reason why I have created another two lists (cl and contentList) is that it helps me with a problem down the line. What I cannot understand however, is why the last line of code doesn't work. If anyone has any ideas, please help me, it would be much appreciated.
Use only String as stated in comments :
var contentList: [String] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
If lbText = label.text {
contentList.append(lbText)
defaults.setValue(contentList, forKey: "content")
}
You can only store a very limited list of data types into UserDefaults, commonly referred to as "property list objects" (Since property list (or plist) files will only store the same data types.
To quote the Xcode docs on UserDefaults, in the section titled "Storing Default Objects":
A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary [or Data, String, NSNumber, Date, Array, or Dictionary types in Swift.] If you want to store any other type of object, you should typically archive it to create an instance of Data.
(I added the equivalent Swift types to the above quote in square brackets, since it looks like Apple hasn't updated it for Swift.)
That's worded a little awkwardly. The idea is that you can only store data of the types listed. Because the Array and Dictionary types are "container" types, you can store any combination of arrays and dictionaries that contain combinations of any of the above types. For example, you can store an array that contains a dictionary, 3 dates, 2 floats, a Double, some Data, and 2 arrays, and those dictionaries and arrays can contain other dictionaries and/or arrays.)
It is almost always wrong to archive UIView objects like UITextViews. You should save the text properties of your text views instead.
If you want to manage a vertical stack of UITextView objects, I suggest adding a vertical stack view to your user interface, and then writing code that adds or removes UITextView subviews to your stack view. You should be able to find plenty of examples of adding and removing objects from stack views online. (It's really easy.)
If you want to manage a scrolling list of feeds of arbitrary length, you might want to use a table view or collection view instead. Those require that you set up a data model and implement a "data source". That takes a little practice to get right, but is very powerful.

Working with Unsplash. Cannot assign value of type 'Array<_>' to type 'Any?'

Currently, I'm working on processing information I've received from a JSON file, and as it's organized into multiple levels, I need to be able to convert not only the file, but all of its contents into the correct file types. I'm having some issues with this, and I can't find anything for Swift on this issue.
So far, I've tried the methods that are apparent within the code below. I couldn't find much information on alternate methods to do this, and I'm still new to programming, so I haven't been able to do much.
URLSession.shared.dataTask(with: urlRequest) { data, response, error in
if let data = data {
print("Clear")
let returnData = String(data: data, encoding: .utf8)
do {
let image = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
print(image!["results"])
var imgl2 = image!["results"]
imgl2 = imgl2 as! Array
//imgl2 = imgl2![0]
//print(imgl2!["color"])
// var imgl3 = imgl2!["results"]
// var imgl4 = imgl3!["results"]
// var imgl5 = imgl4!["results"]
// var imgl6 = imgl5!["results"]
} catch let error {
print(error)
}
}
}.resume()
What should happen here is that I should be able to convert the JSON's contents into the correct file types. To some extent, this works, and I'm able to obtain the first level of results. However, partway through the process at imgl2 = imgl2 as! Array, I can't convert it to an Array. Instead, I receive the error (Cannot assign value of type 'Array<_>' to type 'Any?'). I'd really like to be able to use the array enclosed, but I can't. After this, there will be several additional levels of content that I will need to sort through in this manner. I've tried looking at the API documentation, but I don't understand the way they've written it (this is Unsplash, by the way), and as such, I've tried this method. How exactly would I go about this?
(For further information, I'm trying to pull out the first image in the results, so that I can then edit it programmatically. I need the URL enclosed in the response to the search query, but the way the documentation is worded is unclear enough, and solutions to this problem so sparse, that I've had to use trial and error to get to where I am. Any insight into how to accomplish this would be greatly appreciated.)
Cannot assign value of type 'Array<_>' to type 'Any?
The error tells you that imgl2 is of type Any?, but you're trying to assign it a value of type Array. Swift doesn't allow changing the type of a variable after it has been initialized. What you could do is to assign imgl2 as! Array to a new variable to avoid this error.
I also suggest you to have a look at Decodable protocol to create models corresponding to the JSON structure.

Best way to utilize retrieved object's properties to populate arrays and subsequent labels?

I have a database (parse-server) from which I can fetch objects which contain information. Some of the information in the properties of the objects are used to populate labels on table views. The way I have been populating, let's say, the userName and userLike labels are as follows:
Appending Different Arrays with the objects properties
var userName = [String]()
var userLikes = [String]()
func query(){
let commentsQuery = PFQuery(className: "UserStuff")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
for object in objects{
self.userName.append(object["userName"] as! String)
self.userLikes.append(object["userLikes"] as! String)
}
}
}
}
Ignore the fact that I don't have a .whereKey or any else statements to handle other cases... this is bare bones just for illustration of the question. Anyway, in this method, the userName and userLikes arrays are iterated through to populate the labels. The for object in objectss{} ensures that the indexes in one array (whether index 0,1,2,3,etc...) refers to/comes from the same object as the value in the index of the other array. However, I was wondering if would be better to do it as follows:
Appending the whole object to a PFObject array
var userObjects = [PFObject]()
func query(){
let commentsQuery = PFQuery(className: "UserStuff")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
for object in objects{
self.userName.append(object)
}
}
}
}
With this method I could instead populate the labels with something like:
userNameLabel.text = String((userObjects[0])["userName"])
In this method all properties of the object would be accessible form the same array. I can see that this may have some advantages, but is this definitively the better way to do it/should I switch immediately?
I am going to say that the answer is that the latter of the two is probably the better method. This is because in the former, the information from a particular object is only linked between arrays by the order in the array. Any accidental or incorrectly scripted functions involving .append or .remove could skew the order between arrays and then an object's name might be the 3rd index in the nameArray but its likes may end up being the 4th index in the likesArray and it would be difficult to amend this issue. With the latter method, all information regarding an object's properties are linked to the object itself in the array and this issue is avoided.

How to fetch only one column Data in Core Data using swift 3

I am new in CoreData and I am trying to fetch only one column Data. I am trying using below code:
//MARK :- Fetch All Calculation
func fetchUniqueParentAxis(testID : String) -> [String] {
var arrAxis : [String] = []
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "TesCalculation")
fetchRequest.predicate = NSPredicate(format: "testID == %# ",testID)
fetchRequest.includesPropertyValues = true
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.propertiesToFetch = ["parentAxis"]
do {
calculation = try AppDelegate.getContext().fetch(fetchRequest) as! [TesCalculation]//try AppDelegate.getContext().fetch(fetchRequest) as! [String : AnyObject]
}
catch let error {
//Handle Error
Helper.sharedInstance.Print(error as AnyObject)
}
}.
"parentAxis" is my a column and I want to fetch data of that column only .
You can absolutely fetch a subset of columns in CoreData. Yes, I agree Core Data IS an Object Graph solution that can use SQLite as a storage engine. However, fetching a subset of data instead of an entire NSManagedObject has been possible to do for a very long time in CoreData but recent changes have made it slightly different than before.
let fetchRequest = NSFetchRequest<NSDictionary>(entityName: TesCalculation.entity().name!)
fetchRequest.resultType = .dictionaryResultType
fetchRequest.predicate = NSPredicate(format: "testID == %# ",testID)
fetchRequest.propertiesToFetch = ["parentAxis"]
do {
let results = try AppDelegate.getContext().fetch(fetchRequest)
} catch let error as NSError {
print(error)
}
What this will return you is something that looks like this:
[
{
"parentAxis": "abc"
},
{
"parentAxis": "xyz"
}
]
The question was not about performance gain (even though there might be; I truly have no idea!) but rather if/how this can be done and this is how it can be done. Plus, I disagree with other statements made that it "doesn't make sense to have a property without an object." There are plenty of cases where you are allocating objects where all you need is a property or two for the need. This also comes in handy if you are loosely coupling Entities between multiple stores. Of course there are other methods for this as well (your xcdatamodel's Fetched Properties) so it just really depends on the use case.
Just to show that it has been around for some time this is in the header of the NSFetchRequest under NSFetchRequestResultType:
#available(iOS 3.0, *)
public static var dictionaryResultType: NSFetchRequestResultType { get }
Core data is an object model. It translates rows of the sql database into objects with properties. You are not running queries directly on the SQL, so you cannot do everything in core-data that you could do with SQL. You interact with object which interact with the database. In core-data you can have an object that is not faulted. It means that none of its properties are loaded into memory, but when you need them (when you access a property) it will fetch it from the database. You cannot have a object that has only some of its properties set. They are either all set (ie faulted) or none are set (it is not faulted).
There really isn't much to be gained in most cased by only fetch one or two columns. The extra data transfer is often minimal. The only exception is when you have a large blob of data as as property on the object. In that case you should store the blob on a separate entity and give it a one-to-one relationship. That way the expensive block of data isn't loaded into memory until it is requested.

Parse.com Query with swift 1.2 and string array

I am trying to query from parse.com and I would db receiving about 100 objects per time. I used the swift example code on their website, and the app doesn't build with that code. So I looked around and found that people were using code similar to this:
var query = PFQuery(className:"posts")
query.whereKey("post", equalTo: "true")
query.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
// do something
self.myDataArray = objects as! [String]
})
This does not work, because I am trying to convert PFObject to String
I would need to get the one one value from each object into a swift string array [String]. How do I get just the one text value, instead of the PFObject and how do I get it into the swift string array?
I don't speak swift very well, but the problem with the code is it's trying to cast the returned PFObject to a string, but you want to extract a string attribute, so (if you really want to do it):
for object in objects {
var someString = object.valueForKey("someAttributeName") as String
self.myDataArray.addObject(someString)
}
But please make sure you need to do this. I've noticed a lot of new parse/swift users (especially those who are populating tables) have the urge to discard the returned PFObjects in favor of just one of their attributes. Consider keeping the PFObjects and extracting the attributes later as you need them. You might find you'll need other attributes, too.
For starters, I would definitely recommend using the "if let" pattern to qualify your incoming data. This is a nice Swift feature that will help avoid run-time errors.
var query = PFQuery(className:"posts")
query.whereKey("post", equalTo: "true")
query.findObjectsInBackgroundWithBlock(
{ (objects: [AnyObject]?, error: NSError?) -> Void in
// check your incoming data and try to cast to array of "posts" objects.
if let foundPosts = objects as? [posts]
{
// iterate over posts and try to extract the attribute you're after
for post in foundPosts
{
// this won't crash if the value is nil
if let foundString = post.objectForKey("keyForStringYouWant") as? String
{
// found a good data value and was able to cast to string, add it to your array!
self.myDataArray.addObject(foundString)
}
}
})

Resources