Search code examples
swiftnsdateformatterdate-formatting

Date formatter crash in swift


Please help me to modify this part of code,

if let dateVaule = UserDefaults().value(forKey: SplashSeenDate){
            let dateStr  = dateVaule as! String
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            dateFormatter.timeZone = TimeZone.ReferenceType.local
            let date = dateFormatter.date(from:dateStr)
            if Global.sharedInstance.Splashs != nil && Global.sharedInstance.Splashs?.count != 0{

                for (i,splash) in (Global.sharedInstance.Splashs?.enumerated().reversed())!{
                    if !checkTimeStamp(date: splash.create_timestamp,StoredDate: date!){
                        Global.sharedInstance.Splashs?.remove(at: i)
                    }
                }
                print(Global.sharedInstance.Splashs?.count ?? "-1")
            }
        }

Honestly speaking, i am very confuse with the date formatter. when this project launch, it occured crash at

if !checkTimeStamp(date: splash.create_timestamp,StoredDate: date!){

However, I could not replicate this crash... **Only a few user with iOS10 occured this crash.**Please kindly advise, Thanks a lot!

func checkTimeStamp(date: String!,StoredDate: Date) -> Bool {
    let dateFormatter: DateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    dateFormatter.locale = Locale(identifier:"en_US_POSIX")
    let datecomponents = dateFormatter.date(from: date)

    if (datecomponents! >= StoredDate) {
        return true
    } else {
        return false
    }
}

Solution

  • You are converting String to Date in two different ways:

    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    dateFormatter.timeZone = TimeZone.ReferenceType.local //###Why don't you use `TimeZone.current`?
    let date = dateFormatter.date(from:dateStr)
    

    And in checkTimeStamp(date: String!,StoredDate: Date) -> Bool:

    let dateFormatter: DateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    dateFormatter.locale = Locale(identifier:"en_US_POSIX")
    let datecomponents = dateFormatter.date(from: date)
    

    In your case, lacking this line is critical for your first conversion:

    dateFormatter.locale = Locale(identifier: "en_US_POSIX")
    

    You can find many articles discussing about DateFormatter (or NSDateFormatter in old articles) returning nil.

    When you use DateFormatter with fixed format like "yyyy-MM-dd HH:mm:ss", you must set locale to Locale(identifier: "en_US_POSIX").

    Maybe you'd better define one conversion method (or computed property) to convert String to Date which fits for your app's requirements, and use it everywhere you need String to Date conversion:

    extension String {
        var toAppDate: Date? {
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            dateFormatter.timeZone = TimeZone.current
            dateFormatter.locale = Locale(identifier: "en_US_POSIX") //### <- Do not miss.
            return dateFormatter.date(from: self)
        }
    }
    

    Generally, your code uses too much forced-unwrapping or forced-casting. You can re-write it without using any forced-something:

        if
            let dateStr = UserDefaults().string(forKey: SplashSeenDate),
            let date = dateStr.toAppDate,
            let splashes = Global.sharedInstance.Splashs, !splashes.isEmpty
        {
            //### In most cases, `filter` is faster than repeated `remove`.
            Global.sharedInstance.Splashs = splashes.filter {
                checkTimeStamp(date: $0.create_timestamp, storedDate: date)
            }
            print(Global.sharedInstance.Splashs?.count ?? "-1")
        }
    

    (Assuming create_timestamp is not Optional, and renamed parameter label StoredDate: to storedDate:.)