Swift UIRefresh Control Drag to refresh in UITableView causes app crash - ios

I have a UIRefreshControll, inside my TableView. All works fine, but if i pull the tableview too fast, the app crashes.
Error: Fatal error: Index out of range
When i call the function refreshData from a Button, the crash doesnt appear. Is kind of weird because refreshControll doesnt do something different than calling a action.
I hope somebody can help me.
private let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(self.refreshData), for: .valueChanged)
if #available(iOS 10.0, *) {
self.tableView.refreshControl = self.refreshControl
} else {
self.tableView.addSubview(self.refreshControl)
}
#objc private func refreshData() {
if isRefreshing {
self.refreshControl.endRefreshing()
return
}
isRefreshing = true
print("refresh")
guard let url = URL(string: taskURL) else {
return
}
let request = URLRequest(url: url)
urlSessionBackground.dataTask(with: request) { (dataOpt, response, error) in
if error != nil {
DispatchQueue.main.async {
let alert = UIAlertController(title: "Loading Error", message: "No Internet Connection. Please try again later.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {(action: UIAlertAction!) in
self.refreshControl.endRefreshing()
}))
self.present(alert, animated: true)
}
return
}else {
self.data.removeAll()
self.userImage.removeAll()
self.currentData.removeAll()
self.taskDetails.removeAll()
}
guard let dataNew = dataOpt else {
return
}
let appdata = try? JSONDecoder().decode(TaskList.self, from: dataNew)
for data in appdata!{
self.data.append(CellData.init(image: UIImage(named: "Profile"), message: data.name, checkImage: data.status))
self.userImage.append("")
self.taskDetails.append(TaskDetails.init(status: data.status, assigned: data.assigneUid, time: data.duedateTsUTC, priority: data.priority, desc: data.desc, uid: data.uid))
}
self.currentData = self.data
DispatchQueue.main.async {
self.tableView.reloadData()
self.searchBar.text = ""
self.isRefreshing = false
self.refreshControl.endRefreshing()
}
}.resume()
}
Fixed it: Used temporary variables to save data and use afterwards. Deleting the data was the problem.

Try this
if error != nil {
DispatchQueue.main.async {
let alert = UIAlertController(title: "Loading Error", message: "No Internet Connection. Please try again later.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {(action: UIAlertAction!) in
self.refreshControl.endRefreshing()
}))
self.present(alert, animated: true)
}
return
}else {
if self.data != nil{
self.data.removeAll()
}
if self.userImage != nil{
self.userImage.removeAll()
}
if self.currentData != nil{
self.currentData.removeAll()
}
if self.taskDetails != nil{
self.taskDetails.removeAll()
}
}

Just try this
var dataTask: URLSessionDataTask?
let defaultSession = URLSession(configuration: .background)
#objc private func refreshData() {
if isRefreshing {
self.refreshControl.endRefreshing()
return
}
isRefreshing = true
print("refresh")
guard let url = URL(string: taskURL) else {
return
}
dataTask?.cancel()
let request = URLRequest(url: url)
dataTask =
defaultSession.dataTask(with: url) { [weak self](dataOpt, response, error) in
defer {
self?.dataTask = nil
}
if let error = error {
DispatchQueue.main.async {
let alert = UIAlertController(title: "Loading Error", message: "No Internet Connection. Please try again later.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {(action: UIAlertAction!) in
self.refreshControl.endRefreshing()
}))
self.present(alert, animated: true)
}
return
} else if
let data = dataOpt,
let response = response as? HTTPURLResponse,
response.statusCode == 200 {
self.data.removeAll()
self.userImage.removeAll()
self.currentData.removeAll()
self.taskDetails.removeAll()
let appdata = try? JSONDecoder().decode(TaskList.self, from: data)
for data in appdata!{
self.data.append(CellData.init(image: UIImage(named: "Profile"), message: data.name, checkImage: data.status))
self.userImage.append("")
self.taskDetails.append(TaskDetails.init(status: data.status, assigned: data.assigneUid, time: data.duedateTsUTC, priority: data.priority, desc: data.desc, uid: data.uid))
}
self.currentData = self.data
DispatchQueue.main.async {
self.tableView.reloadData()
self.searchBar.text = ""
self.isRefreshing = false
self.refreshControl.endRefreshing()
}
}
}
dataTask?.resume()
}

Related

Update information user profile Swift 4

