Swift filter by NSDate object property - ios

I would like to filter my custom objects with a date property.
For example:
class Event
{
let dateFrom: NSDate!
let dateTo: NSDate!
init(dateFrom: NSDate, dateTo: NSDate) {
self.dateFrom = dateFrom
self.dateTo = dateTo
}
}
Now i have a List of maybe 500 Events, and i just want to show the Events for a specific date. I could loop through all Objects, and create a new Array of Objects, but could i also use a filter?
Ill tried something like this:
let specificEvents = eventList.filter( { return $0.dateFrom > date } )
Where date is a NSDate Object for a specific date, but i am not able to use the > operater.
Is there an easy way to get all the events for a specific date, where the date is between the dateFrom and dateTo period?

Thanks to Martin for pointing me into the right direction.
Here is an answer if someone else is looking how to solve this in Swift:
Add an Extension to NSDate:
public func <(a: NSDate, b: NSDate) -> Bool {
return a.compare(b) == NSComparisonResult.OrderedAscending
}
public func ==(a: NSDate, b: NSDate) -> Bool {
return a.compare(b) == NSComparisonResult.OrderedSame
}
extension NSDate: Comparable { }
So you are able to use the < and > operator for NSDate comparison.
var date = dateFormatter.dateFromString(dateString)
var dateTomorrow = date?.dateByAddingTimeInterval(NSTimeInterval(60*60*24)) // add one Day
eventsToday = allEvents.filter( { return $0.dateFrom >= date && $0.dateTo < dateTomorrow} )

For Swift 3.x update :-
extension Date: Comparable{
public static func <(a: Date, b: Date) -> Bool{
return a.compare(b) == ComparisonResult.orderedAscending
}
static public func ==(a: Date, b: Date) -> Bool {
return a.compare(b) == ComparisonResult.orderedSame
}
}

Related

How do I use getters and setters for a variable of type array to access items of the array by index instead of the entire array itself

class SomeClass {
var dates: [Date] {
get {
return ckRecord.object(forKey: "dates") as! [Date]
}
set(newDates) {
ckRecord.setObject(newDates as! CKRecordValue, "dates")
}
}
}
In the previous code, how do I write code in the get and set closures to save to CloudKit and retrieve the data from CloudKit everytime I get one of the values from the array or set one of the values in the array, which means I don't retrieve the whole array or set the whole array, only one of the values at a given index as in the following code:
var obj = SomeClass()
obj.dates[0] = Date()
I don't have a problem using CloudKit. I have a problem figuring out how to arrange the code for the get and set closures so that I properly access the array from the CloudKit record by index. I am attempting to wrap the CloudKit record in class SomeClass.
Any help will be appreciated.
I believe, you can't do that by implementing get/set for your property. But you can do that at least by two ways:
1) Extract logic of getter/setter in function:
func getDate(_ index: Int) -> Date?
func set(date: Date, index: Int)
This will work fine, but it looks ugly.
2) More swifty way is using the subscript. In this case you create class, that holds private dates and this class allows you to access concrete date by using subscript. Simple example:
class Dates {
private var dates: [Date] = []
subscript(index: Int) -> Date? {
get {
guard dates.indices.contains(index) else { return nil }
return dates[index]
}
set(newValue) {
guard let date = newValue else { return }
dates.insert(date, at: index)
}
}
}
My suggestion is an extension of CKRecord with functions to insert, add and get a date by index and index subscription.
To modify the array you always have to get it from the record, change it and put it back.
extension CKRecord {
func date(at index : Int) -> Date? {
guard let dates = self["dates"] as? [Date], index < dates.count else { return nil }
return dates[index]
}
func appendDate(_ date: Date) {
guard var dates = self["dates"] as? [Date] else { return }
dates.append(date)
self["dates"] = dates as CKRecordValue
}
func insertDate(_ date : Date, at index: Int) {
guard var dates = self["dates"] as? [Date], index <= dates.count else { return }
dates.insert(date, at: index)
self["dates"] = dates as CKRecordValue
}
public subscript(index: Int) -> Date? {
get {
guard let dates = self["dates"] as? [Date], index < dates.count else { return nil }
return dates[index]
}
set {
guard let newDate = newValue,
var dates = self["dates"] as? [Date],
dates.indices.contains(index) else { return }
dates[index] = newDate
self["dates"] = dates as CKRecordValue
}
}
}

Making a phone call in an iOS application depending on Time of Day

