Parse: Download array in swift - arrays

I'm trying to search for data that contains a certain string then download an array from that row. I know how to do this in objective-c but not in swift. Here's the code I have to search for it:
let query = PFQuery(className:"Wait")
query.whereKey("latitude", equalTo:self.latitude)
query.findObjectsInBackgroundWithBlock {
(objects, error: NSError?) -> Void in
if error == nil {
}
}
I'm not sure how to use "objects" to download the array from that row.

To make things more clear, this should be the closure signature:
query.findObjectsInBackgroundWithBlock {
(objects : [PFObject]?, error: NSError?) -> Void in
if error == nil {
}
}
Then you can iterate through objects like:
for object in objects {
print(object)
}

Related

Swift Array holding elements weakly

I am taking some inspiration from
https://marcosantadev.com/swift-arrays-holding-elements-weak-references/
and I want to be able to maintain an array holding weak references to its elements, so that in case those elements get released elsewhere in my code base, I don't retain them in my array.
I would like the implementation to be as type safe as possible, however should be reusable.
The strategy that I am using is declaring a Weak Reference container as so.
class WeakRefContainer<T> where T: AnyObject {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
Then I want to maintain an array of these WeakRefContainers, so I create an array extension:
extension Array where Element: WeakRefContainer<AnyObject> {
func compact() -> [WeakRefContainer<AnyObject>] {
return filter { $0.value != nil }
}
}
When calling the compact method, I am now able to clear up the array in case stuff needs to be cleaned up.
I am now having some compilation issues which am having trouble understanding.
Lets suppose I have a sample class
class SampleClass {
}
And I try to use everything as follows:
var weakReferencesArray = [WeakRefContainer<SampleClass>]()
let obj1 = WeakRefContainer.init(value: SampleClass())
let obj2 = WeakRefContainer.init(value: SampleClass())
weakReferencesArray.append(obj1)
weakReferencesArray.append(obj2)
weakReferencesArray.compact()
When I try to call compact I get the following error message:
MyPlayground.playground:29:21: 'WeakRefContainer<SampleClass>' is not a subtype of 'WeakRefContainer<AnyObject>'
Can anyone unblock me please? Thanks
Your code doesn't work because WeakRefContainer<SampleClass> is not a subclass of WeakRefContainer<AnyObject> because generics are invariant in Swift. Thus weakReferencesArray can't use the compact method added from the extension.
There is a workaround for this, via a protocol:
protocol WeakHolder {
var hasRef: Bool { get }
}
extension WeakRefContainer: WeakHolder {
var hasRef: Bool { return value != nil }
}
extension Array where Element: WeakHolder {
func compacted() -> [Element] {
return filter { $0.hasRef }
}
mutating func compact() {
self = compacted()
}
}
I also renamed compact to compacted, for better Swift semantics, and replaced the original compact by a mutating version.
You probably want the extension to apply to all [WeakRefContainer<T>] where T can be any type extending AnyObject.
extension Array where Element: WeakRefContainer<T> {
However, currently, parameterised extensions are not possible. See this proposal.
You can kind of work around this by making compact generic:
extension Array{
func compact<T>() -> [Element] where Element == WeakRefContainer<T> {
return filter { $0.value != nil }
}
}

Storing array from Parse into local array

When I try to store arrays from Parse into a local array I can only access it within the findObjectsInBackgroundWithBlock {...}. When I print it outside of that block, it shows []...
Code :
var qArray : [[Int]] = []
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className: "Trivia")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) in
if objects != nil {
if let objects = objects {
for object in objects {
self.qArray.append(object["mainPattern"] as! [Int])
}
print(self.qArray) // Prints a multi dimension array
}
}
if error != nil {
print(error)
}
}
print(self.qArray) // prints []
}
It's most likely because the array hasn't been populated yet because it's running in the background. You can try using dispatch_group to circumvent this issue.
I think you're misunderstanding what findInBackground means. It means the code after the callback continues to execute, so it calls query.findInBackground.... and then it continues with the next line, which is print(self.qArray). At some point later on, it hears back from the database and it executes all the code inside the Callback, which is when the array finally gets populated.

Error with Parse findObjectsInBackgroundWithBlock in Swift

