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
}
Related
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!
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.
thanks for reading my post.
I have an array of tuples declared as such:
var myArray: [(item1: String?, item2: NSDate?)] = []
At the end of my loop I want to sort my array of tuples based on every tuple's item2, whose type is NSDate?.
Based on this answer and this answer I tried the following, but received this compiler error, "cannot invoke 'sort' with an argument list of type '((_,_) -> _)'.
Here is what I tried:
myArray.sort {$0.1.item2?.compare($1.1.item2?) == NSComparisonResult.OrderedDescending }
P.S. println() works fine and prints item1 and item2 as an optional.
You must implement Comparable protocol to NSDate
public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}
public func <(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == .OrderedAscending
}
extension NSDate: Comparable { }
After that you can sort your tuples by date:
myArray!.sort {$0.1 == $1.1 ? $0.1 > $1.1 : $0.1 > $1.1 }
An alternative to the accepted solution:
let res = myArray.sort { (left, right) -> Bool in
return left.item2?.timeIntervalSinceReferenceDate < right.item2?.timeIntervalSinceReferenceDate
}
The reason why the linked solutions did not work is because the array of tuples defined in the question contains optional types.
Checking for the optionals fixes the problem, without having to add new operators to NSDate.
An example, with 3 dates, and optional types:
var myArray: [(item1: String?, item2: NSDate?)] = []
myArray = [("now", NSDate()), ("now+30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(30))), ("now-30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(-30)))]
myArray.sortInPlace { (lhs, rhs) -> Bool in
if lhs.item2 != nil && rhs.item2 != nil {
return lhs.item2!.compare(rhs.item2!) == .OrderedAscending
}
return false // Return true if you want nil values first
}
Same code, if the types didn't allow for optionals:
var myArray: [(item1: String, item2: NSDate)] = []
myArray = [("now", NSDate()), ("now+30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(30))), ("now-30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(-30)))]
myArray.sortInPlace { (lhs, rhs) -> Bool in
return lhs.item2.compare(rhs.item2) == .OrderedAscending
}
MirekE's solution also works well, but you do not have control on where the nil values would end (they will be at the beginning).
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
}
}
My app contains many Core Data NSManagedObject subclasses, all of which have an NSDate which I call their recordID. When comparing two objects, I want to use this data to determine if they are the same. Now since there are many subclasses, I created a protocol to show that they all implement a recordID:
protocol HasID
{
var recordID: NSDate {get}
}
Simple, right? Now I have implemented the == operator as follows:
func == <T: HasID>(left: T, right: T) -> Bool
{
return left.recordID == right.recordID ? true : false
}
Problem- Swift doesn't use my beautiful == operator and instead compares with some generic crap as follows
func ==(lhs: NSObject, rhs: NSObject) -> Bool
Now if I implement == for each individual subclass as follows
func == (left: Pilot, right: Pilot) -> Bool
{
return left.recordID == right.recordID ? true : false
}
Then it uses my operator and works. (I've also got a == operator implemented for NSDate which is why the above code is fine.)
Any idea how I can get my generic == operator to be used rather than the NSObject one?
Putting together previous comments and proposal, it seems it is due to NSManagedObject.
Put the following code in a playground:
import UIKit
import CoreData
protocol HasID
{
var recordID: NSDate {get}
}
func == <T: HasID>(left: T, right: T) -> Bool
{
println("==")
return left.recordID.isEqualToDate(right.recordID)
}
func ~= <T: NSManagedObject where T: HasID>(left: T, right: T) -> Bool
{
println("~=")
return left.recordID.isEqualToDate(right.recordID)
}
class Managed: NSManagedObject, HasID {
let recordID: NSDate = {
let components = NSDateComponents()
components.day = 31
components.month = 3
components.year = 2015
let gregorian = NSCalendar(calendarIdentifier: NSGregorianCalendar)!
return gregorian.dateFromComponents(components)!
}()
}
let foo = Managed()
let bar = Managed()
foo == bar
foo ~= bar
switch foo {
case bar:
println("Equal")
default:
println("Not Equal")
}
As you will see the overloaded == is never called, but ~= is.
Thus I suggest not going too far forcing NSManagedObject to use an overloaded == and use ~= instead. As you can see, you will get it to work in switch statements as well (in fact it is the pattern match operator).
If you play removing NSManagedObject from my example, you will see that for an ordinary class, your solution would work.
My opinion is that == operator for NSManagedObject is used to compare between managed object ids, and not pointers in memory. The same managed object could have different pointers in memory when it is present in different managed object context. That's why I wouldn't try to overload it. It helps keep track of the same managed object in different context.
You can declare an infix operator:
// I've modified your protocol just for my example
protocol HasID
{
var recordID: Int {get}
}
infix operator <*> {
associativity right
}
func <*> (left: HasID, right: HasID) -> Bool
{
return left.recordID == right.recordID ? true : false
}
// example:
class Fake: HasID {
var recordID = 1
}
class Bull: HasID {
var recordID = 1
}
let fake = Fake()
let bull = Bull()
if fake <*> bull {
println("is equal")
}