Abdulrahman Alattas
Abdulrahman Alattas

Reputation: 75

Variable doesn't store what given to it - Swift 3

I'm having trouble with storing a date that I got from SQLite DB into a Date Var, I'm trying to save the date coming from DB to the ResultTask.AlarmDate after converting it from String to Date.

When I tried and deleted the ? from ResultTask.AlarmDate? it stored the data fine, but when I have a task without an alarm I get an error cause I want to store nil it the var when there's no alarm, and tried multiple other ways to fix it but nothing work.

I made a break point right after the issue and used the Console to see the data and got confused, cause the data shown when I print what I'm trying to store in the var but the var is still nil, all shown below.

Notes

The code that getting data from DB works fine, I'm using the Objective-C FM-Database library, and result is seen in the Console below.

the .toDate() is an extension for String that I use and it code is in AppDelegate.swift as seen bellow, and it work just fine.

the Task Variable type is something I made in a Task.swift file to easy the work, and it work just fine & shown below.

The Function:

func GetTasks() -> [Task]{
        var Tasks : [Task] = [Task]()

        let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)
        let docsDir = dirPaths[0]
        databasePath = (docsDir + "/" + "Tasks.db") as NSString

        let TaskRecorderDB = FMDatabase(path: databasePath as String)
        if (TaskRecorderDB?.open())! {
            let querySQL = "Select * from 'Tasks' ORDER BY Priority DESC"
            let results:FMResultSet? = TaskRecorderDB?.executeQuery(querySQL, withArgumentsIn: nil)

            while results!.next() {
                var ResultTask : Task! = Task()
                ResultTask.ID = Int((results?.int(forColumn: "ID"))!)
                ResultTask.FileName = results?.string(forColumn: "FileName")
                ResultTask.Name = results?.string(forColumn: "Name")
                ResultTask.Categorie = Int((results?.int(forColumn: "Categorie"))!)
                ResultTask.Priority = Int((results?.int(forColumn: "Priority"))!)
                ResultTask.AlarmDate? = (results?.string(forColumn: "Alarm").toDate())!
                //Break point
                ResultTask.Repeat? = (results?.string(forColumn: "Repeat"))!
                ResultTask.Completed = Int((results?.int(forColumn: "Completed"))!).toBool()

                if(ResultTask.Completed == false){
                    Tasks.append(ResultTask)
                }
                ResultTask = nil
            }

            TaskRecorderDB?.close()
        } else {
            print("Error #DB06: \(TaskRecorderDB?.lastErrorMessage())")
        }

        return Tasks
    }

Console:

(lldb) print (results?.string(forColumn: "Alarm").toDate())!
(Date) $R0 = 2016-11-23 17:21:00 UTC
(lldb) print ResultTask.AlarmDate
(Date?) $R1 = nil
(lldb) 

AppDelegate.swift:

extension String {
    func toDate () ->Date! {
        var FinalDate : Date! = nil
        if (self != ""){
            let DateFormatter : Foundation.DateFormatter = Foundation.DateFormatter()
            DateFormatter.locale = Locale.current
            DateFormatter.dateFormat = "y-MM-dd_HH-mm-ss"
            FinalDate = DateFormatter.date(from: self)!
        }

        return FinalDate
    }
}

Task.swift:

import Foundation

class Task : NSObject {
    var ID : Int! = nil
    var FileName : String! = nil
    var Name : String! = nil
    var Categorie : Int! = nil
    var Priority : Int! = nil
    var AlarmDate : Date! = nil
    var Repeat : String! = nil
    var Expired : Bool! = nil
    var Completed : Bool! = nil
}

Thanks in advance.

Edit for @KaraBenNemsi :

I updates the .toDate() as you said.

& in task I used var AlarmDate : Date? instead of var AlarmDate : Date! = nil (I didn't use you're other tip in Task.swift for now cause I'll need to change a lot of other code in the app).

And used ResultTask.AlarmDate = results?.string(forColumn: "Alarm").toDate() and I get a fatal error: unexpectedly found nil while unwrapping an Optional value in it when the task doesn't have an alarm.

Upvotes: 0

Views: 233

Answers (2)

christopher.online
christopher.online

Reputation: 2774

Try to change this line in the task class since it can be optional

from this

var AlarmDate: Date! = nil

to this

var AlarmDate: Date?

And then use this line to set the alarm

ResultTask.AlarmDate = results?.string(forColumn: "Alarm")?.toDate()

Also variable names should usually start with a lowercase letter.

I also think this line is unnecessary. You can just omit that line.

ResultTask = nil

Please also change the extension to

extension String {
func toDate() -> Date? {
    guard self != "" else {
         return nil
    }
    var finalDate: Date?
    let DateFormatter : Foundation.DateFormatter = Foundation.DateFormatter()
    DateFormatter.locale = Locale.current
    DateFormatter.dateFormat = "y-MM-dd_HH-mm-ss"
    finalDate = DateFormatter.date(from: self)
    return finalDate
   }
}

And maybe try to get more into how optionals work https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html

Addition: Your class task should probably also look more like below. But then you have to change code in other places as well. It also forces you to pass all the required parameters in the constructor when initialising the task object.

class Task {
    var id: Int
    var fileName: String
    var name: String
    var category: Int
    var priority: Int
    var alarmDate: Date?
    var repeating: String
    var expired: Bool
    var completed: Bool

     init(id: Int, fileName: String, name: String, category: Int, priority: Int, repeating: String, expired: Bool, completed: Bool) {
        self.id = id
        self.fileName = fileName
        self.name = name
        self.category = category
        self.priority = priority
        self.repeating = repeating
        self.expired = expired
        self.completed = completed
    }
}

And here is also a link to a Swift style guide:

https://github.com/raywenderlich/swift-style-guide

Upvotes: 2

user2908787
user2908787

Reputation:

Change your task to have this

var AlarmDate : Date?

And then do this

if let alarm = results?.string(forColumn: "Alarm").toDate() {
    ResultTask.AlarmDate = alarm
}

If any nil is found while unwrapping results, you won't set the value of ResultTask.AlarmDate.

Upvotes: 0

Related Questions