How to call an NSTimer until selector is successful [duplicate] - ios

This question already has answers here:
Stop NSTimer and dismiss view controller (swift)
(5 answers)
Closed 6 years ago.
I have a function called "Check" that checks if an object has been updated, if that's the case, the user is sent to another view controller. However, the NSTimer keeps repeating itself, I want it to stop after the user is sent to the other view controller.
func check(){
let current = PFUser.currentUser()?.objectForKey("username")
let check = PFQuery(className: "Requests")
check.whereKey("username", equalTo: current!)
check.whereKey("requestResponded", equalTo: "True")
check.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error != nil || objects == nil{
print("Request rejected.")
} else {
for object in objects!{
let service = object["service"] as! NSValue
print(service)
if service == 1{
self.performSegueWithIdentifier("detailedRequest", sender: self)
print("detailedRequest")
} else {
self.performSegueWithIdentifier("normalRequest", sender: self)
print("normal")
}
}
print("Successfully retrieved that object.")
}
})
}
self.timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(self.check), userInfo: nil, repeats: true)

Declare a viewDidDisappear function and inside it invalidate your timer. This function automatically be called each time your viewController has disappeared.
Something like this:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
self.timer.invalidate()
}

Related

Looking for best practices when updating a tableview by monitoring for changes in DB

I'm somewhat new to this and this is my first question on stackoverflow. Thanks in advance for your help and bear with me if my formatting sucks
I've got multiple views within my app (all displaying data using tableview subviews) that need to update automatically when the data changes on the database (Firestore), i.e. another user updates the data.
I've found a way to do this which is working well, but I want to ask the community if there's a better way.
Currently, I am creating a Timer object with a timeInterval of 2. On the interval, the timer queries the database and checks a stored data sample against updated data. If the two values vary, I run viewDidLoad which contains my original query, tableView.reloadData(), etc..
Any suggestions or affirmations would be very useful.
var timer = Timer()
var oldChallengesArray = [String]()
var newChallengesArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//set tableview delegate
mainTableView.delegate = self
mainTableView.dataSource = self
//set challengesmodel delegate
challengesModel.delegate = self
//get challenges
DispatchQueue.main.async {
self.challengesModel.getChallenges(accepted: true, challengeDenied: false, incomingChallenges: false, matchOver: false)
self.mainTableView.reloadData()
}
scheduledTimerWithTimeInterval()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.updateTableView), userInfo: nil, repeats: true)
}
#objc func updateTableView(){
ChallengeService.getAllUserChallengeIDs(accepted: true, challengeDenied: false, matchOver: false) { (array) in
if array.isEmpty {
return
} else {
self.newChallengesArray = array
if self.oldChallengesArray != self.newChallengesArray {
self.oldChallengesArray = self.newChallengesArray
self.newChallengesArray.removeAll()
self.viewDidLoad()
}
}
}
}
Firestore is a "realtime database", that means that the database warns you when changes happen to the data. To achieve that the app needs to subscribe to relevant changes in the db. The sample code below can be found here:
db.collection("cities").document("SF")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
print("Current data: \(data)")
}
Also, I would like to point out that calling viewDidLoad is incorrect, you should never call viewDidLoad yourself, create an func to update the data. Something like this:
DispatchQueue.main.async {
self.mainTableView.reloadData()
}

Can we customize SwiftEventBus to notify only for the current active screen and not all the places where it is registered?

Can we customize SwiftEventBus Library to only trigger in the current active ViewController.
I'm trying to trigger an action when ever a notification occurs, so i'm using swift event bus to trigger when ever a push notification comes but it is triggering in all the places it is registered. Can we make so that it will only trigger the action in the active view. If not is there any other library I can use?
Wouldn't it be enough to deregister inactive ViewControllers as mentioned in the SwiftEventBus readme?
//Perhaps on viewDidDisappear depending on your needs
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
SwiftEventBus.unregister(self)
}
Modify library(or subclass SwiftEventBus) like below:
#discardableResult
open class func on(_ target: AnyObject, name: String, sender: Any? = nil, queue: OperationQueue?, handler: #escaping ((Notification?) -> Void)) -> NSObjectProtocol {
let id = UInt(bitPattern: ObjectIdentifier(target))
//modification start
let handlerIner:((Notification?) -> Void) = { [weak target] n in
if let vc = target as? UIViewController, vc.view?.window != nil {
handler(n)
}
}
let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: name), object: sender, queue: queue, using: handlerIner)
// modification end
let namedObserver = NamedObserver(observer: observer, name: name)
Static.queue.sync {
if let namedObservers = Static.instance.cache[id] {
Static.instance.cache[id] = namedObservers + [namedObserver]
} else {
Static.instance.cache[id] = [namedObserver]
}
}
return observer
}

Using TabViewController, When saving data, TableView not updating

