Identify MOT Dates which could be in various formats I want to significantly tidy this code as its cluttered and repetitive

var myDate = "No MOT"

if ((myData[0]["motTests"][0]["expiryDate"].string) != nil) {

    let theDate=myData[0]["motTests"][0]["expiryDate"].string
    let msYear = theDate!.prefix(4)
    let msMonth = theDate!.suffix(5).prefix(2)
    let msDay = theDate!.suffix(2)
    let mYear = Int (msYear)
    let mMonth = Int (msMonth)
    let mDay = Int(msDay)
    self.motDue = self.convertDate(aYear: mYear!, aMonth: mMonth!, aDay: mDay!)

    myDate=msDay+("/")+msMonth+"/"+msYear
}
if ((myData[0]["motTestExpiryDate"].string) != nil){
    let theDate=myData[0]["motTestExpiryDate"].string

    let msYear = theDate!.prefix(4)
    let msMonth = theDate!.suffix(5).prefix(2)
    let msDay = theDate!.suffix(2)

    let mYear = Int (msYear)
    let mMonth = Int (msMonth)
    let mDay = Int(msDay)
    self.motDue = self.convertDate(aYear: mYear!, aMonth: mMonth!, aDay: mDay!)

    myDate=msDay+("/")+msMonth+"/"+msYear
}

self.myMOT.text=String(myDate)


The code checks to see if the vehicle has an MOT, if it does its sets myDate to the date. If not it checks when the first MOT is due and sets myDate to that's and if not myDate stays set to "No MOT"

It works but needs improving.
Best practice is to get away from manipulating date strings yourself. And use Date types in your model. 

So, when you parse the JSON, use DateFormatter to convert the non-user-friendly date strings to Date objects. And when presenting the dates in the UI, use another DateFormatter to present the date in a nice, user-friendly format. I’d also suggest using JSONDecoder for decoding the JSON into your model, so you don’t have to sprinkle your code with cryptic, error-prone, dictionary keys.

So, first, I would suggest specifying a date formatter when you decode. Because most of the dates are in the form yyyy.MM.dd, I’d use that. So, for example:

do {
    let decoder = JSONDecoder()
    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.dateFormat = "yyyy.MM.dd"
    decoder.dateDecodingStrategy = .formatted(formatter)
    let result = try decoder.decode([Vehicle].self, from: data)
    print(result)
} catch {
    print(error)
}


The trick, though, is that one field, completedDate in MOTTest, uses a different string format, namely, yyyy.MM.dd HH:mm:ss, so I’d have JSONDecoder just parse that as a string, but then have a computed property to translate that one date/time string into a Date:

struct Vehicle: Decodable {
    let registration: String
    let make: String
    let model: String
    let firstUsedDate: String
    let fuelType: String
    let primaryColour: String
    let vehicleId: String
    let registrationDate: Date
    let manufactureDate: Date
    let engineSize: String
    let motTests: [MOTTest]
}

struct MOTTest: Decodable {
    private static let formatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "yyyy.MM.dd HH:mm:ss"
        return formatter
    }()

    let completedDateString: String
    var completedDate: Date? { MOTTest.formatter.date(from: completedDateString) }
    let testResult: String
    let expiryDate: Date?
    let odometerValue: String
    let odometerUnit: String
    let odometerResultType: String
    let motTestNumber: String
    let rfrAndComments: [ReasonForRejection]

    private enum CodingKeys: String, CodingKey {
        case completedDateString = "completedDate"
        case testResult, expiryDate, odometerValue, odometerUnit, odometerResultType, motTestNumber, rfrAndComments
    }
}

struct ReasonForRejection: Decodable {
    let text: String
    let type: String
    let dangerous: Bool
}


That way, JSONDecoder not only does the decoding for us to our model structure (avoiding that unstructured collection returned by JSONSerialization), but it also converts all of those yyyy.MM.dd strings to Date instances for us. And for completedDate, our computed property takes care of that for us.



