I am trying to override init in my DatabaseHelper but somehow. I am getting an error Property 'self.dateFormatter' not initialized at super.init call I am not getting what i have done wrong here.
public override init() {
super.init() //I am getting error here
dateFormatter.dateFormat = "yyyyMMdd HHmmss";
do {
db = try Connection(dbPath);
try db!.run(FormTbl.create(temporary: false, ifNotExists: true) { t in
t.column(self.ColumnId, primaryKey:true);
t.column(self.FileName);
t.column(self.ReceivedDate);
t.column(self.SentDate);
t.column(self.Status);
t.column(self.AwaitingStatus);
});
try db!.run(SettingTbl.create(temporary: false, ifNotExists:true) {t in
t.column(self.ColumnId, primaryKey:true);
t.column(self.SettingName, unique: true);
t.column(self.SettingValue);
});
try db!.run(PhotoTbl.create(temporary: false, ifNotExists:true) {t in
t.column(self.ColumnId, primaryKey:true);
t.column(self.PhotoFormId);
t.column(self.FileName);
t.column(self.PhotoSentFileName);
t.column(self.ReceivedDate);
t.column(self.SentDate);
t.column(self.Status);
});
try db!.run(GpsTbl.create(temporary: false, ifNotExists:true) {t in
t.column(self.ColumnId, primaryKey: true);
t.column(self.GpsFormId);
t.column(self.Longitude);
t.column(self.Latitude);
t.column(self.Altitude);
t.column(self.DateTime);
});
} catch {
}
}
NOTE: I was trying to migrate my project from swift3 to swift4.2
my full source code is here : full class url
I am really stuck here from a day! Can any body please help me what I am doing wrong here !! Any suggestion will be a great help! Thanks in advance
The code doesn't even compile in Swift 3.
As dateFormatter is a declared but not initialized property in the class you have to initialize it before calling super for example
public override init() {
dateFormatter = DateFormatter()
super.init()
dateFormatter.dateFormat = "yyyyMMdd HHmmss"
and remove the exclamation mark, dateFormatter is supposed to be non-optional
private let dateFormatter : DateFormatter
Apart from that there are three bad practices in your code.
This is Swift. No trailing semicolons
Please conform to the naming convention, this line is highly confusing
public var DateFormatter : DateFormatter {
All structs, classes and enums start with an uppercase letter
All variables, functions and enum cases start with a lowercase letter.
The creation of the shared instance is objective-c-ish and outdated since Swift 2
You're getting that error because your dateFormatter variable isn't initialized in your class. You have to initialize all your variables before you finish your init initializer, or otherwise the compiler will complain for security reasons.
In your code, you're declaring the following:
private let dateFormatter : DateFormatter!
But that is just a constant that holds no value. Isn't pointing to a DateFormatter class. You haven't initialized it. In order to initialize it, you have to assign a DateFormatter() to it, for example, in that exact same line:
private let dateFormatter = DateFormatter()
You should be able to initialize it in your init method too, by adding dateFormatter = DateFormatter() on it, but you'll need to replace your let with a var.
Related
Is it possible to override a default init without subclassing?
I want every instance of DateFormatter created in my app to have the same locale attached to it. Right now, upon each instance creation, I have to:
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX"
Is it possible to have this happen without the need to write out the locale every single time? I'd prefer to not create a DateFormatter subclass as I feel that's a bit of overkill
Thanks!
There is no way to override an existing method of a class without subclassing it or modifying the source code (which you obviously can't do in case of built-in types).
However, for your specific case, you could simply add a method/static variable that returns a DateFormatter specific to your needs.
extension DateFormatter {
static func usPosix() -> DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
return dateFormatter
}
}
And then instead of calling let dateFormatter = DateFormatter(), you do let dateFormatter = DateFormatter.usPosix()
While I personally HIGHLY advise against swizzling, that is certainly an option here.
First, start by creating your own locale that you want to be used throughout your app:
extension DateFormatter {
// Note the "#objc" here, this is necessary for altering the runtime logic
#objc var swizzledLocale: Locale {
return Locale(identifier: "en_US_POSIX")
}
}
Next, you'll want to tell Swift that whenever a DateFormatter's locale is accessed, you want your new swizzledLocale to be used instead:
// get reference to the two getter methods for the properties you want to "switch"
let orig = #selector(getter: DateFormatter.locale)
let new = #selector(getter: DateFormatter.swizzledLocale)
let origMethod = class_getInstanceMethod(DateFormatter.self, orig)!
let newMethod = class_getInstanceMethod(DateFormatter.self, new)!
// performs the actual switch so that whenever "locale" is accessed in code, the returned value will be the value of "swizzledLocale"
method_exchangeImplementations(origMethod, newMethod)
And that's all there is to it! Now whenever you access any DateFormatter's locale, a Locale("en_US_POSIX") will be returned.
Some things to note: first, this will alter ALL instances of locale, not just the locales that you personally use in your own code. That is, if there's any internal code that relies on a formatter's locale, en_US_POSIX will always be used. Second, the second block of code (the one that performs the actual swizzling) should be called exactly one time; look into wrapping it in a dispatch_once block to ensure it only runs once.
Consider the following code:
let nsdate: NSDate? = NSDate()
if let date = nsdate as? Date { // a warning occurs here
print(date)
}
The compiler complains like this: Conditional downcast from 'NSDate?' to 'Date' is a bridging conversion; did you mean to use 'as'?
A cast from NSData to Data has the same problem. How to fix the it?
Try to cast to an optional Date:
if let date = nsdate as Date?
You're trying to optional cast of optional NSDate to NON optional Date. As long as NSDate is bridged from obj-c to Date, so this cast always success, so no optional cast required here, just basic cast as is enough. Then you need to cast optional value, so the resulting value has to be optional too, therefore Date? is appropriate here.
Swift 3.1 distinguishes
An optional down cast as? Foo
It casts a more unspecific to a more specific type for example
let dict : [String:Any] = ["Foo" : 12]
let number = dict["Foo"] as? Int
A bridge cast of an optional type as Foo?
It bridges a (Core)Foundation type to a toll free Swift type and vice versa.
It's the optional equivalent of the usual non-optional syntax
let string : NSString = "Foo"
let swiftString = string as String
The difference is subtle for the developer but very useful for the compiler.
Basically don't use the NS... Foundation classes in Swift 3 if there is a native Swift counterpart.
Try this:
let nsdate: NSDate? = NSDate()
if let date = nsdate {
print(date)
}
The compiler knows it´s an NSDate if unwrapped, so what you are doing is actually casting an NSDate to a Date
In several places here, it has been suggested that using a computed property within an extension of NSDate might a good way to obtain a string version of a date via a NSDateFormatter, like so:
extension NSDate {
public var UTC : String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")
return formatter.stringFromDate(self)
}
}
However, allocating a NSDateFormatter is expensive and it is suggested that they be created once and cached. The above code creates the NSDateFormatter every time a date is formatted, and I'm wondering if there is a way to create the NSDateFormatter once inside the extension for reuse?
Obviously, I could create it just once outside the extension, but that seems to defeat the encapsulation that characterizes classes.
I am reminded of: https://xkcd.com/1179/ !!
You can add static members to class extensions just the same as on classes. You need to prefix the class name to the static member name when you use it, e.g. NSDate.dateFormatterUTC, even if you’re using it in the same class.
This works:
extension NSDate {
private static let dateFormatterUTC: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")
return formatter
}()
public var UTC : String {
return NSDate.dateFormatterUTC.stringFromDate(self)
}
}
It’s also not the worst thing in the world just to use a private constant:
private let dateFormatterUTC: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")
return formatter
}()
extension NSDate {
public var UTC : String {
return dateFormatterUTC.stringFromDate(self)
}
}
This is not significantly worse than the static class member, because Swift’s private is file-private, not type-private. These two declarations of dateFormatterUTC have the same scope. Even in the first example, NSDate.dateFormatterUTC is accessible throughout the entire file it’s declared in.
I do agree that the static version is preferable, but for stylistic reasons only: I like the way it’s indented right next to the thing that uses it.
As Gwendal wisely notes above, this approach assumes UTC will only ever be called from one thread. Although static let and global let are both thread-safe in Swift, the NSDateFormatter class is not! Looks like it’s threadsafe starting in iOS 7. Phew.
Still, always good to keep a thread safety warning next to any mention of singletons. If you do want to use a non-threadsafe helper object from multiple threads, consider either creating a new helper on every call, or using NSThread.currentThread().threadDictionary to create a per-thread instance. Be sure to do a little profiling to make sure you’re actually solving a performance problem before opting for the more complex thread-local option.
I have an object from the server that is recognized by Swift 2.1 as either NSDate or NSNull. I want to put it into a struct with a property of type NSDate.
Is that possible? If not, how should I handle this to be type safe later when I use it?
struct Data {
var completedAt: [NSDate]
var name: [String]
var gender: [Bool]
}
but sometimes completedAt comes from the server as NSNull:
completedAt = "<null>";
Any help is very much appreciated, thank you.
Based on my interpretation of the text in the question you didn't mean to declare the variables as arrays.
This is how I handle my parson and I think it works pretty neatly.
The date formatter should probable not be initiated in every iteration of the constructor. If you won't use the date regularly you might want to keep the detesting until you need to parse the date or you can have a static date formatter utility that you only instantiate once.
struct Data {
var completedAt: NSDate?
var name: String
var gender: Bool
init?(dictionary: [String:AnyObject]) {
//Guessing that you want some of the values non optional...
guard let name = dictionary["name"] as? String,
let gender = dictionary["gender"] as? String
else {
return nil
}
self.name = name
self.gender = gender
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
//safe handle of optional values
if let completedAtString = dictionary["completedAt"] as? String, completedAt = dateFormater.dateFromString(completedAtString) {
self.completedAt = completedAt
}
}
}
Take a step back. For each item that the server might provide, there is no guarantee whatsoever that you receive what you expect, since you cannot control the server. So you need to decide how to react to which input.
In the case of expecting a date for example (if your data comes in JSON, that means you likely expect a string formatted in a certain way), the actual data that you receive might be an array, dictionary, string, number, bool, null, or nothing. You might then for example decide that you want to interpret nothing or null or an empty string as nil, that you want to interpret a string containing a well-formatted date as an NSDate, and anything else a fatal error in a debug version, and as either nothing or a fatal error in a release version. On the other hand, if an NSDate is absolutely required then you might interpret anything that doesn't give an NSDate as an error.
Then you write a function that delivers exactly what you want and use it. That way you can parse complex data, with your code warning you when something isn't as it should be, and with your code either surviving any possible input, or deliberately crashing on wrong input, as you want it.
I want to be able to get the current time as a decimal point number so it can be used. so for example if the time is 13:46 I want to get it as 13.46
It seems simple but I am struggling getting to it.
We can use an NSDateFormatter to do exactly this:
extension NSDate {
func currentTime() -> String {
let formatter = NSDateFormatter()
formatter.dateFormat = "HH.mm"
return formatter.stringFromDate(self)
}
}
And now we just use it by calling it on any instance of NSDate:
let now = NSDate()
print(now.currentTime())
you can stringify the time and serch lastindexOf(":") and substitue it with a "."
UPDATE
I don't really catch what program language are you using, but it's plenty of library for stringify object so if you have a 13:46 you can convert it to String and, in the same string library you could find the method lastIndexOf(char). But if you don't find it you can always write following this concepts:
String are an array of Char so you can cycle it and convert the char in that position in the char you need.