Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I resolve this build issue - cannot assign to property: 'date' is a get only property

Tags:

swift

class Event {

    var name : String?
    var FullDate : NSDate?

    var date: String? {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "EEEE MMM dd"
        return dateFormatter.stringFromDate(FullDate!)
    }

    init ()
}


class EventsViewController: UIViewController, UITableViewDelegate , UITableViewDataSource 
{
    let Event1 = Event()

    override func viewDidLoad() {
        super.viewDidLoad()
        Event1.date = "10.01.16" // here is the issue
        Event2.date = "12.11.16"  // "" "" "" ""
    }

I dont understand why I can use the FullDate variable from my custom class Event but not the date variable. thanks for your help

like image 1000
jerem Avatar asked Dec 03 '15 16:12

jerem


Video Answer


1 Answers

Your date is actually a computed property that only has a getter So it can only be read, not written too.

In contrast your FullDate is a stored property.

computed properties are often used to hide functionality from other classes or expose buried data in a convenient way.

Below there is some illustration in the SomeClass object.

  • stored properties are just regular old properties
  • computed properties are getters and maybe also setters. A good example is height and width of CGRect, these are actually from CGRect.size but are implemented as convenience getters
  • property observers are functions executed when stored properties change value. didSet and willSet
  • access control on get and set allows you to create stored properties that are read or write only depending on access.

class SomeClass {

    // just a variable
    var storedProperty : Int = 10

    // just a variable but the setter is hidden from other "files"
    private(set) var storedPropertyWithPartialAccessControl : Int = 0

    // only calculates when set
    var derivedProperty : Int = 0 {
        didSet {
            storedProperty = doStuffs(derivedProperty)
        }
    }

    // calculates every get and set
    var computedProperty : Int {
        get { // get is needed
            return doStuffs(storedProperty)
        }
        set(value) { // set is not needed, using only get gives you a read only property
            storedProperty = doReverseStuffs(value)
        }
    }

    // hidden functions that are used in the computed property
    private func doStuffs(value:Int) -> Int {
        return value * 10
    }

    private func doReverseStuffs(value:Int) -> Int {
        return value / 10
    }
}

Because NSDateFormatter is very inefficient, it is very bad to use it in a computed property. One will be created every get. A lot of time and energy will be wasted because the resulting value might be the same most of the time.

Best practice :

  • Use computed properties to get stored properties and set to do calculations.
  • Use a Singleton to store the NSDateFormatter

A much much better way to go:

class SomeEvent {

    // Store calculated results. 
    // This is private to make the class cleaner in use.
    private var storedDate : NSDate?

    private var storedFormattedDate : String = ""

    // get returns stored values without calculations
    // set updates both stored values after calculations
    var date : NSDate? {
        get {
            return storedDate
        }
        set(value) {
            storedFormattedDate = format(date: value)
            storedDate = value
        }
    }

    var formattedDate : String {
        get {
            return storedFormattedDate
        }
        set(value) {
            storedDate = readDateFrom(dateString: value)
            storedFormattedDate = format(date: storedDate)
        }
    }

    // hidden functions, again to make the class cleaner in use.
    private func format(date maybeDate:NSDate?) -> String {

        guard let date = maybeDate else {
            return ""
        }
        guard let dateFormatter = MyDateFormatter.shared().dateFormatter else {
            return ""
        }
        return dateFormatter.stringFromDate(date)

    }

    private func readDateFrom(dateString string:String) -> NSDate? {

        // get detector
        if let detector = MyDateFormatter.shared().dateReader {
            // results
            let matches = detector.matchesInString(string, options: [], range: NSRange(location: 0, length: string.characters.count))
            // first result
            if let match = matches.first, let date = match.date {
                return date
            }
        }

        // just format if there is no detector
        if let dateFormatter = MyDateFormatter.shared().dateFormatter {
            if let date = dateFormatter.dateFromString(string) {
                return date
            }
        }

        // everything failed
        return nil
    }
}

Singleton to store the NSDateFormatter and NSDataDetector:

Google: Swift Singleton

class MyDateFormatter {

    private static var privateShared : MyDateFormatter?

    var dateFormatter : NSDateFormatter?
    var dateReader : NSDataDetector?

    class func shared() -> MyDateFormatter {
        guard let uwShared = privateShared else {
            privateShared = MyDateFormatter()
            return privateShared!
        }
        return uwShared
    }

    class func destroy() {
        privateShared = nil
    }

    private init() {
        dateReader = try? NSDataDetector(types: NSTextCheckingType.Date.rawValue)
        dateFormatter = NSDateFormatter()
        dateFormatter!.dateFormat = "EEEE MMM dd"
    }
}

Tests:

let event = SomeEvent()
event.formattedDate = "tomorrow"
event.formattedDate // "Friday Dec 04"
event.formattedDate = "25/02/2010"
event.formattedDate // "Thursday Feb 25"
event.formattedDate = "Sunday Mar 13"
event.formattedDate // "Sunday Mar 13"
like image 141
R Menke Avatar answered Dec 03 '22 00:12

R Menke