Standing the Test of Time: The Date Provider Pattern

64
STANDING THE TEST OF TIME THE DATE PROVIDER PATTERN DEREK LEE | TOKYO IOS MEETUP APRIL 2017

Transcript of Standing the Test of Time: The Date Provider Pattern

Page 1: Standing the Test of Time: The Date Provider Pattern

STANDING THE TEST OF TIMETHE DATE PROVIDER PATTERN

DEREK LEE | TOKYO IOS MEETUP APRIL 2017

Page 2: Standing the Test of Time: The Date Provider Pattern

!

Page 3: Standing the Test of Time: The Date Provider Pattern

CALENDAR

Page 4: Standing the Test of Time: The Date Provider Pattern

TO DO

Page 5: Standing the Test of Time: The Date Provider Pattern

TRANSPORTATION

Page 6: Standing the Test of Time: The Date Provider Pattern

SHOPPING

Page 7: Standing the Test of Time: The Date Provider Pattern

DRUMMING

Page 8: Standing the Test of Time: The Date Provider Pattern

FLIGHT DEPARTUREHANEDA ✈ OKINAWAAPR-25-2017 7:12PM

Page 9: Standing the Test of Time: The Date Provider Pattern

HOW MUCH TIME DO I HAVEBEFORE MY DEPARTURE?

Page 10: Standing the Test of Time: The Date Provider Pattern

LETS START WITH A SIMPLE STRUCTstruct Flight { let flightNumber: String let departureCityCode: String let departureDateTime: Date let arrivalCityCode: String let arrivalDateTime: Date}

Page 11: Standing the Test of Time: The Date Provider Pattern

