I've just updated one of my apps to Xcode 8 - Swift 3, and now I get a wired issue while trying to add an event that has a start and end date. Here's the code:
#IBAction func addToCalButt(_ sender: AnyObject) {
let eventStore = EKEventStore()
switch EKEventStore.authorizationStatus(for: EKEntityType.event) {
case .authorized: insertEvent(eventStore)
case .denied: print("Access denied")
case .notDetermined:
eventStore.requestAccess(to: .event, completion: { (granted, error) -> Void in
if granted { self.insertEvent(eventStore)
} else { print("Access denied") }
})
default: print("Case Default")
}
}
func insertEvent(_ store: EKEventStore) {
let calendars = store.calendars(for: EKEntityType.event)
for calendar in calendars {
if calendar.title == "Calendar" {
// Get Start and End dates
let startDate = eventObj[EVENTS_START_DATE] as! Date
let endDate = eventObj[EVENTS_END_DATE] as! Date
// Create Event
let event = EKEvent(eventStore: store)
event.title = "\(eventObj[EVENTS_TITLE]!)"
event.startDate = startDate
event.endDate = endDate
event.calendar = calendar
if startDate < endDate {
print("less")
} else {
print("more")
}
// Save Event in Calendar
do {
try store.save(event, span: .thisEvent)
simpleAlert("This Event has been added to your iOS Calendar")
} catch {
print("ERROR SAVING EVENT TO CAL: \(error)")
}
print("startDate: \(startDate) \endDate: \(endDate)")
}
}
}
and here's the console message:
more
ERROR SAVING EVENT TO CAL: Error Domain=EKErrorDomain Code=4 "The start date must be before the end date." UserInfo={NSLocalizedDescription=The start date must be before the end date.}
startDate: 2106-09-20 12:00:00 PM +0000
endDate: 2016-09-22 12:00:00 AM +0000
As you can see the start date is earlier than the end date, so I really can't understand what's wrong in there :(
Any help will be greatly appreciated.
As #Dustin Spengler says, the startDate has 2106 as year, while the endDate has 2016, so of course Xcode is right :)
Related
I have a property #NSManaged public var sectionKeyDate: Date? I am trying to use for the sectionKey. I'm having an issue getting the resulting string from the NSFetchedResultsSectionInfo to format into a Date, to then be converted back into a formatted string.
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard let sectionInfo = fetchedResultsController.sections?[section] else {
return nil
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-DD"
print("sectionInfo.name: ", sectionInfo.name)
print("Date: ", formatter.date(from: sectionInfo.name))
guard let date = formatter.date(from: sectionInfo.name) else { return "Error" }
let string = formatter.string(from: date)
return string
}
The conversion from a string to date is failing and return nil.
sectionInfo.name: 2021-03-25 05:00:00 +0000
Date: nil
(1) I'm not sure how to format this correctly.
(2) I'm not entirely sure if using a Date for the sectionKeyPath is an ok thing to do.
The issue was in getting the correct dateFormat.
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ssZ"
let date = formatter.date(from: sectionInfo.name)
I have a core data entity (LogEntry) that includes a date attribute. I'm trying to migrate a view to SwiftUI and the view must show all the log entries grouped by Year Month (and sorted chronologically) like so:
JUN 2017
Log Entry 1 (June 10)
Log Entry 2 (June 16)
Log Entry 3 (June 17)
JUL 2017
Log Entry 1 (July 1)
Log Entry 2 (July 3)
etc.
I have been able to achieve the desired grouping with this code:
func group(_ result : FetchedResults<LogEntry>) -> [[LogEntry]] {
return Dictionary(grouping: result){ (element : LogEntry) in
sectionDateFormatter.string(from: element.date as Date)
}.values.map{$0}
}
var body: some View {
NavigationView {
List {
ForEach(group(logEntries), id: \.self) { (section: [LogEntry]) in //Works but not ordered
Section(header: Text( self.sectionDateFormatter.string(from: section[0].date as Date))) {
ForEach(section, id: \.self) { logEntry in
HStack {
Text("\(self.logDateFormatter.string(from: logEntry.date as Date))")
Spacer()
Text("\(logEntry.total) m").font(.subheadline)
}
}
}
}.id(logEntries.count)
}
.listStyle(PlainListStyle())
}
}
I have been trying to add the sorting for a while and I can't seem to find the right way to do it in the context of SwiftUI.
I tried this:
func update(_ result : FetchedResults<LogEntry>)-> [[LogEntry]]{
return Dictionary(grouping: result){ (element : LogEntry) in
sectionDateFormatter.string(from: element.date as Date)
}.values.sorted() { $0[0].date! < $1[0].date! }
}
... but I get a "Type of expression is ambiguous without more context" error and it fails to compile.
I also tried this:
func groupedByYearMonth(_ result: FetchedResults<LogEntry>) -> [Date: [LogEntry]] {
let empty: [Date: [LogEntry]] = [:]
return logEntries.reduce(into: empty) { acc, cur in
let components = Calendar.current.dateComponents([.year, .month], from: cur.date as Date)
let date = Calendar.current.date(from: components)!
let existing = acc[date] ?? []
acc[date] = existing + [cur]
}
}
... but in this case, I couldn't figure out how to tweak the SwiftUI list ForEach to work with it.
Any pointers would be greatly appreciated!
You can sort the the fetched results if you use a different key in the temporary dictionary that can be properly sorted. So for the DateFormatter used in the function I set the format to "yyyy-MM" instead.
Note that I start by sorting the input to the function but this step is not needed if the fetched result is already sorted on date which I recommend.
func group(_ result : FetchedResults<LogEntry>) -> [[LogEntry]] {
let sorted = result.sorted { $0.date < $1.date }
return Dictionary(grouping: sorted) { (element : LogEntry) in
dictionaryDateFormatter.string(from: element.date as Date)
}.sorted { $0.key < $1.key }.map(\.value)
}
I've code to show clock time with the timeLabel:UILabel in my app whit Swift3.
And that is:
override func viewWillAppear(_ animated: Bool) {
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.currentTime), userInfo: nil, repeats: true)
}
func currentTime(){
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
let second = calendar.component(.second, from: date)
if second % 2 == 0{
timeLabel.text = "\(hour):\(minutes)"
}else {
timeLabel.text = "\(hour) \(minutes)"
}
But when two points disappear between hour and minutes numbers, they are a little bit movement. How to fix that?
With a trick 😏 can be fix that.
By adding another label with same background color in else condition, Yup😉
For Swift 3:
var attributes = [String: AnyObject]()
attributes[NSForegroundColorAttributeName] = self.view.backgroundColor
let attributedString = NSAttributedString(string: ":", attributes: attributes)
label.attributedText = attributedString
I have a few variables that I create within code for a view controller (basically, I'm just checking the values of segmented controls), but when I try to write into the string within the loops (shown below), nothing is written, and all strings remain "nil."
var textTypeVideo:String?
var textMultipleAgents:String?
var textAdditionalRooms:String?
var textMultipleVideos:String?
var textTurnaround:String?
Here are the functions that check to see where the segmented controls are:
#IBAction func typeOfVideoAction(sender: AnyObject) {
if typeOfVideoControl.selectedSegmentIndex == 0 {
textTypeVideo = "Standard Home Video"
} else {
textTypeVideo = "Standard CV Video"
}
}
#IBAction func mulitpleAgentScenesAction(sender: AnyObject) {
if multipleAgentScenesControl.selectedSegmentIndex == 0 {
textMultipleAgents = "Multiple Agent Scenes Desired"
} else {
textMultipleAgents = "No Multiple Agent Scenes"
}
}
#IBAction func additionalRoomsAction(sender: AnyObject) {
if additionalRoomsFeaturesControl.selectedSegmentIndex == 0 {
textAdditionalRooms = "Additional Rooms & Features Desired"
} else {
textAdditionalRooms = "No Additional Rooms & Features"
}
}
#IBAction func multipleVideosAction(sender: AnyObject) {
if multipleVideoCopiesControl.selectedSegmentIndex == 0 {
textMultipleVideos = "Second Video Copy Requested"
} else {
textMultipleVideos = "No Second Video Copy"
}
}
#IBAction func turnaroundAction(sender: AnyObject) {
if turnaroundControl.selectedSegmentIndex == 0 {
textTurnaround = "No Quick Turnaround"
} else if turnaroundControl.selectedSegmentIndex == 1 {
textTurnaround = "24 Hour Turnaround Desired"
} else if turnaroundControl.selectedSegmentIndex == 2 {
textTurnaround = "2 Business Day Turnaround Desired"
} else if turnaroundControl.selectedSegmentIndex == 3 {
textTurnaround = "3 Business Day Turnaround Desired"
} else {
textTurnaround = "5 Business Day Turnaround Desired"
}
}
Finally, this is what I want to fill in and print in an email:
let messageBody = "\(textTypeVideo) and \(textMultipleAgents) and \(textAdditionalRooms) and \(textMultipleVideos) and \(textTurnaround)"
mailComposerVC.setMessageBody(messageBody, isHTML: false)
Any help is greatly appreciated! I just can't seem to fill in the top variables with what they're supposed to be, based on the segmented controls! Thanks.
Order form has two fields, Ship Date (datefield) and Lead Time (number field in days).
When a ship date is entered, it should be greater than the current date + the lead time. alert user if not, and do not allow save of record.
There is one exception to the rule though: If the approved for rush field and approved by field is filled out the alert is not necessary and record can be saved.
++ It would be a plus if this could actually not account for weekend days. But not mandatory
function checkLeadTime
{
var shipDate = Xrm.Page.getAttribute("requestdeliveryby").getValue();
var leadTime = Xrm.Page.getAttribute("orbus_leadtime").getValue();
var leadTimeDate = Xrm.Page.getAttribute("orbus_leadtimedate").getValue():
if(leadTime != NULL)
{
var approvedRushProduction = xrm.getAttribute("orbus_projectapprovedrush").getValue();
var approvedBy = Xrm.Page.getAttribute("orbus_approvedbyid").getValue();
var currentTime = new Date();
var newDate = currentTime.setDays(currentTime.getDays + leadTime);
leadTimeDate.setValue(newDate);
if(approvedRushProduction == 0 && approvedBy == NULL)
{
if ( newDate < shipDate)
{
alert("Sorry, Ship Date is less than lead time!");
}
else
{
alert("Current Time = " + newDate);
}
}
}
}
You can attach your function to the form onSave event.
if ( newDate < shipDate ) {
alert("Sorry, Ship Date is less than lead time!");
Xrm.Page.context.getEventArgs().preventDefault();
} else {
alert("Current Time = " + newDate);
}
Use Xrm.Page.context.getEventArgs().preventDefault() to stop the save event.
To stop saving and show error message you can use:
Xrm.Page.getControl(fieldName).setNotification(message);
http://garethtuckercrm.com/2013/10/17/crm-2013-new-features-javascript-notifications/