I am trying to load data from my back endless tables, but I can't get the table to load correctly. It just stays blank.I tried it in multiple places and they would cause a crash. The only place it can go is after the last error call. The table stays blank however. The relevant code is in my fetchingUserDataAsync() function.
Here is my table view controller:
import UIKit
class UserTableViewController: UITableViewController {
var backendless = Backendless.sharedInstance()
let follow:Follow = Follow()
var usernames = [""]
var userids = [""]
var userEmails = [""]
var isFollowing = [false]
func fetchingUserDataAsync() {
self.userids.removeAll(keepCapacity: true)
self.usernames.removeAll(keepCapacity: true)
self.isFollowing.removeAll(keepCapacity: true)
print("\n============ Fetching user data using the ASYNC API ============")
let query = BackendlessDataQuery()
backendless.persistenceService.of(BackendlessUser.ofClass()).find(
query,
response: { ( name : BackendlessCollection!) -> () in
let currentPage = name.getCurrentPage()
for names in currentPage as! [BackendlessUser] {
//print("User name = \(names.name)")
if(names.name != self.backendless.userService.currentUser.name){
self.usernames.append(names.name)
self.backendless.persistenceService.of(BackendlessUser.ofClass()).find(
query,
response: { ( objectId : BackendlessCollection!) -> () in
let currentPage = objectId.getCurrentPage()
for objects in currentPage as! [BackendlessUser] {
//print("User id = \(objects.objectId)")
if(objects.objectId != self.backendless.userService.currentUser.objectId){
self.userids.append(objects.objectId)
let currentUser = self.backendless.userService.currentUser
let user = objects.objectId
let whereClauseOne = "follower = '\(currentUser.objectId)' "
let whereClauseTwo = "following= '\(user)'"
//print ("I am following'\(whereClauseTwo)'")
self.backendless.persistenceService.of(objects.ofClass()).find(
query,
response: { objects -> () in
if let objects = objects{
self.isFollowing.append(true)
} else{
self.isFollowing.append(false)
}
},
error: { ( fault : Fault!) -> () in
print("Server reported an error: \(fault)")
}
)
}
}
},
error: { ( fault : Fault!) -> () in
print("Server reported an error: \(fault)")
}
)
}
}
},
error: { ( fault : Fault!) -> () in
print("Server reported an error: \(fault)")
}
)
//////////////////////////////////////////////////////////////////////////////////
self.tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
fetchingUserDataAsync()
}
You need to call reload data after your data arrays are filled with objects , you need to put it inside the response closure and not outside because the response closure is called Async some time later and your reload data as it is now called immediately in your view did load.
Of course when you put it inside you need to dispatch the reload data to the main queue.
Related
I have an issue with code order in a ItemsViewController.swift
When I run my code it starts the for items loop before my api returns the values for the items. This is done in the line: self.viewModel/getItemsTwo... Therefore it thinks that items is nil by the time the loop starts, so it errors with:
fatal error: unexpectedly found nil while unwrapping an Optional value
How can I start the loop only after items has been filled by the api call/function call?
class ItemsViewController: UIViewController {
private let viewModel : ItemsViewModel = ItemsViewModel()
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.getItemsTwo(self.viewModel.getCurrentUser())
var items = self.viewModel.items
for item in items! {
print(item)
}
}
...
The getItemsTwo function in the viewModel sets the viewModel.items variable when it is called
EDIT 1
ItemsViewModel.swift
...
var items : JSON?
...
func getItemsTwo(user: MYUser) {
let user_id = user.getUserId()
let url = String(format:"users/%#/items", user_id)
self.get(url).responseJSON { (response) -> Void in
let dataExample = response.data
var newdata = JSON(data: dataExample!)
self.items = newdata
}
}
...
EDIT 2
I am trying to do this:
just change it in the ViewController to:
var items = self.viewModel.getItemsTwo(self.viewModel.getCurrentUser())
and the ViewModel to:
func getItemsTwo(user: MYUser) -> JSON {
let user_id = user.getUserId()
let url = String(format:"users/%#/items", user_id)
self.get(url).responseJSON { (response) -> Void in
let dataExample = response.data
var newdata = JSON(data: dataExample!)
self.items = newdata
}
return self.items
}
But the return statement still errors as if self.items in nil.
Maybe you could expand your getItemsTwo method to take a callback closure, something like:
func getItemsTwo(user: MYUser, callback: (items: [JSON])-> Void)
Meaning that you have a parameter called callback which is a closure function that returns Void and takes an array of JSON items as an input parameter.
Once you have added newdata to self.items you could call your callback closure like so:
func getItemsTwo(user: MYUser, callback: (items: [JSON])-> Void) {
let user_id = user.getUserId()
let url = String(format:"users/%#/items", user_id)
self.get(url).responseJSON { (response) -> Void in
let dataExample = response.data
var newdata = JSON(data: dataExample!)
self.items = new data
//Items are now populated, call callback
callback(items: self.items)
}
}
And then, in your ItemsViewController you could say:
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.getItemsTwo(self.viewModel.getCurrentUser()) { items in
for item in items {
print(item)
}
}
}
Notice that if you add a closure as the last parameter you can use a so called "Trailing Closure" and place it "outside" or "after" your function as described in this chapter of "The Swift Programming Language".
Hope that helps you (I haven't checked in a compiler so you might get some errors, but then well look at them OK :)).
I am trying to set up a following/ follower system for my app. I have set up the decoration and it displays. My problem is that it displays as always true even when I know it should be false. I have tried various if statements and I can't seem to use the correct variable or constant to run a check so that the array will return true or false. The array is isFollowing[false] initialized in that way.
Relevant Code
self.backendless.persistenceService.of(BackendlessUser.ofClass()).find( query, response: { ( objectId : BackendlessCollection!) -> () in
let currentPage = objectId.getCurrentPage()
for objects in currentPage as! [BackendlessUser] {
if (objects.objectId != self.backendless.userService.currentUser.objectId) {
self.userids.append(objects.objectId)
self.backendless.persistenceService.of(objects.ofClass()).find( query, response: { objects -> () in
if let objects = objects {
self.isFollowing.append(true)
} else {
self.isFollowing.append(false)
}
if self.isFollowing.count == self.usernames.count{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
}, error: { ( fault : Fault!) -> () in
print("Server reported an error: \(fault)")
}
)
}
}
})
Sorry for the ambiguous title. In order for me to save a specific object I need to query it, which returns a collection. How do I save that collection after I've edited it?
Any help appreciated.
func joinGroup() {
var group = Groups()
backendless.initApp(APP_ID, secret:SECRET_KEY, version:VERSION_NUM)
self.backendless.userService.getPersistentUser()
let user = self.backendless.userService.currentUser
let whereClause = "UsersInGroup.objectId = '\(user.objectId)'"
let dataQuery = BackendlessDataQuery()
dataQuery.whereClause = whereClause
var error: Fault?
var dataStore = self.backendless.persistenceService.find(Groups.ofClass(),
dataQuery:dataQuery) as BackendlessCollection
for joiningroup in dataStore.data as! [Groups] {
joiningroup.UsersInGroup.append(user)
}
Here's how objects are saved
func createGroup() {
let groups = self.backendless.persistenceService.of(Groups().ofClass())
backendless.initApp(APP_ID, secret:SECRET_KEY, version:VERSION_NUM)
self.backendless.userService.getPersistentUser()
let user = self.backendless.userService.currentUser
var newGroup = Groups()
newGroup.groupName = "FromClient"
newGroup.ownerId = user.objectId
newGroup.UsersInGroup.append(user)
var error: Fault?
newGroup = groups.save(newGroup) as! Groups
if error == nil {
print("Created group: \(newGroup.groupName)")
}
else {
print("Server reported an error: \(error)")
}
}
You can just save a parent object (provided you have loaded the child collection before), and the child collection will automatically be saved with your new object added to it.
func joinGroup() {
self.backendless.userService.getPersistentUser()
let user = self.backendless.userService.currentUser
let dataStore = backendless.data.of(Groups.ofClass())
dataStore.findID(
"A772D4AB-BCB6-1C2E-FFE9-C436C65E3200",
response: { (result: AnyObject!) -> Void in
let foundContact = result as! Groups
let groupJoining = foundContact
groupJoining.UsersInGroup.append(user)
dataStore.save(
groupJoining,
response: { (result: AnyObject!) -> Void in
let savedContact = result as! Groups
print("Contact has been saved: \(savedContact.objectId)")
},
error: { (fault: Fault!) -> Void in
print("Server reported an error (1): \(fault)")
})
print("Contact has been found: \(foundContact.objectId)")
},
error: { (fault: Fault!) -> Void in
print("Server reported an error (2): \(fault)")
})
}
I have a table called Assesment
it has the name and send values of each task
what I needed to do is to retrieve all these tasks and store them in an array
and here is my code which gives me an error saying that PFObject doesn't have a member called send:
override func viewDidLoad() {
super.viewDidLoad()
//test
var taskQuery = PFQuery(className: "Assesment")
//run query
taskQuery.findObjectsInBackgroundWithBlock({
(success:[AnyObject]?, error: NSError?) -> Void in
if (success != nil) {
for object:PFObject! in success as! [PFObject]{
ERROR>>>> taskMgr.addtask(object.name,send: object.name)
}
println(taskMgr)
}})
//test
// Do any additional setup after loading the view, typically from a nib.
}
even thought I tried to say instead
taskMgr.addtask(object)
AssesmentManager.swift class:
import UIKit
var taskMgr : AssesmentsManager = AssesmentsManager()
struct task {
var name = "Un-Named"
var send = false
}
class AssesmentsManager: NSObject {
var tasks = [task]()
func addtask(name: String, send: Bool) {
tasks.append(task(name: name, send: send))
}
}
UPDATE
if (success != nil) {
for object:PFObject! in success as! [PFObject]{
if object["send"]=="true"{
taskMgr.addtask(object["name"], true )
}
else{
taskMgr.addtask(object["name"], false )}
}
I updated it to remove the string, boolean problem but I still have the same error of not having a member named subscript
UPDATE#2
This is what it looks like now, but still giving me an error that objects is unresolved:
var taskQuery = PFQuery(className: "Assesment")
//run query
taskQuery.findObjectsInBackgroundWithBlock({
(success:[AnyObject]?, error: NSError?) -> Void in
if (success != nil) {
for object:PFObject! in success as! [PFObject]{
for object in objects {
taskMgr.addtask(object["name"], (object["send"] == "true"))
}
}
println(taskMgr)
}})
In Swift 2.0, findObjects returns optional array of PFObject instead of optional AnyObject. Try this
override func viewDidLoad() {
super.viewDidLoad()
var taskQuery = PFQuery(className: "Assesment")
taskQuery.findObjectsInBackgroundWithBlock {
(success:[PFObject]?, error: NSError?) -> Void in
if let objects = success {
for object in objects {
taskMgr.addtask(object["name"], (object["send"] == "true"))
//taskMgr.addtask(object["name"], (object["send"].isEqual("true")))
}
}
}
}
In my app I'm using Parse SDK to get list of medicines and amount from the database and pass it through the iPhone to the watch. I implemented on the watch two separate sendMessages in WillActivate() :
let iNeedMedicine = ["Value": "Query"]
session.sendMessage(iNeedMedicine, replyHandler: { (content:[String : AnyObject]) -> Void in
if let medicines = content["medicines"] as? [String] {
print(medicines)
self.table.setNumberOfRows(medicines.count, withRowType: "tableRowController")
for (index, medicine) in medicines.enumerate() {
let row = self.table.rowControllerAtIndex(index) as? tableRowController
if let row1 = row {
row1.medicineLabel.setText(medicine)
}
}
}
}, errorHandler: { (error ) -> Void in
print("We got an error from our watch device : " + error.domain)
})
Second:
let iNeedAmount = ["Value" : "Amount"]
session.sendMessage(iNeedAmount, replyHandler: { (content:[String : AnyObject]) -> Void in
if let quantity = content["quantity"] as? [String] {
print(quantity)
self.table.setNumberOfRows(quantity.count, withRowType: "tableRowController")
for (index, quant) in quantity.enumerate() {
let row = self.table.rowControllerAtIndex(index) as? tableRowController
row!.amountLabel.setText(quant)
}
}
}, errorHandler: { (error ) -> Void in
print("We got an error from our watch device : " + error.domain)
})
What i get is this: Problem. Is it because of two different messages ?
To display the medicine and the amount in the same table you could do the following:
Create a property let medicines = [(String, String?)]()
When the medicines arrive populate that array with the medicines. So that after this medicines looks like this [("Medicine1", nil), ("Medicine2", nil),...]
When the quantities arrive iterate over medicines and add the quantities to the array, so that it looks like this after that: [("Medicine1", "Quantity1"), ("Medicine2", "Quantity2"),...]
Use the medicines array to populate your table. Create a method that reloads the table:
Like this:
func reloadTable() {
self.table.setNumberOfRows(medicines.count, withRowType: "tableRowController")
var rowIndex = 0
for item in medicines {
if let row = self.table.rowControllerAtIndex(rowIndex) as? tableRowController {
row.medicineLabel.setText(item.0)
if let quantity = item.1 {
row.quantityLabel.setText(quantity)
}
rowIndex++
}
}
}
Call reloadTable() whenever you receive a message with quantity or data.
This is just a raw example to explain the idea. You have to be careful to keep the medicines and the quantities in sync. Especially when you load more data when the user scrolls down.
To fill the data from the messages into your array you can define two functions:
func addMedicines(medicineNames: [String]) {
for name in medicineNames {
medicines.append((name, nil))
}
}
func addQuantities(quantities: [String]) {
guard medicines.count == quantities.count else { return }
for i in 0..<medicines.count {
medicines[i].1 = quantities[i]
}
}