I'm currently developing my own iOS application that makes a phone call from the press of a button. I'd like to make it so the button calls a different number depending on the time of day. My idea is to differentiate the numbers by UTC, since local time will depend on where the device is. For example, if it is between 00:01 and 08:00, then call 1234567. If it is between 08:01 and 16:00 UTC, then call 7654321.
Here is what I have so far:
import UIKit
var str = "Hello, playground"
typealias WorkingTime = (start: NSDate, end: NSDate)
typealias TelephoneNumber = String
enum Regions {
case Americas
case EMEA
case APAC
func serviceDeskNumberForRegion(region: Regions) -> TelephoneNumber {
switch self {
case .Americas:
return "00112341234"
case .EMEA:
return "12345678976543"
case .APAC:
return "3245678908"
}
}
}
//this is to create the dates from string
let dateformatter = NSDateFormatter()
dateformatter.dateFormat = "HH:mm"
//we're hardcoding....
struct WorkingTimesForRegion {
static let AmericanWorkingTimes = (start: dateformatter.dateFromString("00:01")!, end: dateformatter.dateFromString("08:00")!)
static let EMEAWorkingTimes = (start: dateformatter.dateFromString("08:01")!, end: dateformatter.dateFromString("16:00")!)
static let APACWorkingTimes = (start: dateformatter.dateFromString("16:01")!, end: dateformatter.dateFromString("00:00")!)
static func regionForTime(time: NSDate) -> Regions {
//
//here I should use the nscalendar to make sure all times are represented on the same timezones, like UTC. Not sure how to go about this.
//
//here I probably need to check both end and start
//a switch case would've been more elegant here but I don't see how to build it at the moment
//an option I would explore is using NSRange as they make sense in principle and can be used in switch statements.
//but I don't know if you can create ranges of dates ...
if time.compare(WorkingTimesForRegion.AmericanWorkingTimes.end) == .OrderedAscending {
return .Americas
} else if time.compare(WorkingTimesForRegion.EMEAWorkingTimes.end) == .OrderedAscending {
return .EMEA
} else if time.compare(WorkingTimesForRegion.EMEAWorkingTimes.end) == .OrderedAscending {
return .APAC
}
//just in case
return .EMEA
}
}
func serviceDeskForTime(time: NSDate) -> TelephoneNumber {
let serviceDeskInCharge = WorkingTimesForRegion.regionForTime(time)
return serviceDeskInCharge.serviceDeskNumberForRegion(serviceDeskInCharge)
}
//see if it works
serviceDeskForTime(dateformatter.dateFromString("05:00")!)
func call() -> Bool {
guard let url = NSURL(string: "tel://123456765") else { return false }
if UIApplication.sharedApplication().canOpenURL(url) {
return UIApplication.sharedApplication().openURL(url)
}
return false
}
Any help would be appreciated. Thank you!

Techniques to debug issues with swift class in objective c project?

