Abdulrahman Alattas Abdulrahman Alattas - 9 days ago 6
Swift Question

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.

Answer

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

Comments