I'm trying to retrive usernames from Parse's Server with findObjectsInBackgroundWithBlock. I want the results to be stored inside a NSArray, but I get an error in the code.
Here's it:
let query: PFQuery = PFUser.query()!
query.orderByAscending("username")
query.whereKey("username", notEqualTo: myUserId as! String)
query.findObjectsInBackgroundWithBlock {(objects:NSArray?, error:NSError?) -> Void in
}
And I'm getting this error:
Cannot convert value of type '(NSArray?, NSError?) -> Void' to expected argument type 'PFQueryArrayResultBlock?'
I know that everything is working when changing 'NSArray?' to '[PFObject]?', but I want to result to be an Array.. How can I solve this?
You need to do the following:
declare this:
var array:NSArray = NSArray()
query.findObjectsInBackgroundWithBlock {(objects:PFObject?, error:NSError?) -> Void in
if error != nil {
print("error")
} else {
if let objects = objects{
for object in objects {
self.append(object)
}
}
}
}
You can't force the conversion in the callback parameters. Your callback needs to respect the type that will be supplied and then your code in the callback can mutate the supplied parameters and store them.

Populating an array using Parse queries (Swift)

I am trying to populate an array with a query from my parse database. When I try to print out the content of the array, I get an EXC_BAD_INSTRUCTION error. It doesn't seem like I'm properly appending new elements into my array, I'd appreciate any sort of tips
func loadSampleTasks() {
tasks = [Task]()
let query = PFQuery(className: "Task")
query.whereKey("TaskName", equalTo: "kenny")
query.findObjectsInBackgroundWithBlock() {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil && objects != nil {
self.parseResults(objects!)
print(self.tasks) // this prints out Kenny object as expected
}
}
print(tasks) // prints an empty array
}
func parseResults(objects: Array<PFObject>){
for object in objects { //looping through returned data
print("no error in Parse lookup")
let parseResult1 = Task(name: object["TaskName"] as! String)
parseResult1?.completed = object["Completed"] as! Bool
print("Parse result in object loop: \(parseResult1!.name)")
tasks.append(parseResult1!)
}
}
Any help much appreciated!
To what thefredelement said, "This is happening because you're getting your parse results in a closure, which means it may execute after the function itself has already returned." Before this happens, though, it won't work properly and you'll come back with that error.
I got it to work. I had to tableView.reloadData() within the closure. Thank you so much for the help!

Retrieving values from an empty array

I want the user to upload an image, and other users leave replies to that image. Everything works fine so far except if the row for that specific image object is empty, the app crashes.
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my code :
override func viewDidLoad() {
super.viewDidLoad()
var error = ""
var query = PFQuery(className:"Posts")
query.getObjectInBackgroundWithId(cellID) {
(objects: PFObject!, error: NSError!) -> Void in
if error == nil {
var array = objects.objectForKey("replies") as [String] // <- when error occurs the compiler point here.
for object in array {
self.repliesArray.append(object as String)
}
} else {
self.displayError("Error", error: "Error retreiving")
}
self.tableView.reloadData()
}
}
Does this work? You can't append null objects to an array so this appends an empty string instead of null. Also you need the explanation mark so that it can be nil.
var array = objects.objectForKey("replies") as [String!]
for object in array {
if object != nil {
self.repliesArray.append(object as String)
}
else {
self.repliesArray.append("")
}
}
query.getObjectInBackgroundWithId(cellID) {
(objects: PFObject!, error: NSError!) -> Void in
if error == nil {
var array = objects.objectForKey("replies") as [String] // <- when error occurs the compiler point here.
The key here is that you're accepting an implicitly unwrapped optional (PFObject!) which is implying a promise that it will never be nil, but is nil.
Ideally (*), the type should be (objects: PFObject?, error: NSError?) -> Void to make the optionals explicit and force you to do the nil checking that is required. I believe you can do this even if the caller claims to send you (PFObject!, NSError!) -> Void, since I think Swift will make the implicit->explicit conversion for you.
If that's still impossible, then you will have to manually verify that objects is not nil before using it. (This is unlike #Dehli's solution, which checks that the things contained in objects are non-nil. That's not the problem; the problem is that objects itself is nil.)
(*) I say "ideally" here in terms of the existing interface. Passing a tuple of optional value/error is a lousy pattern. The better solution is to use something like a Result object.

Resources