Hello i just implements the method to modify and update the user profile on swift 4 and i have a problem updating the user information (firstname and lastname) in token
//Get token
let token = HPWSLoginManager.shared().saveSuccessResponse.token
// Bearer token to update information
let url = URL(string: "http://51.38.36.76:40/api/v1/updateProfile")
var request = URLRequest(url: url!)
request.httpMethod = "PUT"
request.addValue("Bearer \(token!)", forHTTPHeaderField: "Authorization")
//serialization token
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String: AnyObject]
let sub = json["sub"] as? [String: AnyObject]
DispatchQueue.main.async {
//Get current user
let myUser = PFUser.current()
// check if firstName, lastName are not empty
if(self.firstNameTextfield.text!.isEmpty || self.lastNameTextfield.text!.isEmpty )
{
let myAlert = UIAlertController(title: "Alert", message: "First name and Last name are required fields", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil);
return
}
// set new values user
let userFirstName = self.firstNameTextfield.text
let userLastName = self.lastNameTextfield.text
// update information
myUser?.setObject(userFirstName, forKey: "\(sub?["firstname"])")
myUser?.setObject(userLastName, forKey: "\(sub?["lastname"])")
//display activity indicator
let loadingNotification = MBProgressHUD.showAdded(to: self.view, animated: true)
loadingNotification.labelText = "sauvegarde des informations"
myUser?.saveInBackground(block: { (success:Bool, error:NSError?) -> Void in
// Hide activity indicator
loadingNotification.hide(animated: true)
if(error != nil)
{
let myAlert = UIAlertController(title: "Alert", message: error!.localizedDescription, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil);
return
}
if(success)
{
let userMessage = "votre profil a été mis a jour"
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {(action:UIAlertAction!) -> Void in
self.dismiss(animated: true, completion: {() -> Void in
// self.opener.loadUserDetails()
})
})
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil);
}
} as! PFBooleanResultBlock)
}
} catch {
print("error")
}
}.resume()
I do not know if it's the right way to update the information, and he recovers userFirstName, userLastName when I seize but myUser?.setObject(userFirstName, forKey: "\(sub?["firstname"])") displays nil on the console. I really need your help thank you in advance :)
Try Replacing these lines in the above code -
var myUser = PFUser.current()
myUser?["\(sub?["firstname"])"] = userFirstName
myUser?["\(sub?["lastname"])"] = userLastName

Get value from alert textField

I'm currently trying to get a value from an alert box in swift 3.
The below code is used to prompt the alert and save the data, however, im having trouble with calling back the data and manipulating it so it's just a basic string.
func presentAlert() {
let alertController = UIAlertController(title: "IP?", message: "Please input your unique key:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store it
UserDefaults.standard.set(field.text, forKey: "userIP")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
print("no input given")
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
textField.placeholder = "IP"
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
This method is called here:
override func viewDidAppear(_ animated: Bool) {
presentAlert()
}
I'm trying to call it and assign it in between a string as:
let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
However, this gives me the output:
http://Optional():3000/unLock
When I try to print it.
Any nudge in the correct direction would be greatly appreciated.
Class Added:
class ViewController: UIViewController {
func presentAlert() {
let alertController = UIAlertController(title: "IP?", message: "Please input your unique key:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store your data
//this could be lock unique key name etc in future
UserDefaults.standard.set(field.text, forKey: "userIP")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
print("no input given")
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
textField.placeholder = "IP"
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
//view did appear for the alert
override func viewDidAppear(_ animated: Bool) {
presentAlert()
}
//to post to an /unLock it must be put in the URL
// let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
//let url_to_lock:String = "http://\(textField):3000/Lock"
let url_to_unlock:String = "http://10.73.195.218:3000/unLock"
let url_to_lock:String = "http://10.73.195.218:3000/Lock"
override func viewDidLoad() {
super.viewDidLoad()
}
var Timestamp: String {
return "\(NSDate().timeIntervalSince1970 * 1000)"
}
func un_lock()
{
print(url_to_unlock)
let url:URL = URL(string: url_to_unlock)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "data=unLocking at \(Timestamp)"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
print("error")
return
}
//for errors
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(dataString! )
})
task.resume()
}
func lock()
{
let url:URL = URL(string: url_to_lock)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "data=Locking at \(Timestamp)"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
print("error")
return
}
//for errors
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(dataString! )
})
task.resume()
}
#IBAction func lock(_ sender: UIButton) {
lock()
}
#IBAction func unLock(_ sender: Any) {
un_lock()
}
}
Thank you.
The value for this is optional:
// let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
try:
let url = UserDefaults.standard.value(forKey: "userIP")!
let url_to_unlock:String = "http://\(url):3000/unLock"

Simple explanation needed why delegate does not pass when placed in this section of code

