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)
}
}
})
Related
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.
Core data is still a bit new for me, so I don't quite understand the ins and outs. I understand how to save the basics like Strings, Ints, etc in core data, but I don't quite understand how to save an array of custom objects into core data, or if that is even possible. From my research, my current understanding is I need to set the attribute to Binary Data, and set its identifier to [exercise] (making a fitness application). Unfortunately when I try to pass my array into the managed context, I get an error in my code that reads "Cannot assign value of type '[Exercise]' to type 'Data?'"
func save (completion: (_ finished: Bool) -> ()){
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let workout = Workout(context: managedContext)
workout.nameOfWorkout = workoutNameField.text
workout.exercises = exercises // error appears on this line, both "exercises" are arrays
}
I guess my question is, is what I am attempting to do even possible? If so, what steps am I missing? I read somewhere to convert the array into NSData, and change it back when it needs to be accessed, but my concern is that when I try to change it back, it won't work as planned. Sorry for the long winded question, just want to make sure I'm including all details I can think of.
For storing custom types in CoreData, the fastest way is:
making your custom types (Exercise) subclass of NSObject
setting the attribute's type in the core data model to Transformable
setting the CustomClass to [Exercise]
So you have to define your Exercise class similar to:
public class Exercise: NSObject {
let name: String
let duration: TimeInterval
init(name: String, duration: TimeInterval) {
self.name = name
self.duration = duration
}
}
Then you go to the model definition and set the attribute type and CustomClass field:
CoreData Model Example
You can now use your save function as:
func save (completion: (_ finished: Bool) -> ()){
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let workout = Workout(context: managedContext)
workout.nameOfWorkout = workoutNameField.text
workout.exercises = exercises
}
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.
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.
I'm working on some code where I have a Food object and I have an array of Food objects: var list: [Food] = [] (this line of code is called right at the beginning as a global variable) I have lines of code that make an array in parse but i'm not really sure it is working, it still says "undefined" in parse when it should be a Food Object array. My question is really simple.
1 - How do I make a Food object array in Parse.com?
2 - How do I get the information from that array and make the parse array a local variable?
3 - How to append that array, I know I can just call .append(customFoodObject) to the local array, is that how I should do it?
4 - How, once i've edited the array, do I save it back to parse?
: Just those 4 questions, i've been told many answers like "just use a query" or "make new objects" but I gave the names of the actual objects because I would appreciate it if you gave code that I could see works and then understand how it works; thanks in advance for all you help.
Anyway the questions demands for a huge manuscript, nonetheless i will try to answer your question with some explanations and code snippets where possible.
Objects contain similar stuffs so they would be a row in a table in a database. After all Parse is a sugar topped database API. So a object would be mapped to a Class in Parse or specifically a row of the class of type Food.
Creating a Food object in Parse is pretty straightforward as the documentation is fairly explanatory.
let food = PFObject(className: "Food")
food["name"] = "Sushi Pizza"
food["price"] = "1100¥"
//saves on the server even if the networks fails now
//caches the result offline and when the phone enters in network it uploads
food.saveEventually(nil)
For storing array of foods do this:
let foodClassName = "Food"
for index in foods!{
let object = PFObject(className: foodClassName)
object["name"] = index.name
object["price"] = index.price
object.saveEventually(nil)
}
Basically you create a Food table with the className and then insert similar objects and save it.
Getting array is querying the parse database. All you have to know is the name of the Class Parse uses. In our case we had if "Food" stored in a constant.
let query = PFQuery(className: foodClassName)
//query.fromLocalDatastore() //uncomment to query from the offline store
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil && objects != nil{
for index in objects!{
(index as! PFObject).pin() //pining saves the object in offline parse db
//the offline parse db is created automatically
self.makeLocalVariable(index as! PFObject) //this makes the local food object
}
}
}
So to save it to locl Food object we initially had we transform the PFObject like so
var localFoods:[Food]? //global scoped variable
func makeLocalVariable(index:PFObject){
let foodname = index.objectForKey("name") as! String
let price = index.objectForKey("price") as! String //we had the currrecny saved too
let foodObj = Food(name: foodname, price: price)
localFoods?.append(foodObj)
}
Yes i did it that way. Thats correct.
4.So the process is basically similar to fetching data. Now what you do is suppose fetch data for Food with name Pizza because we want to increase the price. Heres how you do that.
let queryEdit = PFQuery(className: foodClassName)
queryEdit.whereKey("name", equalTo: "Pizza")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil && objects != nil{
if objects!.count == 0{
//edit
let obj = (objects as! [PFObject]).first!
obj.setValue("2340¥", forKey: "price")
//save it back
obj.saveEventually(nil)
}
}
}
I hoped i answered your questions. Remember a Object can be mapped to Class in Parse or Table or Entity in a Relation Database. Then that table can have many similar instances and you can say they are array of one type.
Let me know if i can help you more. Cheers!
Food for this example is just a Struct but can be easily a class if it has functionalities.
struct Food{
var name:String?
var price:String?
}