I am new to using swift/Parse and am having difficulty updating a user column in _User. The issue is that Parse is already running getObjectinBackgroundWithId, and the saveInBackground does not run (no success or error). If I run saveInBackgroundWithBlock without retrieving the user by their id, I have no problem saving.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var current = PFUser.currentUser().objectId
var userId = self.dictionary[indexPath.row]["userId"]
var query = PFQuery(className: "_User")
var user: PFObject?
self.query.getObjectInBackgroundWithId(userId) {
(success: PFObject!, error: NSError!) -> Void in
if success != nil {
success.addUniqueObject(current, forKey: "friendRequest")
user = success
}
}
var cell = tableView.cellForRowAtIndexPath(indexPath)
if cell?.accessoryType == UITableViewCellAccessoryType.Checkmark {
cell?.accessoryType = UITableViewCellAccessoryType.None
}
else {
self.searchController.active = false
let alertController = UIAlertController(title: "Alert", message: "Are you sure you want to add \(cell!.textLabel!.text!) to your friends?", preferredStyle: .Alert)
let OKAction = UIAlertAction(title: "Yes", style: .Default) { (action) in
user!.saveInBackgroundWithBlock {
(success: Bool!, error: NSError!) -> Void in
if success == true {
println("worked")
}
else {
println(error)
}
}
}
alertController.addAction(OKAction)
let cancelAction = UIAlertAction(title: "No", style: .Cancel) { (action) in
}
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true) {
}
}
}
Any help would be greatly appreciated! Thanks
Related
I add UICollectionView in my app and each cell has "Delete" trash button.
When I click on this button, it will show alert message to ask. After clicks on OK button, the item in Collection View will delete. However, the UI does not refresh , the item was not deleted.
My Code is ...
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
debugPrint("viewDidAppear")
callWatchLater()
}
override func viewWillAppear(_ animated: Bool) {
DispatchQueue.main.async(execute: {
debugPrint("Appear")
self.myCollectionView.reloadData()
})
}
override func viewDidLayoutSubviews() {
DispatchQueue.main.async(execute: {
debugPrint("SubViews")
self.myCollectionView.reloadData()
})
}
Code for Alert Controller is
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "watchCell", for: indexPath) as! WatchLaterTableCell
let watchTableList = self.watchLaterTable[indexPath.row]
cell.deleteMovie.addTarget(self, action:#selector(showAlert(sender:)), for: .touchUpInside)
}
return cell
}
func showAlert(sender:UIButton!)
{
debugPrint("Press Button")
let alert = UIAlertController(title: "Are you really want to delete this movie?", message: "", preferredStyle: UIAlertControllerStyle.alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
self.callRest()
debugPrint("Press OK")
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel) {
UIAlertAction in
_ = self.navigationController?.popViewController(animated: true)
}
// Add the actions
alert.addAction(okAction)
alert.addAction(cancelAction)
// Present the controller
DispatchQueue.main.async(execute: {
self.present(alert, animated: true, completion: nil)
})
}
Code For callRest() is
func callRest(){
SwiftLoading().showLoading()
if Reachability().isInternetAvailable() == true {
rest.auth(auth: access_token)
rest.delete(StringResource().mainURL + "user/" + user_id + "/watch/later/" + String(vedioId) , parma: [:], finished: {(result: NSDictionary, status : Int) -> Void in
debugPrint(result)
if(status == 200){
let data = result["data"] as! NSString
self.messages = String(describing: data)
DispatchQueue.main.sync{[unowned self] in
let alertController = UIAlertController(title: "", message: self.messages , preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
debugPrint("call rest Press OK")
self.callWatchLaterGetAPI()
}
alertController.addAction(okAction)
// Present the controller
DispatchQueue.main.async(execute: {
self.present(alertController, animated: true, completion: nil)
})
}
self.myCollectionView.reloadData()
SwiftLoading().hideLoading()
}else{
let error = result["error"] as! NSArray
for item in 0...(error.count) - 1 {
let device : AnyObject = error[item] as AnyObject
self.messages = device["message"] as! String
DispatchQueue.main.sync{[unowned self] in
let alertController = UIAlertController(title: "", message: self.messages , preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
debugPrint("Press OK")
}
alertController.addAction(okAction)
// Present the controller
DispatchQueue.main.async(execute: {
self.present(alertController, animated: true, completion: nil)
})
}
SwiftLoading().hideLoading()
}
}
})
}else{
noInternetConnection()
}
}
However, the CollectionView does not refresh . Can anyone help me this, please?
Is there a cleaner, swiftier solution to handle the optional chaining happening in my code below? I'm setting up the user for CloudKit access in my custom function runCKsetup():
func runCKsetup() {
container.requestApplicationPermission(.userDiscoverability) { (status, error) in
guard error == nil else {
if let error = error as? NSError {
if let errorDictionary: AnyObject = error.userInfo as? Dictionary<String, AnyObject> as AnyObject? {
let localizedDescription = errorDictionary["NSLocalizedDescription"]! as! String!
if localizedDescription! == "This operation has been rate limited" {
// Create an alert view
let alert = UIAlertController(title: "Network Error", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Test for Connection", style: UIAlertActionStyle.default) { (action) in
self.runCKsetup()
})
self.present(alert, animated: true, completion: nil)
} else {
// Create an alert view
let alert = UIAlertController(title: "Sign in to iCloud", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default) { (action) in
})
self.present(alert, animated: true, completion: nil)
}
}
}
return
}
if status == CKApplicationPermissionStatus.granted {
self.container.fetchUserRecordID { (recordID, error) in
guard error == nil else {
self.presentMessageAlert((error?.localizedDescription)!, title: "Error", buttonTitle: "Ok")
return }
guard let recordID = recordID else { return }
self.container.discoverUserIdentity(withUserRecordID: recordID, completionHandler: { (info, fetchError) in
//do something with the users names: e.g. print("\(info?.nameComponents?.givenName) \(info?.nameComponents?.familyName)")
})
}
}
}
}
I'm trying to implement a forgot password feature in my app so users can enter their email in and have it reset in the database and have the new generated password emailed to them.
I believe my code should work once I fix a few minor issues but the main hurdle I'm having is how to display an alert message from the handler of another alert message.
Any idea how to do it? Either the alert message doesn't show up at all or the first one doesn't close at all.
Here is my attempt at it:
//This function will send a password reset email
func emailPassword(alertAction: UIAlertAction!) -> Void {
let textField = reset_alert.textFields![0] as UITextField
if(!textField.text!.isEmpty){
if(textField != "mik"){
let query = PFQuery(className: "_User")
let forgotten_email = textField.text
query.whereKey("email", equalTo: forgotten_email!)
query.findObjectsInBackgroundWithBlock{
(objects: [PFObject]?, error: NSError?) -> Void in
//NO ERROR//
if(error == nil){
//email in db generate random password
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let randomString : NSMutableString = NSMutableString(capacity: 12)
var users_name = ""
for _ in 0...10{
let length = UInt32 (letters.length)
let rand = arc4random_uniform(length)
randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
}
//set new password for user
if let objects = objects {
for object in objects {
object["_hashed_password"] = randomString
users_name = object["name"] as! String
//send password to email
self.mailgun.sendMessageTo("\(users_name) <\(textField)>", from: "")
self.displayAlert("SUCCESS", msg: "Check your email for your new password")
self.reset_alert.dismissViewControllerAnimated(true, completion: nil)
}
}
else{
self.reset_alert.dismissViewControllerAnimated(true, completion: nil)
self.displayAlert("ERROR", msg: "Email not registered to an account")
}
}
//ERROR//
else{
self.reset_alert.dismissViewControllerAnimated(true, completion: nil)
self.displayAlert("ERROR", msg: "Email not registered to an account") }
} //end if textfield not admin email
self.presentViewController(reset_alert, animated: true, completion: nil)
}
}//end of if textfield is empty
}
Try this extension:
typealias AlertActionBlock = (UIAlertAction) -> Void
extension UIViewController {
func flash(title title:String?, message:String?, cancelTitle:String?, actions:UIAlertAction?...) {
let b = {
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancelBlock:AlertActionBlock = {(action:UIAlertAction) -> Void in }
let cancelAction = UIAlertAction(title: cancelTitle, style: UIAlertActionStyle.Cancel, handler: cancelBlock)
alertController.addAction(cancelAction)
for action in actions {if let action = action {alertController.addAction(action)}}
self.presentViewController(alertController, animated: true, completion: nil)
}
if NSThread.isMainThread() {
b()
} else {
dispatch_async(dispatch_get_main_queue(), b)
}
}
}
That should let you call that function from background threads or the main thread just call that function and fill in the variables
EDIT (I didn't realize you need a textfield) so try this:
func flash(title title:String?, message:String?, textFieldConfigurator:(UITextField -> Void)? = nil, cancelTitle:String?, actions:UIAlertAction?...) {
let b = { () -> Void in
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
if let textFieldConfigurator = textFieldConfigurator {alertController.addTextFieldWithConfigurationHandler(textFieldConfigurator)}
let cancelBlock:AlertActionBlock = {(action:UIAlertAction) -> Void in }
let cancelAction = UIAlertAction(title: cancelTitle, style: UIAlertActionStyle.Cancel, handler: cancelBlock)
alertController.addAction(cancelAction)
for action in actions {if let action = action {alertController.addAction(action)}}
self.presentViewController(alertController, animated: true, completion: nil)
}
if NSThread.isMainThread() {
b()
} else {
dispatch_async(dispatch_get_main_queue(), b)
}
}
If you need multiple text fields make it an array and iterate through it. Let me know how it goes!
class NewsTableViewController: UITableViewController {
#IBOutlet weak var authenticationButton: UIBarButtonItem!
var blogPosts = []
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()
self.navigationItem.title = PFConfig.currentConfig().objectForKey("title") as? String
authenticationButton.enabled = true
if let authEnabled:Bool = PFConfig.currentConfig().objectForKey("adminEnabled") as? Bool {
if authEnabled {
authenticationButton.tintColor = UIView.appearance().tintColor
} else {
authenticationButton.tintColor = UIColor.darkGrayColor()
}
}
loadPosts()
//set the title
PFConfig.getConfigInBackgroundWithBlock { (var config: PFConfig!, var error: NSError!) -> Void in
if error == nil {
if let title:String = config.objectForKey("title") as? String {
self.navigationItem.title = title
}
if let authEnabled:Bool = config.objectForKey("adminEnabled") as? Bool {
if authEnabled {
self.authenticationButton.tintColor = UIView.appearance().tintColor
} else {
self.authenticationButton.tintColor = UIColor.darkGrayColor()
}
}
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return blogPosts.count
}
#IBAction func authenticate(sender: UIBarButtonItem) {
//check if enabled, and if not, get error message from config
if sender.tintColor != UIColor.darkGrayColor() {
//enabled
var authAlert = UIAlertController(title: "Authenticate", message: "Please login to access the admin page.", preferredStyle: UIAlertControllerStyle.Alert)
authAlert.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
textField.placeholder = "Password"
})
authAlert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
authAlert.addAction(UIAlertAction(title: "Go", style: UIAlertActionStyle.Default, handler: { (goAction) -> Void in
let textField:UITextField = authAlert.textFields![0] as UITextField
let text = textField.text
self.authenticateUser(text)
}))
self.presentViewController(authAlert, animated: true, completion: nil)
} else {
//disabled
var serverMessage = PFConfig.currentConfig().objectForKey("adminEnabledMessage") as? String
var errorMessage = UIAlertController(title: "Error", message: "The Admin Console is not enabled right now. Message from server: \(serverMessage!)", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
//authHandlers
func authenticateUser(password: String) {
//get the server password
let serverPass = PFConfig.currentConfig().objectForKey("password") as? String
if password == serverPass {
//move them to the admin console
self.performSegueWithIdentifier("showConsole", sender: nil)
} else {
//error message
var errorMessage = UIAlertController(title: "Error", message: "Incorrect password, please try again.", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
func loadPosts() {
let query = PFQuery(className: "Posts")
query.findObjectsInBackgroundWithBlock { (objects, error: NSError!) -> Void in
if error == nil {
self.blogPosts = objects
self.tableView.reloadData()
} else {
var errorMessage = UIAlertController(title: "Error", message: "There was an error retrieving the posts. Please try again later. \(error.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as PostTableViewCell
// Configure the cell...
let currentPost:PFObject = blogPosts.objectAtIndex(indexPath.row) as PFObject
let title = currentPost.objectForKey("title") as String
cell.titleLabelC.text = title
let gmtFormatter:NSDateFormatter = NSDateFormatter()
gmtFormatter.dateFormat = "E MMM d # hh:mm a"
let dateString = gmtFormatter.stringFromDate(currentPost.createdAt)
cell.detailsLabelC.text = dateString
return cell
}
Here is my code for the UITableView that I added to my storyboard. For some reason, when I call self.tableView.reloadData() in loadPosts(), it won't update the table and simply won't call cellForRowAtIndexPath. Any idea why?
Your TableViewDataSource always returns 0 for the number of sections but you need at least 1. Just remove that DataSource function because it is 1 by default.
This error about multithreading.
UITableView - it is UI element. UI works only in main thread. That means that reloadData also works only in main thred.
In your code query.findObjectsInBackgroundWithBlock is async method. So reloadData will not work in this method.
You should call it in main thread through GCD.
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
I've narrowed down the error to this block of code. I need to define something at optional but I'm not sure where.
#IBAction func SignupWithFacebook(sender: AnyObject) {
var permissionsArray = ["user_about_me", "user_birthday", "email"]
PFFacebookUtils.logInWithPermissions(permissionsArray, block: { (user: PFUser?, error: NSError!) -> Void in
if (user == nil) {
let errormessage = error.userInfo!["error"] as NSString
var facebookLoginError = UIAlertController(title: "Error While Logging", message: "\(errormessage)", preferredStyle: .Alert)
var okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
facebookLoginError.addAction(okButton)
self.presentViewController(facebookLoginError, animated: true, completion: nil)
}
})
}
Any help would be greatly appreciated.
Your code assumes that if user is nil then error will exist. I don't know if you can make that guarantee. Try this instead:
#IBAction func SignupWithFacebook(sender: AnyObject) {
let permissionsArray = ["user_about_me", "user_birthday", "email"]
PFFacebookUtils.logInWithPermissions(permissionsArray) { (user, error) -> Void in
if user == nil {
if let e = error {
let errormessage = e.userInfo!["error"] as NSString
let facebookLoginError = UIAlertController(title: "Error While Logging", message: "\(errormessage)", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
facebookLoginError.addAction(okButton)
self.presentViewController(facebookLoginError, animated: true, completion: nil)
}
}
}
}