By the way, I used the following sample JSON:

[
    {
        "registration": "ZZ99ABC",
        "make": "FORD",
        "model": "FOCUS",
        "firstUsedDate": "2010.11.13",
        "fuelType": "Petrol",
        "primaryColour": "Yellow",
        "vehicleId": "4Tq319nVKLz+25IRaUo79w==",
        "registrationDate": "2010.11.13",
        "manufactureDate": "2010.11.13",
        "engineSize": "1800",
        "motTests":[
            {
                "completedDate": "2013.11.03 09:33:08",
                "testResult": "PASSED",
                "expiryDate": "2014.11.02",
                "odometerValue": "47125",
                "odometerUnit": "mi",
                "odometerResultType": "READ",
                "motTestNumber": "914655760009",
                "rfrAndComments": []
            },
            {
                "completedDate": "2013.11.01 11:28:34",
                "testResult": "FAILED",
                "odometerValue": "47118",
                "odometerUnit": "mi",
                "odometerResultType": "READ",
                "motTestNumber": "841470560098",
                "rfrAndComments":[
                    {
                        "text": "Front brake disc excessively pitted (3.5.1h)",
                        "type": "FAIL",
                        "dangerous": true
                    },
                    {
                        "text": "Nearside Rear wheel bearing has slight play (2.6.2)",
                        "type": "ADVISORY",
                        "dangerous": false
                    }
                ]
            },
            {
                "completedDate": "2018.05.20 11:28:34",
                "testResult": "FAILED",
                "odometerValue": "57318",
                "odometerUnit": "mi",
                "odometerResultType": "READ",
                "motTestNumber": "741489560458",
                "rfrAndComments":[
                    {
                        "text": "Nearside Parking brake efficiency below requirements (1.4.2 (a) (i))",
                        "type": "MAJOR",
                        "dangerous": false
                    },
                    {
                        "text": "Front brake disc excessively pitted (3.5.1h)",
                        "type": "DANGEROUS",
                        "dangerous": false
                    },
                    {
                        "text": "tyres wearing unevenly",
                        "type": "USER ENTERED",
                        "dangerous": true
                    }
                ]
            }
        ]
    },
    {
        "registration": "YY09DEF",
        "make": "BMW",
        "model": "Z4",
        "firstUsedDate": "2009.01.25",
        "fuelType": "Petrol",
        "primaryColour": "Green",
        "vehicleId": "3Fv916dPLGx=43PRaKa45e++",
        "registrationDate": "2009.01.25",
        "manufactureDate": "2009.01.25",
        "engineSize": "1800",
        "motTests":[
            {
                "completedDate": "2012.01.10 10:27:56",
                "testResult": "PASSED",
                "expiryDate": "2013.01.09",
                "odometerValue": "12345",
                "odometerUnit": "mi",
                "odometerResultType": "READ",
                "motTestNumber": "345655760009",
                "rfrAndComments": []
            }
        ]
    }
]




Needless to say, if you want to decode manually, like you did in your question, just have two parsing date formatters, one for the yyyy.MM.dd format and another for the yyyy.MM.dd HH:mm:ss format. 



Finally, you might ask “how do I show these Date objects in my UI?” 

You define UI date formatters:

let dateOnlyFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    return formatter
}()

let dateTimeFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .medium
    return formatter
}()


Then you can do things like:

if let date = result.first?.motTests.first?.expiryDate {
    let string = dateOnlyFormatter.string(from: date)
    print("Expiry date:", string)
}



  Expiry date: 2 Nov 2014


And

if let date = result.first?.motTests.first?.completedDate {
    let string = dateTimeFormatter.string(from: date)
    print("Completed date:", string)
}



  Completed date: 3 Nov 2013 at 9:33:08


Clearly, use whatever dateStyle and timeStyle you want. But the idea is that we’ll show these strings in a nice, localized format as dictated by the user’s device settings.