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

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.

Related

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!

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

How to compare two strings ignoring case in Swift language?

How can we compare two strings in swift ignoring case ?
for eg :
var a = "Cash"
var b = "cash"
Is there any method that will return true if we compare var a & var b
Try this :
For older swift:
var a : String = "Cash"
var b : String = "cash"
if(a.caseInsensitiveCompare(b) == NSComparisonResult.OrderedSame){
println("Et voila")
}
Swift 3+
var a : String = "Cash"
var b : String = "cash"
if(a.caseInsensitiveCompare(b) == .orderedSame){
print("Et voila")
}
Use caseInsensitiveCompare method:
let a = "Cash"
let b = "cash"
let c = a.caseInsensitiveCompare(b) == .orderedSame
print(c) // "true"
ComparisonResult tells you which word comes earlier than the other in lexicographic order (i.e. which one comes closer to the front of a dictionary). .orderedSame means the strings would end up in the same spot in the dictionary
if a.lowercaseString == b.lowercaseString {
//Strings match
}
Try this:
var a = "Cash"
var b = "cash"
let result: NSComparisonResult = a.compare(b, options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil, locale: nil)
// You can also ignore last two parameters(thanks 0x7fffffff)
//let result: NSComparisonResult = a.compare(b, options: NSStringCompareOptions.CaseInsensitiveSearch)
result is type of NSComparisonResult enum:
enum NSComparisonResult : Int {
case OrderedAscending
case OrderedSame
case OrderedDescending
}
So you can use if statement:
if result == .OrderedSame {
println("equal")
} else {
println("not equal")
}
localizedCaseInsensitiveContains : Returns whether the receiver contains a given string by performing a case-insensitive, locale-aware search
if a.localizedCaseInsensitiveContains(b) {
//returns true if a contains b (case insensitive)
}
Edited:
caseInsensitiveCompare : Returns the result of invoking compare(_:options:) with NSCaseInsensitiveSearch as the only option.
if a.caseInsensitiveCompare(b) == .orderedSame {
//returns true if a equals b (case insensitive)
}
CORRECT WAY:
let a: String = "Cash"
let b: String = "cash"
if a.caseInsensitiveCompare(b) == .orderedSame {
//Strings match
}
Please note: ComparisonResult.orderedSame can also be written as .orderedSame in shorthand.
OTHER WAYS:
a.
if a.lowercased() == b.lowercased() {
//Strings match
}
b.
if a.uppercased() == b.uppercased() {
//Strings match
}
c.
if a.capitalized() == b.capitalized() {
//Strings match
}
Could just roll your own:
func equalIgnoringCase(a:String, b:String) -> Bool {
return a.lowercaseString == b.lowercaseString
}
For Swift 5
Ignoring the case and compare two string
var a = "cash"
var b = "Cash"
if(a.caseInsensitiveCompare(b) == .orderedSame){
print("Ok")
}
Phone numbers comparison example; using swift 4.2
var selectPhone = [String]()
if selectPhone.index(where: {$0.caseInsensitiveCompare(contactsList[indexPath.row].phone!) == .orderedSame}) != nil {
print("Same value")
} else {
print("Not the same")
}
You can just write your String Extension for comparison in just a few line of code
extension String {
func compare(_ with : String)->Bool{
return self.caseInsensitiveCompare(with) == .orderedSame
}
}
Swift 4, I went the String extension route using caseInsensitiveCompare() as a template (but allowing the operand to be an optional). Here's the playground I used to put it together (new to Swift so feedback more than welcome).
import UIKit
extension String {
func caseInsensitiveEquals<T>(_ otherString: T?) -> Bool where T : StringProtocol {
guard let otherString = otherString else {
return false
}
return self.caseInsensitiveCompare(otherString) == ComparisonResult.orderedSame
}
}
"string 1".caseInsensitiveEquals("string 2") // false
"thingy".caseInsensitiveEquals("thingy") // true
let nilString1: String? = nil
"woohoo".caseInsensitiveEquals(nilString1) // false
Swift 3: You can define your own operator, e.g. ~=.
infix operator ~=
func ~=(lhs: String, rhs: String) -> Bool {
return lhs.caseInsensitiveCompare(rhs) == .orderedSame
}
Which you then can try in a playground
let low = "hej"
let up = "Hej"
func test() {
if low ~= up {
print("same")
} else {
print("not same")
}
}
test() // prints 'same'
You could also make all the letters uppercase (or lowercase) and see if they are the same.
var a = “Cash”
var b = “CASh”
if a.uppercaseString == b.uppercaseString{
//DO SOMETHING
}
This will make both variables as ”CASH” and thus they are equal.
You could also make a String extension
extension String{
func equalsIgnoreCase(string:String) -> Bool{
return self.uppercaseString == string.uppercaseString
}
}
if "Something ELSE".equalsIgnoreCase("something Else"){
print("TRUE")
}
Swift 3
if a.lowercased() == b.lowercased() {
}
Swift 3:
You can also use the localized case insensitive comparison between two strings function and it returns Bool
var a = "cash"
var b = "Cash"
if a.localizedCaseInsensitiveContains(b) {
print("Identical")
} else {
print("Non Identical")
}
extension String
{
func equalIgnoreCase(_ compare:String) -> Bool
{
return self.uppercased() == compare.uppercased()
}
}
sample of use
print("lala".equalIgnoreCase("LALA"))
print("l4la".equalIgnoreCase("LALA"))
print("laLa".equalIgnoreCase("LALA"))
print("LALa".equalIgnoreCase("LALA"))

Swift filter by NSDate object property

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
}
}

Resources