I am trying to check if event already exists in calendar and show alert that it exists. This is my code below
Edited: It keeps adding Duplicate Events.
let event = EKEvent(eventStore: eventStore)
var savedEventId : String = ""
event.title = title
event.startDate = (self.dataEvent?.dates?.begin)!
event.endDate = (self.dataEvent?.dates?.end)!
event.calendar = eventStore.defaultCalendarForNewEvents
let predicate = eventStore.predicateForEventsWithStartDate(startDate, endDate: endDate, calendars: nil)
let existingEvents = eventStore.eventsMatchingPredicate(predicate)
for singleEvent in existingEvents {
if singleEvent.title == self.dataEvent?.titleString && singleEvent.startDate == startDate && singleEvent.endDate == endDate {
let alert = UIAlertController(title: "Event Already Exists", message: "Event Already Exists in Calendar", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
do {
try eventStore.saveEvent(event, span: .ThisEvent)
savedEventId = event.eventIdentifier
print("Event Added")
let alert = UIAlertController(title: "Event Successfully Added", message: "Event Added to Calendar", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} catch {
print("Error occurred")
}
The problem is that your do-catch block in which you add the event gets called all the time even if the event exists. You need to create a boolean to track whether the same event was found or not and only execute the do-catch block if it wasn't found.
Instead of using a for loop, you can use Array.contains(where:), which allows for an early exit if such an event was found.
var eventAlreadyExists = false
let event = EKEvent(eventStore: eventStore)
var savedEventId : String = ""
event.title = title
event.startDate = (self.dataEvent?.dates?.begin)!
event.endDate = (self.dataEvent?.dates?.end)!
event.calendar = eventStore.defaultCalendarForNewEvents
let predicate = eventStore.predicateForEventsWithStartDate(startDate, endDate: endDate, calendars: nil)
let existingEvents = eventStore.eventsMatchingPredicate(predicate)
let eventAlreadyExists = existingEvents.contains(where: {event in self.dataEvent?.titleString == event.title && event.startDate == startDate && event.endDate = endDate})
// Matching event found, don't add it again, just display alert
if eventAlreadyExists {
let alert = UIAlertController(title: "Event Already Exists", message: "Event Already Exists in Calendar", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} else {
// Event doesn't exist yet, add it to calendar
do {
try eventStore.saveEvent(event, span: .ThisEvent)
savedEventId = event.eventIdentifier
print("Event Added")
let alert = UIAlertController(title: "Event Successfully Added", message: "Event Added to Calendar", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} catch {
print("Error occurred")
}
}
Note: If your endDate and startDate are equal - then use in predicate range of time, eg endDate + 1, startDate +1 in eventStore.predicateForEventsWithStartDate. Overwise your existingEvents will always empty
Related
I need to remove an event with certain/specific title, I hope that I can delete/remove the event based on the eventID/Identifier. but I don't know how to do that in code. I don't know how to give identifier to the event and remove it based on their identifier/title.
here is the code I use to save the event:
let eventStore = EKEventStore()
let newEvent = EKEvent(eventStore: eventStore)
newEvent.calendar = eventStore.defaultCalendarForNewEvents
newEvent.title = self.eventNameTextField.text ?? "Some Event Name"
newEvent.startDate = timeDatePicker.date
newEvent.endDate = endTimeDatePicker.date
newEvent.notes = "Ini adalah catatan"
newEvent.location = "Jalan Sunda kelapa no.60"
let eventAlarm = EKAlarm(relativeOffset: -60 * 10) // 10 minutes before the start date
newEvent.alarms = [eventAlarm]
do {
try eventStore.save(newEvent, span: .thisEvent)
print("Event has been saved")
} catch {
let alert = UIAlertController(title: "Event could not be saved", message: (error as NSError).localizedDescription, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(OKAction)
self.present(alert, animated: true, completion: nil)
}
I know that I can use evenStore.remove() , but that method needs EKEvent instance. I don't understand how to remove a specific event if using that method, it will be easier if I can remove the event based on their identifier
Actually an EKEvent instance has a get-only attribute called eventIdentifier. You can't modify this identifier, but you can get it after you save the event. So:
do {
try eventStore.save(newEvent, span: .thisEvent)
let id = newEvent.eventIdentifier ?? "NO ID"
//Save your ID in your database or anywhere else so you can retrieve the event later
print("Event has been saved with id \(id)")
} catch {
let alert = UIAlertController(title: "Event could not be saved", message: (error as NSError).localizedDescription, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(OKAction)
self.present(alert, animated: true, completion: nil)
}
Then you can get the event using its identifier
let event = eventStore.event(withIdentifier: id)
and then pass this EKEvent to eventStore.remove()
I am trying to recovery a value from firebase database and compare it with a UITextField value, in case of matching, I save it to a var that I will us. The problem is that the variable in question has a default value just when I use it.
Above I show my func code where the variable affected is "codeRecovered":
#IBAction func signUpAction(_ sender: AnyObject)
{
var codeRecovered: String = ""
if emailSignUpTextField.text == "" || self.secretCodeTextField.text == "" {
let alertController = UIAlertController(title: "Error", message: "Please enter your email, pin code and password", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
} else {
self.dbHandler = self.ref?.child("Companies").observe(.value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let value = snap.value as! [String:String]
if let auxSecretCode = value["secretCode"]
{
if auxSecretCode == self.secretCodeTextField.text{
print("Value recovered OK(works fine): \(auxSecretCode)")
codeRecovered = auxSecretCode
print("Recovered value saved OK(works fine): \(codeRecovered)")
}
}
}
})
//Here codeRecovered is already ""
print("\(codeRecovered) is the recovered value(empty) and \(self.secretCodeTextField.text ?? "def") is the textField value")
if codeRecovered != self.secretCodeTextField.text{
let alertController = UIAlertController(title: "Error", message: "Please enter a correct pin code", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
//....
Async calls with sync result use....
#IBAction func signUpAction(_ sender: AnyObject)
{
var codeRecovered: String = ""
if emailSignUpTextField.text == "" || self.secretCodeTextField.text == "" {
let alertController = UIAlertController(title: "Error", message: "Please enter your email, pin code and password", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
} else {
self.dbHandler = self.ref?.child("Companies").observe(.value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let value = snap.value as! [String:String]
if let auxSecretCode = value["secretCode"]
{
if auxSecretCode == self.secretCodeTextField.text{
print("Value recovered OK(works fine): \(auxSecretCode)")
codeRecovered = auxSecretCode
print("Recovered value saved OK(works fine): \(codeRecovered)")
}
}
}
//Here codeRecovered is already ""
print("\(codeRecovered) is the recovered value(empty) and \(self.secretCodeTextField.text ?? "def") is the textField value")
if codeRecovered != self.secretCodeTextField.text{
let alertController = UIAlertController(title: "Error", message: "Please enter a correct pin code", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
})
to use your codeRecovered in a sequence it must be within self.dbHandler = self.ref?.child("Companies").... block because it runs in async thread
I added a line of code so that name text-field is mandatory when registering an account with firebase but when I did that the UIAlert broke. It stopped showing up when I added that line of code. The code I added is highlighted with a >. What is the best way to fix the problem? Either recode the mandatory name text-field or recode the UIAlert. Which is the simplest way?
#IBAction func registerTapped(_ sender: Any) {
let namec = nameTextField.text
if let email = emailTextField.text, let pass = passwordTextField.text, let name = (namec?.capitalized.isEmpty)! ? nil:namec?.capitalized {
FIRAuth.auth()?.createUser(withEmail: email, password: pass, completion: { (user, error) in
if user != nil {
//user found
let interval = NSDate().timeIntervalSince1970
let date = Date(timeIntervalSince1970: interval)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy/HH/mm/SS"
// you can change the date format to whatever you wants
let dateString = dateFormatter.string(from: date)
print(dateString)
self.refD?.child("Users").child((user?.uid)!).setValue(["Email": email, "Name": name, "User Created": dateString])
print("User Created And Added To Database", email, name, dateString)
self.performSegue(withIdentifier: "registertologin", sender: self)
}
else {
print(error!)
let alert = UIAlertController(title: "Error Creating Account ", message: "\(error!.localizedDescription)", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
})
}
}
}
i think you have to add alert code in the main queue block because your code is inside a completion handler block
DispatchQueue.main.async {
print(error!)
let alert = UIAlertController(title: "Error Creating Account ", message: "\(error!.localizedDescription)", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
Try this!
I have some code in my project:
#IBAction func createAccountAction(sender: AnyObject) {
if self.emailField.text == "" || self.passwordField.text == ""
{
let alertController = UIAlertController(title: "Oops!", message: "Please enter an email and password.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
else
{
FIRAuth.auth()?.createUserWithEmail(self.emailField.text!, password: self.passwordField.text!) { (user, error) in
if error == nil
{
let alertController = UIAlertController(title: "Done", message: "Account created!", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.emailField.text = ""
self.passwordField.text = ""
}
else
{
let alertController = UIAlertController(title: "Oops!", message: error?.localizedDescription, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
As you can see in the last else statement I have a alertController with the message Oops that will show when clicking the createAccountAction button.
But that happens when the user presses the button and has not filled in anything in the textfields.
Now what I want is that when a user succesfully fills in the textfields, that a same popup should appear along with other text that I specified.
When I run the code it does the part
self.emailField.text = ""
self.passwordField.text = ""
but does not present the AlertController.
How can I achieve what I want?
You're missing the line:
self.presentViewController(alertController, animated: true, completion: nil)
which should be after:
self.emailField.text = ""
self.passwordField.text = ""
So you aren't presenting the alert.
Also. You can change that entire function to this much simpler one:
#IBAction func createAccountAction(sender: AnyObject) {
if self.emailField.text == "" || self.passwordField.text == "" {
let title = "Oops"
let message = "Please enter an email and password."
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
} else {
FIRAuth.auth()?.createUserWithEmail(self.emailField.text!, password: self.passwordField.text!) { (user, error) in
var title : String
var message : String
if error == nil {
title = "Done"
message = "Account created!"
self.emailField.text = ""
self.passwordField.text = ""
} else {
title = "Oops!"
message = "error?.localizedDescription"
}
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
}
You are only presenting the alert controller in the 'else' part of your 'if else' statement.
Additionally refactored example to ensure bugs like that don't happen:
var title: String!
var message: String!
if let error = error {
title = "Oops!"
message = error.localizedDescription
} else {
title = "Done"
message = "Account created!"
self.emailField.text = ""
self.passwordField.text = ""
}
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
I wonder if its possible to show error messages in a UIAlertController.
My server sends error messages back as JSON.
I can get each error message using:
if let errorVal = errorVal {
if let items = errorVal["errors"].array {
for item in items {
print(item)
}
}
}
Now I wonder how I can show the errors in a AlertController.
The AlertController's message parameter expect a string but my errors come as JSON then cast to .array
let alertController = UIAlertController(title: "Hey! :)", message: "My Errors", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
Well, you could build up a string with description of each error ( or just message ) and show that ( may be too much to show ). It would go like this:
var errorMessages = ""
if let errorVal = errorVal {
if let items = errorVal["errors"].array {
for item in items {
print(item)
errorMessages = errorMessages + item + "\n" // if this is NSError you can use description, message or code
}
}
}
and later on you can do something like:
let alertController = UIAlertController(title: "Hey! :)", message: errorMessages , preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)