So I am rewriting an open source project that is currently objective c to swift. I figured the best way to tackle this complicated project would be to rewrite and integrate each class one at a time. So I re-wrote my first class and am able to point all of the objective c classes to the swift class and there are no compile errors.
When I go to run the program, it fails. I believe it's because an object is failing to be created. The swiftclass-swift.h file is created, but I did have to add one method in there manually that wasn't getting created automatically. Are there techniques or articles on what would be the best way to debug what the issue could be? Should I just create breakpoints at every call of the new class or is there a better workflow to identify the issue?
I have created breakpoints where the class is being initialized and I don't see anything obvious. Additionally, I tried creating unit tests against the class but those won't run because xcode tries to compile the program before running the tests. Also note that the class works fine in a playground. Any help or information to read will be appreciated.
Here is the class:
class DateRange: NSObject, NSCopying {
private var dateFormatter = NSDateFormatter()
var start: NSDate
var end: NSDate
var isEmpty: Bool {
get {return self.start.self.isEqualToDate(self.end)}
}
init(start: NSDate?, end: NSDate?) {
self.start = start!
self.end = end!
super.init()
}
class func dateRange<DR: DateRange>(start: NSDate?, end: NSDate?) -> DR {
let dateR = DateRange(start: start, end: end) as! DR
return dateR
}
func components(unitFlags: NSCalendarUnit, forCalendar calendar: NSCalendar) -> NSDateComponents? {
self.checkIfValid()
return calendar.components(unitFlags, fromDate: self.start, toDate: self.end, options: NSCalendarOptions.WrapComponents)
}
func checkIfValid() {
assert(self.start.compare(self.end) != .OrderedDescending)
}
func containsDate(date: NSDate) -> Bool {
self.checkIfValid()
return date.compare(self.start) != .OrderedDescending || self.end.compare(self.start) != .OrderedDescending
}
func intersectDateRange(range: DateRange?) {
self.checkIfValid()
if (range!.end.compare(self.start) != .OrderedDescending || self.end.compare(range!.start) != .OrderedDescending) {
self.end = self.start
return
}
if self.start.compare(range!.start) == .OrderedAscending {
self.start = range!.start
}
if range!.end.compare(self.end) == .OrderedAscending {
self.end = range!.end
}
}
func intersectsDateRange(range: DateRange?) -> Bool {
if range!.end.compare(self.start) != .OrderedDescending || self.end.compare(range!.start) != .OrderedDescending {
return false
}
return true
}
func includesDateRange(range: DateRange?) -> Bool {
if range!.start.compare(self.start) == .OrderedAscending || self.end.compare(range!.end) == .OrderedAscending { return false
}
return true
}
func unionDateRange(range: DateRange?) {
self.checkIfValid()
range!.checkIfValid()
self.start = self.start.earlierDate((range?.start)!)
self.end = self.end.laterDate((range?.end)!)
}
func enumerateDaysWithCalendar(calendar: NSCalendar?, usingBlock: (day: NSDate?, stop: Bool?) ->()) -> Void {
let comp: NSDateComponents = NSDateComponents()
comp.day = 1
var date: NSDate = self.start
let stop: Bool = false
while !stop && date.compare(self.end) == .OrderedAscending {
usingBlock(day: date, stop: stop)
date = (calendar?.dateByAddingComponents(comp, toDate: self.start, options: NSCalendarOptions.WrapComponents))!
comp.day += 1
}
}
func isEqualToDate(range: DateRange?) -> Bool {
return range == range!.start.isEqualToDate(self.start) && range!.end.isEqualToDate(self.end)
}
// MARK: - NSObject
func copyWithZone(zone: NSZone) -> AnyObject {
return DateRange(start: self.start, end: self.end)
}
override func isEqual(object: AnyObject?) -> Bool {
var isObject = Bool()
if self.isEqual(object) {
isObject = true
}
if ((object?.isKindOfClass(DateRange)) != nil) {
isObject = false
}
return isObject
}
}
That header is generated each time you compile swift code. It is not something meant to be edited manually. Any edits you did make would be blown away the next time you compile.
What this means is something about that declaration is not compatible with objective c. Maybe it uses some swift-only feature (like tuples), or maybe it was marked private so it won't export to the header. Cannot say without actually seeing the declaration.
I would start by modifying the declaration to be simpler until it shows up in the header, and making sure that the objective c code can call it when it shows up.

Comparing same dates returns OrderedAscending

Given this function:
func isLessThanDate(dateToCompare: NSDate) -> Bool {
var isLess = false
print(self)
print(dateToCompare)
if self.compare(dateToCompare) == NSComparisonResult.OrderedAscending {
isLess = true
}
return isLess
}
I get OrderedAscending with the same date.
If you want to ignore the millisecond differences, you can use floor on the two dates, but without knowing the exact intent of your code, I'm not sure if that's really what you want. See the comments on this answer for an alternative using NSCalendar functions.
BTW, you can simplify your function if you directly return a boolean value instead of using the isLess variable:
func isLessThanDate(dateToCompare: NSDate) -> Bool {
return self.compare(dateToCompare) == NSComparisonResult.OrderedAscending
}

filter the list for start date and end date in swift

I have to filter a list depending on the start date and end date. The NSDate extension is as follows,
public func <(a: NSDate, b: NSDate) -> Bool {
return a.compare(b) == NSComparisonResult.OrderedAscending
}
public func ==(a: NSDate, b: NSDate) -> Bool {
return a.compare(b) == NSComparisonResult.OrderedSame
}
extension NSDate:Comparable{}
the list is as follows,
class Observation
{
var date:NSDate = NSDate()
var value:String = ""
}
var observationList = [Observation]()
now i am filtering like this,
let filterObservations = observationList.filter( { return $0.date >= startDate && $0.date < endDate} )
but it is not returning anything. Can anyone please point me what i did wrong. my observationList has definitely got an item lying in start date and end date range.
Thanks,
neena

Resources