I am trying to pass a users' email address to another ViewController via a delegate when the user has successfully logged in.
The snippet of code in question (marked *) works fine where it is and the data is passed successfully. However, at this point the user has not successfully logged in, therefore I would rather insert the snippet where the /**** is, a little further down.
However it does work when in that position. Why is that? (I am new to Swift)
Thanks
#IBAction func loginButtonTapped(_ sender: AnyObject)
{
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
if (userPassword!.isEmpty || userEmail!.isEmpty) { return; }
// send user data to server side
let myUrl = URL(string: "http://www.quasisquest.uk/KeepScore/userLogin.php");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
let postString = "email=\(userEmail!)&password=\(userPassword!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
DispatchQueue.main.async
{
// spinningActivity!.hide(true)
if(error != nil)
{
//Display an alert message
let myAlert = UIAlertController(title: "Alert", message: error!.localizedDescription, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler:nil)
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
*if self.delegate != nil {
self.delegate?.userLoggedIn(data: userEmail! )
}
// retrieve login details and check to see if all ok
if let parseJSON = json {
let returnValue = parseJSON["status"] as? String
if(returnValue != "error")
{
/**** if self.delegate != nil {
self.delegate?.userLoggedIn(data: userEmail! )
} ****/
UserDefaults.set(UserDefaults.standard)(true, forKey: "isUserLoggedIn");
let mainPage = self.storyboard?.instantiateViewController(withIdentifier: "ViewController")
let mainPageNav = UINavigationController(rootViewController: mainPage!)
let appDelegate = UIApplication.shared.delegate
appDelegate?.window??.rootViewController = mainPageNav
self.dismiss(animated: true, completion: nil)
} else {
// display an alert message
let userMessage = parseJSON["message"] as? String
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler:nil)
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
}
}
} catch
{
print(error)
}
}
}

I am using activity indicator in closure one time it work correct for like but not for next

Kindly help me out for properly start activity indicator when button pressed and stop when process commpleted. My code is given below
startting animation
self.activityIndicator.startAnimating()
self.nextButton.enabled = false
self.activityIndicator.startAnimating()
self.newForMe()
self.finals = self.mids.filter { !self.vmids.contains($0) }
self.RandomNumber = Int(arc4random_uniform(UInt32(self.finals.count)))
let accesstoken = self.defaults.valueForKey("accesstoken")
if self.finals.count != 0
{
//self.activityIndicator.startAnimating()
self.like1Button.enabled = true
self.imgView.hidden = false
self.vview.hidden = true
if self.vc.isConnectedToNetwork() {
let jSon2 = JSON(data: NSData(contentsOfURL: NSURL(string: "https://api.instagram.com/v1/media/\(self.finals[self.RandomNumber])?access_token=\(accesstoken!)")!)!, options: NSJSONReadingOptions.AllowFragments, error: nil)
self.st = jSon2["data"]["images"]["low_resolution"]["url"].stringValue
self.firstCardString = NSURL(string: self.st)!
let con = jSon2["data"]["user_has_liked"].boolValue
if con == true {
self.nextPressed(self)
}
}else {
// print("No Connectivity")
}
// print(con)
let data = NSData(contentsOfURL: self.firstCardString)
if data != nil
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.imgView.image = UIImage(data: data!)
dispatch_async(dispatch_get_main_queue(), {
self.activityIndicator.stopAnimating()
})
})
}
self.nextButton.enabled = true
}
stoping animation
I am astoping it here then also it not starting exectly
else {
self.like1Button.enabled = false
self.imgView.image = UIImage(named: "download (1)")
let alert = UIAlertController(title: "No Photo to like", message: "There is no photo to like!!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
// self.like1Button.hidden = true
self.imgView.image = nil
self.activityIndicator.stopAnimating()
}))
self.presentViewController(alert, animated: true, completion: nil)
}

How to use closures to return a JSON value from a NSURLSession

I am trying to return a result from a JSON object but unable to do so. I am new to Swift so kindly explain me how to do so. In the below code I want to return json_level_number in the return of function fetchNumberOfSections () where i have hard coded as 5 right now return 5. If i declare a variable json_level_number just above the reachability code it sort of solves the problem but then it is returning '0' for the first time. The API returns 2 each time.
Code as below:
func fetchNumberOfSections () -> Int {
if Reachability.isConnectedToNetwork() == true {
// Below code to fetch number of sections
var urlAsString = "http://themostplayed.com/rest/fetch_challenge_sections.php"
urlAsString = urlAsString+"?apiKey="+apiKey
print (urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
do {
let jsonResult = (try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
let json_level_number: String! = jsonResult["level_number"] as! String
//
dispatch_async(dispatch_get_main_queue(), {
// self.dateLabel.text = jsonDate
// self.timeLabel.text = jsonTime
print(json_level_number)
// self.activityIndicatorStop()
})
}
catch let errorJSON {
print (errorJSON)
// alert box code below
let alert = UIAlertController(title: "JSON Error!", message:"Error processing JSON.", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
// Put here any code that you would like to execute when
self.dismissViewControllerAnimated(true, completion: {})
}
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
// alert box code end
}
})
jsonQuery.resume()
// End
}
else {
print("Internet connection FAILED")
self.performSegueWithIdentifier("nointernet", sender: nil)
}
return 5
}

Resources