Hi Im developing a chat app with parse and I have one group-chat tab. What I would like to accomplish Is that when the user registers he/she can choose from a list of "tags" for example, "cats", "dogs" and "cars". Then when the user is signed in the groups tab only shows group-chats associated to the chosen tags. Right now i have a function where the user can create a group-chat but If possible i want to remove that and use my idea that I explained above.
Here's my code for the group's tab:
import UIKit
// Parse loaded from SwiftParseChat-Bridging-Header.h
class GroupsViewController: UITableViewController, UIAlertViewDelegate {
var groups: [PFObject]! = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if PFUser.currentUser() != nil {
self.loadGroups()
}
else {
Utilities.loginUser(self)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadGroups() {
var query = PFQuery(className: PF_GROUPS_CLASS_NAME)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) in
if error == nil {
self.groups.removeAll()
self.groups.extend(objects as [PFObject]!)
self.tableView.reloadData()
} else {
ProgressHUD.showError("Network error")
println(error)
}
}
}
#IBAction func newButtonPressed(sender: UIBarButtonItem) {
self.actionNew()
}
func actionNew() {
var alert = UIAlertView(title: "Please enter a name for your group", message: "", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "OK")
alert.alertViewStyle = UIAlertViewStyle.PlainTextInput
alert.show()
}
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
if buttonIndex != alertView.cancelButtonIndex {
var textField = alertView.textFieldAtIndex(0);
if let text = textField!.text {
if countElements(text) > 0 {
var object = PFObject(className: PF_GROUPS_CLASS_NAME)
object[PF_GROUPS_NAME] = text
object.saveInBackgroundWithBlock({ (success: Bool, error: NSError!) -> Void in
if success {
self.loadGroups()
} else {
ProgressHUD.showError("Network error")
println(error)
}
})
}
}
}
}
// MARK: - TableView Data Source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.groups.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
var group = self.groups[indexPath.row]
cell.textLabel?.text = group[PF_GROUPS_NAME] as? String
var query = PFQuery(className: PF_CHAT_CLASS_NAME)
query.whereKey(PF_CHAT_GROUPID, equalTo: group.objectId)
query.orderByDescending(PF_CHAT_CREATEDAT)
query.limit = 1000
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if let chat = objects.first as? PFObject {
let date = NSDate()
let seconds = date.timeIntervalSinceDate(chat.createdAt)
let elapsed = Utilities.timeElapsed(seconds);
let countString = (objects.count > 1) ? "\(objects.count) meddelanden" : "\(objects.count) meddelande"
cell.detailTextLabel?.text = "\(countString) \(elapsed)"
} else {
cell.detailTextLabel?.text = "0 meddelanden"
}
cell.detailTextLabel?.textColor = UIColor.lightGrayColor()
}
return cell
}
// MARK: - TableView Delegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
var group = self.groups[indexPath.row]
let groupId = group.objectId as String
Messages.createMessageItem(PFUser(), groupId: groupId, description: group[PF_GROUPS_NAME] as String)
self.performSegueWithIdentifier("groupChatSegue", sender: groupId)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "groupChatSegue" {
let chatVC = segue.destinationViewController as ChatViewController
chatVC.hidesBottomBarWhenPushed = true
let groupId = sender as String
chatVC.groupId = groupId
}
}
}
It sounds like you want to query a class (the one containing chats) with the constraint that an array column property of that class (the one listing each chat's tags) contains some list of tags related to the current user. PFQuery has a method called whereKey:containsAllObjectsInArray: which does exactly that.
To make it work, you need to be clear about whether the list of tags is an array of pointers to tag objects, or an array of strings that are tag names. Naturally, if tags on chats are stored as pointers to objects, then the second parameter should be an array of PFObject, and if they are strings, then the array parameter must be an array of NSString.
Unrelated to the semantics of the query, but important: Doing an unguarded, asynch query for anything in cellForRowAtIndexPath: will be unproductive. This method is called every time a cell scrolls into view, which happens arbitrarily often, and arbitrarily rapidly. Use another method to fetch the table's datasource, then reload the table once the data arrives.
(I think the post has been given a close vote because it asks a specific question, then presents the reader with a lot of code, much of it unrelated, leaving the reader to find the relevant query, and showing no indication of what's been tried so far. The site will work better for you if you give maximum help to those who might answer an otherwise good question).
Related
This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 6 years ago.
I'm working on an app Witch has a list of places (added by the user) and a map view with a button to add the users curent location. the app works to this point. When the user taps on a place in the tableView the app should go to the mapView and show the place on the map with a pin . but when I do that the app crashes and gives me the following error unless the user has renamed the place and if they have, it works perfectly :
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
note that the crash occurs in the mapView on the line that says:
let latitude = NSString(string: places[activePlace]["lat"]!).doubleValue
and that the
print("activePlace current value is \(activePlace)")
returns the number of the line in the TableView
How could I solve this? thanks !
Here is the code in the TableView:
import UIKit
var places = [Dictionary<String,String>()]
var activePlace = -1
class TableViewController: UITableViewController {
func companyNameUpdatedAlert(title: String, error: String, indexPath: Int) {
let alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.Alert)
alert.addTextFieldWithConfigurationHandler { (textField) -> Void in
textField.placeholder = "Enter new text"
}
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
let lat = places[indexPath]["lat"]!
let lon = places[indexPath]["lon"]!
places.removeAtIndex(indexPath)
places.insert(["name" : alert.textFields![0].text!, "lat" : lat, "lon" : lon], atIndex: indexPath)
self.tableView.reloadData()
NSUserDefaults.standardUserDefaults().setObject(places, forKey: "places")
NSUserDefaults.standardUserDefaults().synchronize()
}))
self.presentViewController(alert, animated: true, completion: nil)
}
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let changeText = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Change text" , handler: { (action:UITableViewRowAction, indexPath:NSIndexPath) -> Void in
self.companyNameUpdatedAlert("Update text", error: "enter text below", indexPath: indexPath.row)
})
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete" , handler: { (action:UITableViewRowAction, indexPath:NSIndexPath) -> Void in
places.removeAtIndex(indexPath.row)
tableView.reloadData()
NSUserDefaults.standardUserDefaults().setObject(places, forKey: "places")
NSUserDefaults.standardUserDefaults().synchronize()
})
return [changeText, deleteAction]
}
override func viewDidLoad() {
//save start
if NSUserDefaults.standardUserDefaults().objectForKey("places") != nil {
places = NSUserDefaults.standardUserDefaults().objectForKey("places") as! [Dictionary]
//test
NSUserDefaults.standardUserDefaults().setObject(places, forKey: "places")
NSUserDefaults.standardUserDefaults().synchronize()
//save stop
super.viewDidLoad()
if places.count == 1 {
places.removeAtIndex(0)
places.append(["name":"go to map to add location","lat":"90","lon":"90"])
}
if NSUserDefaults.standardUserDefaults().objectForKey("places") != nil {
places = NSUserDefaults.standardUserDefaults().objectForKey("places") as! [Dictionary]
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return places.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = places[indexPath.row]["name"]
return cell
}
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
activePlace = indexPath.row
return indexPath
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "newPlace" {
activePlace = -1
}
}
override func viewWillAppear(animated: Bool) {
tableView.reloadData()
}
}
and here is the code in the viewController ( mapView)
if activePlace == -1 {
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
} else {
print("activePlace current value is \(activePlace)")
let latitude = NSString(string: places[activePlace]["lat"]!).doubleValue
let longitude = NSString(string: places[activePlace]["lon"]!).doubleValue
let coordinate = CLLocationCoordinate2DMake(latitude, longitude)
let latDelta:CLLocationDegrees = 0.01
let lonDelta:CLLocationDegrees = 0.01
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let region:MKCoordinateRegion = MKCoordinateRegionMake(coordinate, span)
self.Map.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = places[activePlace]["name"]
self.Map.addAnnotation(annotation)
print("activePlaces current value is (activePlaces)")
First of all, You can create custom class, container. Like UserData with required information like description and location. Second, You have, for free from framework, a container for location CLLocationCoordinate2D, use it. If You will have just array of simple objects, it will be easier to read, find bugs and maintain application.
Your error simply says that element of array that You are trying to get is nil. Remember that in Swift Arrays, Dictionaries and other containers are passed via value, not via reference, because they are STRUCTS!
And Your problem is in prepareForSegue, why You are not passing selected item here to destinationViewController? You should do that.
P.S. Do not use NSUserDefaults.standardUserDefaults() to store user data. You can use CoreData instead, it is pretty simple to use. You need just a little extra code in AppDelegate and some changes in TableView.
The exclamation mark means: Dear compiler, I have an optional value. I am absolutely sure that it isn't nil. Should I be wrong, and should that optional value be nil when you unwrap it even though I was sure it isn't, then please crash and give me a fatal error "unexpectedly found nil while unwrapping optional value".
That's what you tell the compiler, and that's what you got. Where Objective-C lets you get away with things and maybe create strange behaviour, Swift doesn't.
Learn about "if let", guard statements, and in general read a good book about Swift.
I want to improve the MPCRevisited project which is Chat app that using multi peer method. I'm using BLE to connect one device to another device (iPad and iPod) and send and receive the data. However, when I press home button to make background mode on one device, after 5 seconds, I can't send or receive the data.
image description here
I've already check all the thing in background modes, but still its not working at all.
import UIKit
import MultipeerConnectivity
class ParkBenchTimer {
let startTime:CFAbsoluteTime
var endTime:CFAbsoluteTime?
init() {
startTime = CFAbsoluteTimeGetCurrent()
}
func stop() -> CFAbsoluteTime {
endTime = CFAbsoluteTimeGetCurrent()
return duration!
}
var duration:CFAbsoluteTime? {
if let endTime = endTime {
return endTime - startTime
} else {
return nil
}
}
}
class ChatViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var chatTextField: UITextField!
#IBOutlet weak var chatTableView: UITableView!
var messagesArray: [[String : String]] = []
let mpcManager = MPCManager.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.chatTableView.delegate = self
self.chatTableView.dataSource = self
self.chatTableView.estimatedRowHeight = 60.0
self.chatTableView.rowHeight = UITableViewAutomaticDimension
self.chatTextField.delegate = self
self.mpcManager.messageRecievedDelegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// MARK: IBAction method implementation
#IBAction func endChat(sender: AnyObject) {
let messageDictionary: [String: String] = ["message": "_end_chat_"]
if self.mpcManager.sendData(dictionaryWithData: messageDictionary, toPeer: self.mpcManager.session.connectedPeers[0] as MCPeerID){
self.dismissViewControllerAnimated(true, completion: { () -> Void in
self.mpcManager.session.disconnect()
})
}
}
// MARK: UITableView related method implementation
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.messagesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCellWithIdentifier("idCell") else {
assert(true)
return UITableViewCell()
}
guard let currentMessage = self.messagesArray[safe: indexPath.row] else {
print(" ")
assert(true)
return UITableViewCell()
}
if let sender = currentMessage["sender"] {
var senderLabelText: String
var senderColor: UIColor
if sender == "self" {
senderLabelText = "I said:"
senderColor = UIColor.purpleColor()
} else {
senderLabelText = sender + " said:"
senderColor = UIColor.orangeColor()
}
cell.detailTextLabel?.text = senderLabelText
cell.detailTextLabel?.textColor = senderColor
}
if let message = currentMessage["message"] {
cell.textLabel?.text = message
}
return cell
}
// MARK: UITextFieldDelegate method implementation
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
guard let textFieldText = textField.text else {
assert(true)
return false
}
let messageDictionary: [String: String] = ["message": textFieldText]
guard let connectedPeer = self.mpcManager.session.connectedPeers[safe: 0] else {
print(" ")
assert(true)
return false
}
if self.mpcManager.sendData(dictionaryWithData: messageDictionary, toPeer: connectedPeer) {
let dictionary = ["sender": "self", "message": textFieldText]
self.messagesArray.append(dictionary)
self.updateTableview()
} else {
print("Could not send data")
}
textField.text = ""
return true
}
// MARK: Custom method implementation
func updateTableview(){
chatTableView.reloadData()
if self.chatTableView.contentSize.height > self.chatTableView.frame.size.height {
let indexPathToScrollTo = NSIndexPath(forRow: messagesArray.count - 1, inSection: 0)
self.chatTableView.scrollToRowAtIndexPath(indexPathToScrollTo, atScrollPosition: .Bottom, animated: true)
}
}
}
extension ChatViewController : MPCManagerRecievedMessageDelegate {
func managerRecievedData(data:NSData ,fromPeer:MCPeerID) {
// Convert the data (NSData) into a Dictionary object.
let dataDictionary = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [String : String]
// Check if there's an entry with the "message" key.
if let message = dataDictionary["message"] {
// Make sure that the message is other than "_end_chat_".
if message != "_end_chat_"{
// Create a new dictionary and set the sender and the received message to it.
let messageDictionary: [String: String] = ["sender": fromPeer.displayName, "message": message]
// Add this dictionary to the messagesArray array.
messagesArray.append(messageDictionary)
// Reload the tableview data and scroll to the bottom using the main thread.
self.updateTableview()
} else {
}
}
}
func managerDidRecievedMessage(message: String, fromPeer: MCPeerID) {
// Create a new dictionary and set the sender and the received message to it.
//let messageDictionary: [String: String] = ["sender": fromPeer.displayName, "message": message]
// Add this dictionary to the messagesArray array.
//messagesArray.append(messageDictionary)
// Reload the tableview data and scroll to the bottom using the main thread.
//self.updateTableview()
}
func managerDidEndChat(fromPeer:MCPeerID) {
// In this case an "_end_chat_" message was received.
// Show an alert view to the user.
let alert = UIAlertController(title: "", message: "\(fromPeer.displayName) ended this chat.", preferredStyle: UIAlertControllerStyle.Alert)
let doneAction: UIAlertAction = UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default) { (alertAction) -> Void in
self.mpcManager.session.disconnect()
self.dismissViewControllerAnimated(true, completion: nil)
}
alert.addAction(doneAction)
self.presentViewController(alert, animated: true, completion: nil)
}
}
This is my code.
Please help me if someone knows this problem. What I want to do is one device to keep sending the message and other device to become background and foreground back and forth.
Thank you.
Looking at some other StackOverflow posts (here and here), it seems like the Multipeer Connectivity Framework is not built to function in the background and your functionality will disappear after a couple minutes.
Bluetooth will function in the background, with the capabilities that you checked, but you will have to create your own messaging platform; even though Multipeer relies partially on Bluetooth, the capabilities are separate entities.
I am trying to pull an array of strings in the from "my_classes" in the "User" class in Parse. I want each individual string within the array to become a separate cell in a tableview when I tap on the search button. This is my array in "my_classes" : ["Physics","Economics","Pre Calculus"]. I want "Physics" as it's own cell, "Economics" as its own cell, etc.
import UIKit
import Parse
class CardSetClassTableViewController: UITableViewController, UISearchBarDelegate {
// MARK: Outlets
#IBOutlet var searchBar: UISearchBar!
#IBOutlet var resultsTableView: UITableView!
// MARK: Variables
var searchResults = [String]()
// MARK: Actions
#IBAction func newClassBarButtonItemPressed(sender: AnyObject) {
self.performSegueWithIdentifier("newClassSegue", sender: self)
}
// MARK: Functions
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle:UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
func searchBarSearchButtonClicked(searchBar: UISearchBar)
{
if reachabilityStatus == kNOTREACHABLE {
self.displayAlert("No Internet Connection", message: "Please connect to the internet before continuing.")
} else {
searchBar.resignFirstResponder()
print("Search word = \(searchBar.text!)")
let classNameQuery = PFQuery(className:"_User")
classNameQuery.whereKey("my_classes".lowercaseString, equalTo: searchBar.text!.lowercaseString)
let query = PFQuery.orQueryWithSubqueries([classNameQuery])
query.findObjectsInBackgroundWithBlock {
(results: [PFObject]?, error: NSError?) -> Void in
if error != nil {
self.displayAlert("Error", message: error!.localizedDescription)
return
}
if let objects = results {
self.searchResults.removeAll(keepCapacity: false)
for object in objects {
let className = object.valueForKey("my_classes") as! String
self.searchResults.append(className)
}
dispatch_async(dispatch_get_main_queue()) {
self.resultsTableView.reloadData()
self.searchBar.resignFirstResponder()
}
}
}
}
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel!.text = searchResults[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let classIndexPath = tableView.indexPathForSelectedRow!
let selectedCell = tableView.cellForRowAtIndexPath(classIndexPath)! as UITableViewCell
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.labelText = "Loading"
if reachabilityStatus == kNOTREACHABLE {
spinningActivity.hide(true)
self.displayAlert("No Internet Connection", message: "Please connect to the internet before continuing.")
} else {
// let className : String = String(selectedCell.textLabel!.text!)
self.performSegueWithIdentifier("addCardSet", sender: self)
}
searchBar.resignFirstResponder()
}
}
Thanks!
Try the following...
Edit
var songsArray = [String]()
func fetchUsers() {
let userQuery: PFQuery = PFUser.query()!
//search users by the sepcified username, returns a users! object
//make an array to put the values from the users! array object into
//then append those from your "middle-man" array into your destination array,
//in this example songArray is destination array and songsFromParse is "middle-man" array
userQuery.whereKey("username", equalTo: (username)!)
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
var songsFromParse = users!
if error == nil {
if songsFromParse.count != 0 {
self.songsArray = (songsFromParse[i].valueForKey("CurrentSongURLArray") as! Array)
}
self.tableView.reloadData()
} else {
print(error)
}
})
}
You then take your new array that contains the objects that you retrieved, in this example songsArray and use it to populate your tableView. In cellForRowAtIndexPath ...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell ID")
cell?.textLabel?.text = songsArray[indexPath]
return cell!
}
Whenever I run my app, the first time the view containing the tableview loads, the table view does not get populated. But once i switch tabs (i am using a tab bar controller), and then switch back, the table view becomes populated. Does anyone have any ideas on how to fix this?
Here is the code:
import UIKit
import Parse
class NotesViewController: UITableViewController {
var notes = [PFObject]()
var cacheNotes = Bool()
#IBOutlet var showContentTableView: UITableView!
func findNotes() {
if let user = PFUser.currentUser()?.username! {
let network = Reachability.isConnectedToNetwork()
let query = PFQuery(className: "notes")
query.whereKey("username", equalTo: user)
if network == true {
print("connected to network")
} else {
("not connected to network")
query.fromLocalDatastore()
}
query.findObjectsInBackgroundWithBlock({ (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
self.notes = objects!
for object in objects! {
if self.cacheNotes == true && network == true {object.pinInBackground()}
}
}
})
}
showContentTableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
if NSUserDefaults.standardUserDefaults().objectForKey("cacheNotes") == nil {
NSUserDefaults.standardUserDefaults().setObject(true, forKey: "cacheNotes")
}
}
override func viewDidAppear(animated: Bool) {
cacheNotes = NSUserDefaults.standardUserDefaults().objectForKey("cacheNotes") as! Bool
print(cacheNotes)
findNotes()
showContentTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return notes.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
// Configure the cell...
cell.textLabel?.text = notes[indexPath.row]["title"] as? String
return cell
}
}
When you retrieve data from Parse using findObjectsInBackgroundWithBlock the network operation happens in the background, while the rest of the function keeps executing. So, the showContentTableView.reloadData() happens immediately, before the data has been returned. You need to reload the table data once the network operation has completed, that is, inside the block/closure
func findNotes() {
if let user = PFUser.currentUser()?.username! {
let network = Reachability.isConnectedToNetwork()
let query = PFQuery(className: "notes")
query.whereKey("username", equalTo: user)
if network == true {
print("connected to network")
} else {
("not connected to network")
query.fromLocalDatastore()
}
query.findObjectsInBackgroundWithBlock({ (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
self.notes = objects!
showContentTableView.reloadData()
for object in objects! {
if self.cacheNotes == true && network == true {object.pinInBackground()}
}
}
})
}
}
Also, I would suggest that you call findNotes from viewWillAppear rather than viewDidAppear - it will get a head start on populating the data before the table is onscreen.
I have seen a similar request but did not respond to my problem.
I am building a simple app notes but when I make the login, the data saved on Parse appear for a few seconds on the TableView and then disappear. What am I doing wrong?
thank you all
import UIKit
class MasterTableViewController: UITableViewController, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate {
var noteObjects: NSMutableArray! = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.loginSetup()
}
// Parse - Login
func logInViewController(logInController: PFLogInViewController, shouldBeginLogInWithUsername username: String, password: String) -> Bool {
if (!username.isEmpty || !password.isEmpty) {
return true
} else {
return false
}
}
func logInViewController(logInController: PFLogInViewController, didLogInUser user: PFUser) {
self.dismissViewControllerAnimated( true, completion: nil )
}
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError: NSError?)
{
println("Faild to log in..")
}
// Parse - sign up
func signUpViewController(signUpController: PFSignUpViewController, shouldBeginSignUp info: [NSObject : AnyObject]) -> Bool {
if let password = info["password"] as? String {
return count(password.utf16) >= 8
} else {
return false
}
}
func signUpViewController(signUpController: PFSignUpViewController, didSignUpUser user: PFUser) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func signUpViewController(signUpController: PFSignUpViewController, didFailToSignUpWithError error: NSError?) {
println("Faild to sign up")
}
func signUpViewControllerDidCancelSignUp(signUpController: PFSignUpViewController) {
println("User dismissed sign up")
}
// Parse - logout
#IBAction func logoutAction(sender: AnyObject) {
PFUser.logOut()
self.loginSetup()
}
func loginSetup() {
if (PFUser.currentUser() == nil) {
var logInViewController = PFLogInViewController()
logInViewController.delegate = self
var signUpViewController = PFSignUpViewController()
signUpViewController.delegate = self
logInViewController.signUpController = signUpViewController
self.presentViewController(logInViewController, animated: true, completion: nil)
}else {
self.fetchAllObjectsFromLocalDatastore()
self.fetchAllObjects()
}
}
/*
func fetchAllObjectsFromLocalDatastore() {
var query: PFQuery = PFQuery(className: "Note")
//query.cachePolicy = .CacheElseNetwork
query.fromLocalDatastore()
query.whereKey("username", equalTo: PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if (error == nil) {
if let objects = objects as? [PFObject] {
for object in objects {
println(object.objectId)
}
}
//var temp: PFObject = PFObject()
//self.noteObjects = temp.mutableCopy() as! NSMutableArray
//println(self.noteObjects)
self.tableView.reloadData()
}else {
println(error!.userInfo)
}
}
}
func fetchAllObjects() {
PFObject.unpinAllObjectsInBackgroundWithBlock(nil)
var query: PFQuery = PFQuery(className: "Note")
//query.cachePolicy = .CacheElseNetwork
query.whereKey("username", equalTo: PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if (error == nil) {
PFObject.pinAllInBackground(objects, block: nil)
self.fetchAllObjectsFromLocalDatastore()
}else {
println(error!.userInfo)
}
}
}
*/
func fetchAllObjectsFromLocalDatastore() {
var query: PFQuery = PFQuery(className: "Note")
query.fromLocalDatastore()
query.whereKey("username", equalTo: PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if (error == nil) {
//var temp: NSArray = objects as! [NSArray]
var temp: NSArray = objects! as NSArray
self.noteObjects = temp.mutableCopy() as! NSMutableArray
self.tableView.reloadData()
}else {
println(error!.userInfo)
}
}
}
func fetchAllObjects() {
PFObject.unpinAllObjectsInBackgroundWithBlock(nil)
var query: PFQuery = PFQuery(className: "Note")
query.whereKey("username", equalTo: PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if (error == nil) {
PFObject.pinAllInBackground(objects, block: nil)
self.fetchAllObjectsFromLocalDatastore()
}else {
println(error!.userInfo)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return noteObjects!.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MasterTableViewCell
var object: PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
cell.masterTitleLabel?.text = object["title"] as? String
cell.masterTextLabel?.text = object["text"] as? String
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("editNote", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var upcoming: AddNoteTableViewController = segue.destinationViewController as! AddNoteTableViewController
if (segue.identifier == "editNote") {
let indexPath = self.tableView.indexPathForSelectedRow()!
var object: PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
upcoming.object = object;
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
}