My current setup: TabViewController that is connected to two TableViewControllers embedded in navigation controllers. I am able to update the tableview no problem, on app load and when switching back between the different tabs.
My problem comes when I open another tableviewcontroller to edit. When I hit save from this view controller, it updates the data and saves everything just fine however, My tableView will not update no matter what I try unless I switch between the different tabs or close and reopen the app.
I have tried using delegates to trigger the update, I have tried use NSNotificationCenter, and no dice.
Has anybody else had this issue?
Here is my save function:
func saveDose(completion: (_ finished: Bool) -> ()) {
if searchName == "" {
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let dose = Dose(context: managedContext)
let doseNumberString = numberDosesText.text
let doseNumberInt = Int64(doseNumberString!) ?? 0
dose.name = nameText.text
dose.script = scriptText.text
dose.dosage = dosageText.text
// dose.doseInterval = doseInterval
dose.firstDose = datePicker.date
dose.numberDoses = doseNumberInt
dose.doseReminder = remindersSwitch.isOn
do {
try managedContext.save()
print("Data Saved")
completion(true)
} catch {
print("Failed to save data: ", error.localizedDescription)
completion(false)
}
} else {
update(name:searchName, firstDose: searchDate)
completion(true)
}
}
And here is where I call it and load back to my other tableview.
#IBAction func saveBtnPressed(_ sender: UIBarButtonItem) {
saveDose { (done) in
if done {
print("We need to return now")
navigationController?.popViewController(animated: true)
self.dismiss(animated: true, completion: nil)
} else {
print("Try again")
}
}
}
And here is where I reload my tableview when the view appears
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("View has been loaded back from other view")
self.fetchData()
self.tableView.reloadData()
print("View will appear")
}
And here is my fetchData and loadData functions
func fetchData() {
loadData { (done) in
if done {
setEmptyView(Array: logArray.count)
}
}
}
func loadData(completion: (_ complete: Bool) -> ()) {
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Log")
do {
logArray = try managedContext.fetch(request) as! [Log]
print("Data Fetched No Issues")
completion(true)
} catch {
print("Unable to fetch data: ", error.localizedDescription)
completion(false)
}
}
Again I have tried delegates and have used this code in other applications that have worked fine. Is it something to do with the tab bar controller?
The data is obviously saving fine, I just can't get the tableView to update properly.
It seems like it is not calling the viewWillAppear function after the save. I have tried using delegates as well to force the update of the tableview but nothing has been working.
Basically you need to call tableView.reloadData(). after you have done saving.
Moreover you can use beginUpdates, endUpdates on tableView to perform animations on specific rows. for more information on this you may refer to This Link
Reload your tableview in custom delegates method your problem will be solved.

how to dismiss the alert programatically using swift?

Hi I am new for developing ios application.I have used some cocopods framework for doing the alert view. I have used alert view following mentioned sweetAlert. In that I have tried to dismiss the alert programmatically with out press the tab button in alert.Please any one help for fix the problem.
You can dismiss the alert by calling dismissViewControllerAnimated method on alertController object.
alertControllerObject?.dismissViewControllerAnimated(true, completion: nil)
I think you can use pressed(sender: UIButton!) method in SweetAlert class.
#IBAction func aBasicMessageAlert(sender: AnyObject) {
let sweetAlert = SweetAlert().showAlert("Here's a message!")
close(sweetAlert, after: 2.0)
}
func close(alert: SweetAlert, after seconds: Double) {
NSTimer.scheduledTimerWithTimeInterval(seconds,
target: self,
selector: #selector(closeAlert),
userInfo: ["alert": alert],
repeats: true)
}
func closeAlert(timer: NSTimer) {
let alert = timer.userInfo!["alert"] as! SweetAlert
let dummyCloseButton = UIButton()
dummyCloseButton.tag = 0
alert.pressed(dummyCloseButton)
}
Use this,
yourAlerView.dismiss(withClickedButtonIndex: 0, animated: true)
when you handle the return key.
ButtonIndex is index of button you want to click by default to hide alert.
Hope this will help you.
Try This
When you call Alert Method Also Called this, Inside the alert Method
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "hideAlert:", userInfo: userInfo, repeats: true) //repeats: false
Called This outside the Alert Method
func hideAlert(){
isOtherButton == true// isOtherButton getting from your SweetAlert Demo
}
You need to add this method in SweetAlert, and call it.
func closeAlert(){
UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
self.view.alpha = 0.0
}) { (Bool) -> Void in
self.view.removeFromSuperview()
self.cleanUpAlert()
//Releasing strong refrence of itself.
self.strongSelf = nil
}
}
Implement alert like this
let alert = SweetAlert() // take this as global
func showAlert(){
alert.showAlert(title as String, subTitle: msg as String, style: style, buttonTitle:buttonOtherTitle as String, buttonColor:UIColor.redColor() , otherButtonTitle: buttonOkTitle as String, otherButtonColor: colors.KBlueTextColor!) { (isOtherButton) -> Void in
if isOtherButton
{
completionHandler(false)
}
else
{
completionHandler(true)
}
}
}
func CloseAlert(){
alert.closeAlert()
}

Swift/Parse - No Results Matched the Query when Updating Data

I have recently been playing around with parse,and yes, I know that it is closing soon:(. I have this error however that keeps on coming up. I am trying to update my textview.text to parse every single second. However, when the code runs a message comes up saying "no results matched the query", even though textview.text is not empty. I would really appreciate your help. Thanks.
override func viewDidLoad() {
super.viewDidLoad()
notes["Content"] = detailDescriptionLabel.text
notes.saveInBackgroundWithBlock { (succes, error) -> Void in
if error != nil {
print("unable to save objects")
}
}
scheduledTimerWithTimeInterval()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function **Countdown** with the interval of 1 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateText"), userInfo: nil, repeats: true)
}
func updateText () {
let query = PFQuery(className: "Notes")
query.getObjectInBackgroundWithId("Content") { (notes, error) -> Void in
notes?["Content"] = self.detailDescriptionLabel.text
print("Updated")
}
}
Picture of error in Log
Image of Parse Dashboard

Resources