Commit 8ca47e11 authored by Paul Bird's avatar Paul Bird

Initial commit

parent c2bec83e
......@@ -13,6 +13,12 @@
DADF48BF1F30D5890025EDA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DADF48BE1F30D5890025EDA3 /* Assets.xcassets */; };
DADF48C21F30D5890025EDA3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DADF48C01F30D5890025EDA3 /* LaunchScreen.storyboard */; };
DADF48CD1F30D5890025EDA3 /* operateDocsExample1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF48CC1F30D5890025EDA3 /* operateDocsExample1Tests.swift */; };
DADF48D81F30D6150025EDA3 /* operateAPIUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF48D71F30D6150025EDA3 /* operateAPIUtilities.swift */; };
DADF48DA1F30D64E0025EDA3 /* DateExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF48D91F30D64E0025EDA3 /* DateExtensions.swift */; };
DADF48DC1F30D7200025EDA3 /* UserDefaultsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF48DB1F30D7200025EDA3 /* UserDefaultsHelper.swift */; };
DADF48DE1F30D7730025EDA3 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF48DD1F30D7730025EDA3 /* SwiftyJSON.swift */; };
DADF48E21F30D83C0025EDA3 /* Model in Resources */ = {isa = PBXBuildFile; fileRef = DADF48E11F30D83C0025EDA3 /* Model */; };
DADF48F01F30DAAE0025EDA3 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF48EF1F30DAAE0025EDA3 /* StringExtensions.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
......@@ -36,6 +42,12 @@
DADF48C81F30D5890025EDA3 /* operateDocsExample1Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = operateDocsExample1Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DADF48CC1F30D5890025EDA3 /* operateDocsExample1Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = operateDocsExample1Tests.swift; sourceTree = "<group>"; };
DADF48CE1F30D5890025EDA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DADF48D71F30D6150025EDA3 /* operateAPIUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = operateAPIUtilities.swift; sourceTree = "<group>"; };
DADF48D91F30D64E0025EDA3 /* DateExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateExtensions.swift; sourceTree = "<group>"; };
DADF48DB1F30D7200025EDA3 /* UserDefaultsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsHelper.swift; sourceTree = "<group>"; };
DADF48DD1F30D7730025EDA3 /* SwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyJSON.swift; sourceTree = "<group>"; };
DADF48E11F30D83C0025EDA3 /* Model */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Model; sourceTree = "<group>"; };
DADF48EF1F30DAAE0025EDA3 /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -77,12 +89,18 @@
DADF48B61F30D5890025EDA3 /* operateDocsExample1 */ = {
isa = PBXGroup;
children = (
DADF48F21F30DAF50025EDA3 /* Extensions */,
DADF48F11F30DAEA0025EDA3 /* Entities */,
DADF48E11F30D83C0025EDA3 /* Model */,
DADF48B71F30D5890025EDA3 /* AppDelegate.swift */,
DADF48B91F30D5890025EDA3 /* ViewController.swift */,
DADF48BB1F30D5890025EDA3 /* Main.storyboard */,
DADF48BE1F30D5890025EDA3 /* Assets.xcassets */,
DADF48C01F30D5890025EDA3 /* LaunchScreen.storyboard */,
DADF48C31F30D5890025EDA3 /* Info.plist */,
DADF48D71F30D6150025EDA3 /* operateAPIUtilities.swift */,
DADF48DB1F30D7200025EDA3 /* UserDefaultsHelper.swift */,
DADF48DD1F30D7730025EDA3 /* SwiftyJSON.swift */,
);
path = operateDocsExample1;
sourceTree = "<group>";
......@@ -96,6 +114,22 @@
path = operateDocsExample1Tests;
sourceTree = "<group>";
};
DADF48F11F30DAEA0025EDA3 /* Entities */ = {
isa = PBXGroup;
children = (
);
name = Entities;
sourceTree = "<group>";
};
DADF48F21F30DAF50025EDA3 /* Extensions */ = {
isa = PBXGroup;
children = (
DADF48D91F30D64E0025EDA3 /* DateExtensions.swift */,
DADF48EF1F30DAAE0025EDA3 /* StringExtensions.swift */,
);
name = Extensions;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
......@@ -146,8 +180,7 @@
TargetAttributes = {
DADF48B31F30D5890025EDA3 = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = Y8AYMBFRB7;
ProvisioningStyle = Automatic;
ProvisioningStyle = Manual;
};
DADF48C71F30D5890025EDA3 = {
CreatedOnToolsVersion = 8.3.3;
......@@ -182,6 +215,7 @@
files = (
DADF48C21F30D5890025EDA3 /* LaunchScreen.storyboard in Resources */,
DADF48BF1F30D5890025EDA3 /* Assets.xcassets in Resources */,
DADF48E21F30D83C0025EDA3 /* Model in Resources */,
DADF48BD1F30D5890025EDA3 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
......@@ -200,8 +234,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DADF48DC1F30D7200025EDA3 /* UserDefaultsHelper.swift in Sources */,
DADF48BA1F30D5890025EDA3 /* ViewController.swift in Sources */,
DADF48DA1F30D64E0025EDA3 /* DateExtensions.swift in Sources */,
DADF48DE1F30D7730025EDA3 /* SwiftyJSON.swift in Sources */,
DADF48F01F30DAAE0025EDA3 /* StringExtensions.swift in Sources */,
DADF48B81F30D5890025EDA3 /* AppDelegate.swift in Sources */,
DADF48D81F30D6150025EDA3 /* operateAPIUtilities.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -342,11 +381,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = Y8AYMBFRB7;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = operateDocsExample1/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = tech.essensys.operateDocsExample1;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 3.0;
};
name = Debug;
......@@ -355,11 +395,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = Y8AYMBFRB7;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = operateDocsExample1/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = tech.essensys.operateDocsExample1;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 3.0;
};
name = Release;
......@@ -411,6 +452,7 @@
DADF48D31F30D5890025EDA3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DADF48D41F30D5890025EDA3 /* Build configuration list for PBXNativeTarget "operateDocsExample1Tests" */ = {
isa = XCConfigurationList;
......@@ -419,6 +461,7 @@
DADF48D61F30D5890025EDA3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
......
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
......@@ -30,6 +40,16 @@
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
......@@ -59,6 +79,11 @@
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
......
//
// DateExtensions.swift
// operateDocsExample1
//
// Created by Paul Bird on 01/08/2017.
// Copyright © 2017 Paul Bird. All rights reserved.
//
import Foundation
extension Date {
//MARK:- Properties
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
var endOfDay: Date? {
var components = DateComponents()
components.day = 1
components.second = -1
return (Calendar.current as NSCalendar).date(byAdding: components, to: startOfDay, options: NSCalendar.Options())
}
//MARK:- Functions
func iso8601formatted() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSX"
return dateFormatter.string(from: self)
}
func string(_ format: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
return dateFormatter.string(from: self)
}
func day() -> Int {
let calendar = Calendar.current
let components = (calendar as NSCalendar).components(.day, from: self)
let day = components.day
return day!
}
func dayShortString() -> String {
let dayFormatter = DateFormatter()
dayFormatter.dateFormat = "EEE"
return dayFormatter.string(from: self)
}
func hour() -> Int {
let calendar = Calendar.current
let components = (calendar as NSCalendar).components(.hour, from: self)
let hour = components.hour
return hour!
}
func minute() -> Int {
let calendar = Calendar.current
let components = (calendar as NSCalendar).components(.minute, from: self)
let minute = components.minute
return minute!
}
func minutesFrom(_ date: Date) -> Int{
return (Calendar.current as NSCalendar).components(
.minute,
from: date,
to: self,
options: []).minute!
}
func month() -> Int {
let calendar = Calendar.current
let components = (calendar as NSCalendar).components(.month, from: self)
let month = components.month
return month!
}
func monthShortString() -> String {
let monthFormatter = DateFormatter()
monthFormatter.dateFormat = "MMM"
return monthFormatter.string(from: self)
}
func second() -> Int {
let calendar = Calendar.current
let components = (calendar as NSCalendar).components(.second, from: self)
let second = components.second
return second!
}
func secondsFrom(_ date: Date) -> Int{
return (Calendar.current as NSCalendar).components(
.second,
from: date,
to: self,
options: []).second!
}
}
//
// Location.swift
// MRoomDemo1
//
// Created by Paul Bird on 22/09/2016.
// Copyright © 2016 essensys. All rights reserved.
//
import Foundation
struct Location {
var locationId: String
var locationName: String
var meetingRooms: [(productId: String, roomName: String)]
}
//
// MeetingRoom.swift
// MRoomDemo1
//
// Created by Paul Bird on 20/09/2016.
// Copyright © 2016 essensys. All rights reserved.
//
import Foundation
struct MeetingRoom {
var locationId: String
var productId: String
var roomName: String
init() {
locationId = ""
productId = ""
roomName = ""
}
}
//
// SettingsBundle.swift
// MRoomDemo1
//
// Created by Paul Bird on 04/10/2016.
// Copyright © 2016 essensys. All rights reserved.
//
import Foundation
class SettingsBundle {
static let defaults = Foundation.UserDefaults.standard
class func autoLoginStatus() -> Bool {
let status = defaults.bool(forKey: "autoLoginSwitch_preference")
return status
}
class func password() -> String {
let username = defaults.string(forKey: "password_preference") ?? ""
return username
}
class func username() -> String {
let username = defaults.string(forKey: "username_preference") ?? ""
return username
}
class func setDefaults() {
if defaults.string(forKey: "autoLoginSwitch_preference") == nil {
defaults.set(true, forKey: "autoLoginSwitch_preference")
}
if defaults.string(forKey: "password_preference") == nil {
defaults.set("", forKey: "password_preference")
}
if defaults.string(forKey: "username_preference") == nil {
defaults.set("", forKey: "username_preference")
}
defaults.synchronize()
}
class func setPassword(_ password: String) {
defaults.set(password, forKey: "password_preference")
defaults.synchronize()
}
class func setUsername(_ username: String) {
defaults.set(username, forKey: "username_preference")
defaults.synchronize()
}
class func setAutoLoginSwitch(_ autoLoginSwitch: Bool) {
defaults.set(autoLoginSwitch, forKey: "autoLoginSwitch_preference")
defaults.synchronize()
}
}
//
// TimeSegments.swift
// MRoomDemo1
//
// Created by Paul Bird on 20/09/2016.
// Copyright © 2016 essensys. All rights reserved.
//
import Foundation
struct TimeSegments {
var timeSegmentSizeMinutes: Int
var segments: [TimeSegment] = []
var managedHoursStart: Int
var managedHoursFinished: Int
init(timeSegmentSizeMinutes: Int, managedHoursStart: Int, managedHoursFinished: Int) {
segments = []
self.timeSegmentSizeMinutes = timeSegmentSizeMinutes
self.managedHoursStart = managedHoursStart
self.managedHoursFinished = managedHoursFinished
self.populateSegmentsForManagedHours()
}
func currentTimeSegmentIndex() -> Int? {
let segmentIndex = (hoursSinceStart() * timeSegmentsPerHour()) + minuteSegmentsSinceStart()
if segmentIndex >= 0 && segmentIndex < (numberOfManagedHours() * timeSegmentsPerHour()) {
return segmentIndex
} else {
return nil
}
}
fileprivate func hoursSinceStart() -> Int {
let currentHour = Date().hour()
return currentHour - managedHoursStart
}
fileprivate func lastSegmentIndex() -> Int {
return segments.count - 1
}
fileprivate func minuteSegmentsSinceStart() -> Int {
let currentMinute = Date().minute()
return currentMinute / timeSegmentSizeMinutes
}
func numberOfManagedHours() -> Int {
return managedHoursFinished - managedHoursStart
}
func numberOfManagedMinutes() -> Int {
return 60 * (managedHoursFinished - managedHoursStart)
}
fileprivate mutating func populateSegmentsForManagedHours() {
let numberOfManagedHours = managedHoursFinished - managedHoursStart
let segmentsPerHour = 60 / timeSegmentSizeMinutes
let numberOfSegments = numberOfManagedHours * segmentsPerHour
for _ in 1...numberOfSegments {
segments += [TimeSegment(occupied: false)]
}
}
func indexForNextFreeSegment() -> Int? {
guard let ctsi = currentTimeSegmentIndex() else { return nil }
let startSequenceIndex = ctsi + 1
for segment in startSequenceIndex...lastSegmentIndex() {
if segments[segment].occupied == false {
return segment
}
}
return nil
}
func indexForNextOccupiedSegment() -> Int? {
guard let ctsi = currentTimeSegmentIndex() else { return nil }
let startSequenceIndex = ctsi + 1
guard lastSegmentIndex() > startSequenceIndex else { return nil }
for segment in startSequenceIndex...lastSegmentIndex() {
if segments[segment].occupied == true {
return segment
}
}
return nil
}
func indexForTime(hour: Int, minute: Int) -> Int? {
let hoursSinceStart = hour - managedHoursStart
let minuteSegments = minute / timeSegmentSizeMinutes
let segmentIndex = (hoursSinceStart * timeSegmentsPerHour()) + minuteSegments
if segmentIndex >= 0 && segmentIndex <= lastSegmentIndex() {
return segmentIndex
}
return nil
}
func indexForEndTime(hour: Int, minute: Int) -> Int? {
let hoursSinceStart = hour - managedHoursStart
let minuteSegments = minute / timeSegmentSizeMinutes
var segmentIndex = (hoursSinceStart * timeSegmentsPerHour()) + minuteSegments
//If meeting is set to end a segment we don't want to roll into the next time segment
if minute % timeSegmentSizeMinutes == 0 {
segmentIndex = segmentIndex - 1
}
if segmentIndex >= 0 && segmentIndex <= lastSegmentIndex() {
return segmentIndex
}
return nil
}
func minutesUntilNextFreeTimeSegment() -> Int? {
guard let nextFree = startTimeDateForNextFreeSegment() else { return nil }
return nextFree.minutesFrom(Date())
}
mutating func refreshFromBookings(_ bookings: [RoomBooking]) {
setAllTimeSegmentsToOccupiedFalse()
for booking in bookings {
let _ = setOccupied(fromHour: booking.startTime.hour(),
fromMin: booking.startTime.minute(),
duration: booking.durationInMinutes)
}
}
func secondsUntilNextFreeTimeSegment() -> Int? {
guard let nextFree = startTimeDateForNextFreeSegment() else { return nil }
return nextFree.secondsFrom(Date())
}
mutating func setAllTimeSegmentsToOccupiedFalse() {
for index in 0..<segments.count {
segments[index].occupied = false
}
}
mutating func setOccupied(fromHour: Int, fromMin: Int, duration: Int) -> Bool {
var toHour = 0
var toMin = 0
let hoursToAdd = Int((fromMin+duration)/60)
toHour = fromHour + hoursToAdd
toMin = (fromMin + duration) % 60
let result = setOccupied(fromHour: fromHour, fromMin: fromMin, toHour: toHour, toMin: toMin)
return result
}
mutating func setOccupied(fromHour: Int, fromMin: Int, toHour: Int, toMin: Int) -> Bool {
if fromHour >= managedHoursFinished || toHour > managedHoursFinished {
return false
}
guard let startIndex = indexForTime(hour: fromHour, minute: fromMin) else {
return false
}
guard let endIndex = indexForEndTime(hour: toHour, minute: toMin) else {
return false
}
for index in startIndex...endIndex {
segments[index].occupied = true
}
return true
}
func startTimeForIndex(_ segmentIndex: Int) -> Date {
let startDate = Date().startOfDay
let managedHoursStartMinutes = managedHoursStart * 60
let minutesToAdd = segmentIndex * timeSegmentSizeMinutes
let totalMinutesToAdd = managedHoursStartMinutes + minutesToAdd
let datetimeForIndex = startDate.addingTimeInterval(Double(totalMinutesToAdd * 60))
return datetimeForIndex
}
func startTimeDateForNextFreeSegment() -> Date? {
guard let index = indexForNextFreeSegment() else { return nil}
return startTimeForIndex(index)
}
func startTimeDateForNextBooking() -> Date? {
guard let index = indexForNextOccupiedSegment() else { return nil}
return startTimeForIndex(index)
}
fileprivate func timeSegmentsPerHour() -> Int {
return 60 / timeSegmentSizeMinutes
}
}
//
// Settings.swift
// MRoomDemo1
//
// Created by Paul Bird on 03/10/2016.
// Copyright © 2016 essensys. All rights reserved.
//
import Foundation
class UserDefaultsHelper {
static let defaults = Foundation.UserDefaults.standard
//MARK:- Defaults
class func setDefaults() {
if defaults.string(forKey: "accessToken") == nil {
setAccessToken("")
}
setLoginFailed(false)
}
//MARK:- Getters
class func accessToken() -> String {
let accessToken = defaults.string(forKey: "accessToken")
return accessToken ?? ""
}
class func calendarsUpdated() -> Bool {
let calendarsUpdated = defaults.bool(forKey: "calendarsUpdated")
return calendarsUpdated
}
class func locationsUpdated() -> Bool {
let locationsUpdated = defaults.bool(forKey: "locationsUpdated")
return locationsUpdated
}
class func loginFailed() -> Bool {
let loginFailed = defaults.bool(forKey: "loginFailed")
return loginFailed
}
class func productsUpdated() -> Bool {
let productsUpdated = defaults.bool(forKey: "productsUpdated")
return productsUpdated
}
class func refreshToken() -> String {
let refreshToken = defaults.string(forKey: "refreshToken") ?? ""
return refreshToken
}
class func username() -> String {
let username = defaults.string(forKey: "username") ?? ""
return username
}
//MARK:- Setters
class func setAccessToken(_ accessToken: String) {
defaults.set(accessToken, forKey: "accessToken")
defaults.synchronize()
}
class func setCalendarsUpdated(_ calendarsUpdated: Bool) {
defaults.set(calendarsUpdated, forKey: "calendarsUpdated")
defaults.synchronize()
}
class func setLocationsUpdated(_ locationsUpdated: Bool) {
defaults.set(locationsUpdated, forKey: "locationsUpdated")
defaults.synchronize()
}
class func setLoginFailed(_ loginFailed: Bool) {
defaults.set(loginFailed, forKey: "loginFailed")
defaults.synchronize()
}
class func setProductsUpdated(_ productsUpdated: Bool) {
defaults.set(productsUpdated, forKey: "productsUpdated")
defaults.synchronize()
}
class func setRefreshToken(_ refreshToken: String) {