WE CAN EASILY INITIALIZE WITH SOME DATAlet tripHome = Flight( flightNumber: "JAL925", departureCityCode: "HND", departureDateTime: Date( timeIntervalSince1970: 1493115120 // 4-25-2017 19:12 ), arrivalCityCode: "OKA", arrivalDateTime: Date( timeIntervalSince1970: 1493125920 // 4-25-2017 22:12 ))

Page 12: Standing the Test of Time: The Date Provider Pattern

CALCULATING THE REMAINING TIME IS EASY...?func timeRemainingUntilDeparture() -> TimeInterval { let departureTimeInterval = departureDateTime.timeIntervalSinceNow

let currentTimeInterval = Date().timeIntervalSinceNow

return departureTimeInterval - currentTimeInterval}

Page 13: Standing the Test of Time: The Date Provider Pattern

AND WITH SOME QUICK FORMATTING...func formatTimeInterval(timeInterval: TimeInterval) -> String { let d = Int(timeInterval / 86400) let h = Int(timeInterval / 3600) % 24 let m = Int(timeInterval / 60) % 60 let s = Int(timeInterval) % 60

return "\(d) days, \(h) hours, \(m) minutes, and \(s) seconds"}

Page 14: Standing the Test of Time: The Date Provider Pattern

WE CAN EASILY OUTPUT THE REMAINING TIMElet timeIntervalUntilDeparture = tripHome.timeRemainingUntilDeparture()

let formattedRemainingTime = formatTimeInterval( timeInterval: timeIntervalUntilDeparture)

print("Time until departure: " + formattedRemainingTime)

TIME UNTIL DEPARTURE: 25 DAYS, 19 HOURS,45 MINUTES, AND 50 SECONDS

Page 15: Standing the Test of Time: The Date Provider Pattern

!

Page 16: Standing the Test of Time: The Date Provider Pattern

!HMMM....

Page 17: Standing the Test of Time: The Date Provider Pattern

!MAYBE WE SHOULD WRITE A TEST FOR THIS

Page 18: Standing the Test of Time: The Date Provider Pattern

func testTimeRemainingUntilDeparture_returnsCorrectTimeInterval() {

}

Page 19: Standing the Test of Time: The Date Provider Pattern

func testTimeRemainingUntilDeparture_returnsCorrectTimeInterval() { let tripHome = Flight( flightNumber: "JAL925", departureCityCode: "HND", departureDateTime: Date(timeIntervalSince1970: 1493115120), arrivalCityCode: "OKA", arrivalDateTime: Date(timeIntervalSince1970: 1493125920) )

}

Page 20: Standing the Test of Time: The Date Provider Pattern

func testTimeRemainingUntilDeparture_returnsCorrectTimeInterval() { let tripHome = Flight( flightNumber: "JAL925", departureCityCode: "HND", departureDateTime: Date(timeIntervalSince1970: 1493115120), arrivalCityCode: "OKA", arrivalDateTime: Date(timeIntervalSince1970: 1493125920) )

let remainingTime = tripHome.timeRemainingUntilDeparture()

}

Page 21: Standing the Test of Time: The Date Provider Pattern

func testTimeRemainingUntilDeparture_returnsCorrectTimeInterval() { let tripHome = Flight( flightNumber: "JAL925", departureCityCode: "HND", departureDateTime: Date(timeIntervalSince1970: 1493115120), arrivalCityCode: "OKA", arrivalDateTime: Date(timeIntervalSince1970: 1493125920) )

let remainingTime = tripHome.timeRemainingUntilDeparture()

XCTAssertEqual(Int(remainingTime), ???)}

Page 22: Standing the Test of Time: The Date Provider Pattern

func testTimeRemainingUntilDeparture_returnsCorrectTimeInterval() { let tripHome = Flight( flightNumber: "JAL925", departureCityCode: "HND", departureDateTime: Date(timeIntervalSince1970: 1493115120), arrivalCityCode: "OKA", arrivalDateTime: Date(timeIntervalSince1970: 1493125920) )

let remainingTime = tripHome.timeRemainingUntilDeparture()

XCTAssertEqual(Int(remainingTime), 2144133)}

Page 23: Standing the Test of Time: The Date Provider Pattern

WE RUN THE TEST AND...

Page 24: Standing the Test of Time: The Date Provider Pattern
Page 25: Standing the Test of Time: The Date Provider Pattern

HMM.

Page 26: Standing the Test of Time: The Date Provider Pattern

WELL... LETS UPDATE THE TEST THEN

Page 27: Standing the Test of Time: The Date Provider Pattern

func testTimeRemainingUntilDeparture_returnsCorrectTimeInterval() { let tripHome = Flight( flightNumber: "JAL925", departureCityCode: "HND", departureDateTime: Date(timeIntervalSince1970: 1493115120), arrivalCityCode: "OKA", arrivalDateTime: Date(timeIntervalSince1970: 1493125920) )

let remainingTime = tripHome.timeRemainingUntilDeparture()

XCTAssertEqual(Int(remainingTime), 2144113)}

Page 28: Standing the Test of Time: The Date Provider Pattern
Page 29: Standing the Test of Time: The Date Provider Pattern

!

Page 30: Standing the Test of Time: The Date Provider Pattern

WE NEED MORE CONTROL

Page 31: Standing the Test of Time: The Date Provider Pattern

HERE'S OUR CODE:func timeRemainingUntilDeparture() -> TimeInterval { let departureTimeInterval = departureDateTime.timeIntervalSinceNow

let currentTimeInterval = Date().timeIntervalSinceNow

return departureTimeInterval - currentTimeInterval}

Page 32: Standing the Test of Time: The Date Provider Pattern

WHAT IS IT THAT WE NEED MORE CONTROL OVER?func timeRemainingUntilDeparture() -> TimeInterval { let departureTimeInterval = departureDateTime.timeIntervalSinceNow

let currentTimeInterval = Date().timeIntervalSinceNow

return departureTimeInterval - currentTimeInterval}

Page 33: Standing the Test of Time: The Date Provider Pattern

THE CURRENT TIMElet currentTimeInterval = Date().timeIntervalSinceNow

Page 34: Standing the Test of Time: The Date Provider Pattern

THEREFORE, THE CURRENT TIME IS OUR DEPENDENCY

Page 35: Standing the Test of Time: The Date Provider Pattern

SO HOW CAN WE SOLVE THIS PROBLEM?

Page 36: Standing the Test of Time: The Date Provider Pattern

USING A DATE PROVIDER

Page 37: Standing the Test of Time: The Date Provider Pattern

DATE PROVIDER:AN OBJECT THAT WE CAN ASK TO GIVE US

THE CURRENT DATE & TIME

Page 38: Standing the Test of Time: The Date Provider Pattern

BACK TO OUR TEST THEN...

Page 39: Standing the Test of Time: The Date Provider Pattern

FIRST WE NEED TO PASS OUR DEPENDENCY INlet remainingTime = tripHome.timeRemainingUntilDeparture()

↓let remainingTime = tripHome.timeRemainingUntilDeparture( dateProvider: ???)

Page 40: Standing the Test of Time: The Date Provider Pattern

THIS CHANGES OUR METHOD'S DECLARATIONfunc timeRemainingUntilDeparture() -> TimeInterval

↓func timeRemainingUntilDeparture(dateProvider: DateProvider) -> TimeInterval

Page 41: Standing the Test of Time: The Date Provider Pattern

WHICH MEANS WE'LL NEED A PROTOCOLprotocol DateProvider {

}

Page 42: Standing the Test of Time: The Date Provider Pattern

... AND A WAY TO GET THE CURRENT DATE/TIMEprotocol DateProvider { func currentDateTime() -> Date}

Page 43: Standing the Test of Time: The Date Provider Pattern

WE'LL CREATE A FAKE OBJECT FOR TESTINGprotocol DateProvider { func currentDateTime() -> Date}

class FakeDateProvider: DateProvider {

func currentDateTime() -> Date {

}}

Page 44: Standing the Test of Time: The Date Provider Pattern

WHAT SHOULD THIS FUNCTION RETURN?protocol DateProvider { func currentDateTime() -> Date}

class FakeDateProvider: DateProvider {

func currentDateTime() -> Date { return ??? }}

Page 45: Standing the Test of Time: The Date Provider Pattern

METHOD NAME + "_RETURNVALUE"protocol DateProvider { func currentDateTime() -> Date}

