Swift code with multiple NSDateFormatter - optimization

912 views Asked by At

For swift learning purposes, I'm work on personal Core Data project. So far everything is working, but I cannot figure out how to optimize the code. By optimization I mean only visual optimization, the performance is different story and is too early for me. In the class below I'm using dateFormatter quite often, so I'm trying to avoid it. Seems that I miss something fundamental in the programming, which is not surprise for absolute beginner and amateur.

import UIKit
import CoreData

class AddEditViewController: UIViewController {

    @IBOutlet weak var startDateTextField: UITextField!
    @IBOutlet weak var endDateTextField: UITextField!
    @IBOutlet weak var shipNameTextField: UITextField!
    @IBOutlet weak var positionOnBoardTextField: UITextField!
    @IBOutlet weak var workingDaysLabel: UILabel!

    var startDatePassedFromTable: NSDate!
    var endDatePassedFromTable: NSDate!
    var shipNamePassedFromTable: String!
    var positionOnBoardPassedFromTable: String!
    var workingDaysPassedFromTable: Int!

    var exsistingContract: NSManagedObject!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Load the view with the data passed from the table
        if (exsistingContract != nil) {

            self.title = "Edit Contract"

            var dateFormatter = NSDateFormatter()
            dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
            dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
            dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT+0:00")
            dateFormatter.dateFormat = "dd-MM-yyyy"

            startDateTextField.text = dateFormatter.stringFromDate(startDatePassedFromTable)
            endDateTextField.text = dateFormatter.stringFromDate(endDatePassedFromTable)
            shipNameTextField.text = shipNamePassedFromTable
            positionOnBoardTextField.text = positionOnBoardPassedFromTable
            workingDaysLabel.text = "\(workingDaysPassedFromTable)"

        } else {
            self.title = "Add Contract"
        }
    }

    // Start Date Text Field- action and format for DatePicker
    @IBAction func startDateTextFieldEditing(sender: UITextField) {
        var datePickerView:UIDatePicker = UIDatePicker()
        datePickerView.datePickerMode = UIDatePickerMode.Date
        sender.inputView = datePickerView
        datePickerView.addTarget(self, action: Selector("startDatePickerValueChanged:"), forControlEvents: UIControlEvents.ValueChanged)
    }

    func startDatePickerValueChanged(sender: UIDatePicker) {
        var dateformatter = NSDateFormatter()
        dateformatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
        dateformatter.dateStyle = NSDateFormatterStyle.MediumStyle
        dateformatter.dateFormat = "dd-MM-yyyy"
        startDateTextField.text = dateformatter.stringFromDate(sender.date)

        return calculateWorkingDays()
    }

    // End Date Text Field - action and format for DatePicker
    @IBAction func endDateTextFieldEditing(sender: UITextField) {
        var datePickerView:UIDatePicker = UIDatePicker()
        datePickerView.datePickerMode = UIDatePickerMode.Date
        sender.inputView = datePickerView
        datePickerView.addTarget(self, action: Selector("endDatePickerValueChanged:"), forControlEvents: UIControlEvents.ValueChanged)
    }

    func endDatePickerValueChanged(sender: UIDatePicker) {
        var dateformatter = NSDateFormatter()
        dateformatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
        dateformatter.dateStyle = NSDateFormatterStyle.MediumStyle
        dateformatter.dateFormat = "dd-MM-yyyy"
        endDateTextField.text = dateformatter.stringFromDate(sender.date)

        return calculateWorkingDays()
    }

    // Hide keyboard or DatePicker
    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        self.view.endEditing(true)
    }

    // Calculate the working days from String -> NSDate of startDateTextField and endDateTextField
    func calculateWorkingDays() {

        // Check if the startDateTextField AND endDateTextField are not empty
        if !startDateTextField.text.isEmpty && !endDateTextField.text.isEmpty {

            // Date format and localisation
            let calendar = NSCalendar.currentCalendar()
            let unit: NSCalendarUnit = .CalendarUnitDay
            var dateFormatter = NSDateFormatter()
            dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
            dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT+0:00")
            dateFormatter.dateFormat = "dd-MM-yyyy"
            let startDatePicker: NSDate = dateFormatter.dateFromString(startDateTextField.text)!
            let endDatePicker: NSDate = dateFormatter.dateFromString(endDateTextField.text)!

            // Calculate the diference and set the workingDays Label to Days -> String from Int + 1
            let components = calendar.components(unit, fromDate: startDatePicker, toDate: endDatePicker, options: nil)
            workingDaysLabel.text = "\(components.day + 1)"
        }
    }

    @IBAction func buttonSavePressed(sender: UIBarButtonItem) {
        // Check for empty TextField
        if startDateTextField.text.isEmpty || endDateTextField.text.isEmpty || shipNameTextField.text.isEmpty || positionOnBoardTextField.text.isEmpty {
            // Allert message with OK button
            let alertController = UIAlertController(title: "Error Save Contract", message: "Start and End Date are required", preferredStyle: UIAlertControllerStyle.Alert)
            let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil)
            alertController.addAction(okAction)
            presentViewController(alertController, animated: true, completion: nil)

        } else {
            // Save data and return to the root controller
            // Check if contract is exsisting and EDIT or NEW for save new contract
            var appDelegate: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
            var context: NSManagedObjectContext = appDelegate.managedObjectContext!
            let entity = NSEntityDescription.entityForName("Contract", inManagedObjectContext: context)!

            var dateFormatter = NSDateFormatter()
            dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
            dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT+0:00")
            dateFormatter.dateFormat = "dd-MM-yyyy"

            // Check if contract exsist
            if (exsistingContract != nil) {

                // Edit exsisting contract
                exsistingContract.setValue((dateFormatter.dateFromString(startDateTextField.text)), forKey: "startdate")
                exsistingContract.setValue((dateFormatter.dateFromString(endDateTextField.text)), forKey: "enddate")
                exsistingContract.setValue(shipNameTextField.text, forKey: "ship")
                exsistingContract.setValue(positionOnBoardTextField.text, forKey: "position")
                exsistingContract.setValue(workingDaysLabel.text?.toInt(), forKey: "workingdays")

            } else {

                // Create new contract
                var newContractData = Contract(entity: entity, insertIntoManagedObjectContext: context)
                newContractData.startdate = dateFormatter.dateFromString(startDateTextField.text)!
                newContractData.enddate = dateFormatter.dateFromString(endDateTextField.text)!
                newContractData.ship = shipNameTextField.text
                newContractData.position = positionOnBoardTextField.text
                newContractData.workingdays = workingDaysLabel.text!.toInt()!

            }

            // Save and return to root
            context.save(nil)
            self.navigationController?.popToRootViewControllerAnimated(true)

        }

    }

    @IBAction func cancelButtonPressed(sender: UIBarButtonItem) {
        self.navigationController?.popToRootViewControllerAnimated(true)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}
2

There are 2 answers

0
Greg On BEST ANSWER

You can create only one instance on NSDateFormatter and use it where you need it:

lazy var dateFormatter: NSDateFormatter = {
    let f = NSDateFormatter()
    f.locale = NSLocale(localeIdentifier: "en_US_POSIX")
    f.timeZone = NSTimeZone(abbreviation: "GMT+0:00")
    f.dateFormat = "dd-MM-yyyy"
    return f
}()
0
Musa almatri On

You can also create a Singleton and use it where you need:

import UIKit

class Dater: DateFormatter {

    static var shared = Dater()

    private override init() {
        super.init()
        self.locale = NSLocale(localeIdentifier: "en_US") as Locale!
    }

    func date(from string: String, format: String) -> Date? {
        self.dateFormat = format
        return date(from: string)
    }

    func string(from date: Date, format: String) -> String? {
        self.dateFormat = format
        return string(from: date)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

you can use it like this :

Dater.shared.string(from: Date(), format: "MM/dd/yyyy")

or

Dater.shared.date(from: "14/02/2012", format: "MM/dd/yyyy")