class FakeDateProvider: DateProvider {

func currentDateTime() -> Date { return currentDateTime_returnValue }}

Page 46: Standing the Test of Time: The Date Provider Pattern

WHICH WE CAN DEFINE AND INITIALIZEprotocol DateProvider { func currentDateTime() -> Date}

class FakeDateProvider: DateProvider { var currentDateTime_returnValue = Date()

func currentDateTime() -> Date { return currentDateTime_returnValue }}

Page 47: Standing the Test of Time: The Date Provider Pattern

BACK TO OUR TEST... WE NEED TO PASS IN OUR FAKElet remainingTime = tripHome.timeRemainingUntilDeparture( dateProvider: ???)

Page 48: Standing the Test of Time: The Date Provider Pattern

BACK TO OUR TEST... WE NEED TO PASS IN OUR FAKElet remainingTime = tripHome.timeRemainingUntilDeparture( dateProvider: ???)

↓let fakeDateProvider = FakeDateProvider()let remainingTime = tripHome.timeRemainingUntilDeparture( dateProvider: fakeDateProvider)

Page 49: Standing the Test of Time: The Date Provider Pattern

NEXT IS THE MOST IMPORTANT STEP

Page 50: Standing the Test of Time: The Date Provider Pattern

SET THE VALUE YOU WANT THE FAKE TO RETURNfakeDateProvider.currentDateTime_returnValue = Date( timeIntervalSince1970: 1493028720 // 4-24-2017 19:12)

Page 51: Standing the Test of Time: The Date Provider Pattern

AND NOW WE CAN ADJUST OUR EXPECTATIONXCTAssertEqual(Int(remainingTime), 86400) // 24 Hours Prior

Page 52: Standing the Test of Time: The Date Provider Pattern

OUR UPDATED TEST:

Page 53: Standing the Test of Time: The Date Provider Pattern

func testTimeRemainingUntilDeparture_returnsCorrectTimeInterval() { let tripHome = Flight( flightNumber: "JAL925", departureCityCode: "HND", departureDateTime: Date(timeIntervalSince1970: 1493115120), arrivalCityCode: "OKA", arrivalDateTime: Date(timeIntervalSince1970: 1493125920) )

let fakeDateProvider = FakeDateProvider() fakeDateProvider.currentDateTime_returnValue = Date(timeIntervalSince1970: 1493028720)

let remainingTime = tripHome.timeRemainingUntilDeparture(dateProvider: fakeDateProvider)

XCTAssertEqual(Int(remainingTime), 86400)}

Page 54: Standing the Test of Time: The Date Provider Pattern

WHAT ABOUT THE ACTUAL IMPLEMENTATION?

Page 55: Standing the Test of Time: The Date Provider Pattern

THE DEFAULT IMPLEMENTATION IS EASYprotocol DateProvider { func currentDateTime() -> Date}

Page 56: Standing the Test of Time: The Date Provider Pattern

JUST RETURN THE CURRENT DATE & TIMEprotocol DateProvider { func currentDateTime() -> Date}

struct DefaultDateProvider: DateProvider { func currentDateTime() -> Date { return Date() }}

Page 57: Standing the Test of Time: The Date Provider Pattern

AND PASS IN THE DEFAULT TO YOUR ACTUAL CALL SITElet timeIntervalUntilDeparture = tripHome.timeRemainingUntilDeparture( dateProvider: DefaultDateProvider())

let formattedRemainingTime = formatTimeInterval( timeInterval: timeIntervalUntilDeparture)

print("Time until departure: " + formattedRemainingTime + " (" + String(timeIntervalUntilDeparture) + ")")

Page 58: Standing the Test of Time: The Date Provider Pattern

!

Page 59: Standing the Test of Time: The Date Provider Pattern

...

Page 60: Standing the Test of Time: The Date Provider Pattern

THIS IS ACTUALLY DEPENDENCY INJECTION

Page 61: Standing the Test of Time: The Date Provider Pattern

SUMMARY

▸ Identified our dependency (current date/time)▸ Created a protocol for it▸ Extracted it from our method

▸ Passed in the actual value at runtime▸ Created a fake we could use for testing

Page 62: Standing the Test of Time: The Date Provider Pattern

THANK YOU!@DEREKLEEROCK

GITHUB.COM/DEREKLEEROCK

Page 63: Standing the Test of Time: The Date Provider Pattern

Q&A: WHAT DO YOU USE FOR GETTING EPOCH DATES AND TIMES?

HTTPS://WWW.EPOCHCONVERTER.COM/MAC TERMINAL "DATE" COMMAND:

$ date -r 1 # Outputs Thu Jan 1 09:00:01 JST 1970

$ date -r 1493125920 # Outputs Tue Apr 25 22:12:00 JST 2017

$ date +%s # Outputs current date/time in # of seconds

Page 64: Standing the Test of Time: The Date Provider Pattern

SOURCE CODE IS AVAILABLE ON GITHUB:

https://github.com/derekleerock/StandingTheTestOfTime