From 23dd669bdfa90f7123614f612b66be1e5ffb702a Mon Sep 17 00:00:00 2001 From: Marco Filetti Date: Tue, 9 Feb 2016 14:47:43 +0200 Subject: [PATCH] More detailed calendar (work in progress) --- JustUsed.xcodeproj/project.pbxproj | 179 +-- JustUsed/DiMe Data/CalendarEvent.swift | 116 ++ JustUsed/DiMe Data/Event.swift | 5 + JustUsed/DiMe Data/HistoryManager.swift | 47 +- JustUsed/DiMe Data/Location.swift | 11 + JustUsed/DiMe Data/Person.swift | 110 ++ JustUsed/Libraries/SwiftyJSON.swift | 1352 +++++++++++++++++ .../Model}/CalendarManager.swift | 3 + JustUsed/Model/LocationSingleton.swift | 30 +- JustUsed/Utils/Utils.swift | 85 ++ 10 files changed, 1730 insertions(+), 208 deletions(-) create mode 100644 JustUsed/DiMe Data/CalendarEvent.swift create mode 100644 JustUsed/DiMe Data/Person.swift create mode 100755 JustUsed/Libraries/SwiftyJSON.swift rename {Shared => JustUsed/Model}/CalendarManager.swift (96%) diff --git a/JustUsed.xcodeproj/project.pbxproj b/JustUsed.xcodeproj/project.pbxproj index cad14ec..673a487 100644 --- a/JustUsed.xcodeproj/project.pbxproj +++ b/JustUsed.xcodeproj/project.pbxproj @@ -9,12 +9,10 @@ /* Begin PBXBuildFile section */ A914066E1C0348EF00C3C2E0 /* SpotlightDocumentTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914066D1C0348EF00C3C2E0 /* SpotlightDocumentTracker.swift */; }; A91406731C036D5500C3C2E0 /* FirefoxHistoryFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91406721C036D5500C3C2E0 /* FirefoxHistoryFetcher.swift */; }; - A9200E7A1C64B67300A6AEEB /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9200E791C64B67300A6AEEB /* Cocoa.framework */; }; - A9200E7F1C64B67300A6AEEB /* ExtensionCompanionRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9200E7E1C64B67300A6AEEB /* ExtensionCompanionRequestHandler.swift */; }; - A9200E831C64B67300A6AEEB /* fi.hiit.JustUsedCompanion.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A9200E771C64B67300A6AEEB /* fi.hiit.JustUsedCompanion.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - A9200E901C64C81000A6AEEB /* CalendarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9200E8F1C64C81000A6AEEB /* CalendarManager.swift */; }; - A9200E911C64C81000A6AEEB /* CalendarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9200E8F1C64C81000A6AEEB /* CalendarManager.swift */; }; - A9200E921C64C81000A6AEEB /* CalendarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9200E8F1C64C81000A6AEEB /* CalendarManager.swift */; }; + A92411E71C69FAC900DD6449 /* CalendarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92411E61C69FAC900DD6449 /* CalendarManager.swift */; }; + A92411EF1C69FB3F00DD6449 /* CalendarEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92411EE1C69FB3F00DD6449 /* CalendarEvent.swift */; }; + A92411F11C69FD8F00DD6449 /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92411F01C69FD8F00DD6449 /* Person.swift */; }; + A92411F31C69FDCB00DD6449 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92411F21C69FDCB00DD6449 /* SwiftyJSON.swift */; }; A92574551BCF8F98004866B4 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92574541BCF8F98004866B4 /* Utils.swift */; }; A92574591BCF8FF9004866B4 /* XCGLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92574581BCF8FF9004866B4 /* XCGLogger.swift */; }; A925745B1BCF901D004866B4 /* JustUsedConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = A925745A1BCF901D004866B4 /* JustUsedConstants.swift */; }; @@ -47,13 +45,6 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - A9200E811C64B67300A6AEEB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A9438DC71B7A3F0300F1A54B /* Project object */; - proxyType = 1; - remoteGlobalIDString = A9200E761C64B67300A6AEEB; - remoteInfo = fi.hiit.JustUsedCompanion; - }; A9438DE21B7A3F0400F1A54B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A9438DC71B7A3F0300F1A54B /* Project object */; @@ -120,17 +111,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - A9200E8D1C64B67300A6AEEB /* Embed App Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - A9200E831C64B67300A6AEEB /* fi.hiit.JustUsedCompanion.appex in Embed App Extensions */, - ); - name = "Embed App Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; A9D894231BD00EED00314475 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -147,12 +127,10 @@ /* Begin PBXFileReference section */ A914066D1C0348EF00C3C2E0 /* SpotlightDocumentTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpotlightDocumentTracker.swift; sourceTree = ""; }; A91406721C036D5500C3C2E0 /* FirefoxHistoryFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirefoxHistoryFetcher.swift; sourceTree = ""; }; - A9200E771C64B67300A6AEEB /* fi.hiit.JustUsedCompanion.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = fi.hiit.JustUsedCompanion.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - A9200E791C64B67300A6AEEB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; - A9200E7D1C64B67300A6AEEB /* fi_hiit_JustUsedCompanion.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = fi_hiit_JustUsedCompanion.entitlements; sourceTree = ""; }; - A9200E7E1C64B67300A6AEEB /* ExtensionCompanionRequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionCompanionRequestHandler.swift; sourceTree = ""; }; - A9200E801C64B67300A6AEEB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A9200E8F1C64C81000A6AEEB /* CalendarManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarManager.swift; sourceTree = ""; }; + A92411E61C69FAC900DD6449 /* CalendarManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarManager.swift; sourceTree = ""; }; + A92411EE1C69FB3F00DD6449 /* CalendarEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarEvent.swift; sourceTree = ""; }; + A92411F01C69FD8F00DD6449 /* Person.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Person.swift; sourceTree = ""; }; + A92411F21C69FDCB00DD6449 /* SwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyJSON.swift; sourceTree = ""; }; A92574541BCF8F98004866B4 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; A92574581BCF8FF9004866B4 /* XCGLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCGLogger.swift; sourceTree = ""; }; A925745A1BCF901D004866B4 /* JustUsedConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustUsedConstants.swift; sourceTree = ""; }; @@ -196,14 +174,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - A9200E741C64B67300A6AEEB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A9200E7A1C64B67300A6AEEB /* Cocoa.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; A9438DCC1B7A3F0400F1A54B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -222,43 +192,10 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - A9200E781C64B67300A6AEEB /* Frameworks */ = { - isa = PBXGroup; - children = ( - A9200E791C64B67300A6AEEB /* Cocoa.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - A9200E7B1C64B67300A6AEEB /* Companion */ = { - isa = PBXGroup; - children = ( - A9200E7E1C64B67300A6AEEB /* ExtensionCompanionRequestHandler.swift */, - A9200E801C64B67300A6AEEB /* Info.plist */, - A9200E7C1C64B67300A6AEEB /* Supporting Files */, - ); - path = Companion; - sourceTree = ""; - }; - A9200E7C1C64B67300A6AEEB /* Supporting Files */ = { - isa = PBXGroup; - children = ( - A9200E7D1C64B67300A6AEEB /* fi_hiit_JustUsedCompanion.entitlements */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - A9200E8E1C64C7DA00A6AEEB /* Shared */ = { - isa = PBXGroup; - children = ( - A9200E8F1C64C81000A6AEEB /* CalendarManager.swift */, - ); - path = Shared; - sourceTree = ""; - }; A925744C1BCF8EAA004866B4 /* Model */ = { isa = PBXGroup; children = ( + A92411E61C69FAC900DD6449 /* CalendarManager.swift */, A925745D1BCF906A004866B4 /* AppDelegate.swift */, A925745E1BCF906A004866B4 /* AppSingleton.swift */, A925745F1BCF906A004866B4 /* LocationSingleton.swift */, @@ -280,6 +217,7 @@ A92574691BCF9190004866B4 /* fmdb */, A9D4A2D41B7B9B3400EC1D68 /* libsqlite3.dylib */, A92574581BCF8FF9004866B4 /* XCGLogger.swift */, + A92411F21C69FDCB00DD6449 /* SwiftyJSON.swift */, ); name = Libraries; path = JustUsed/Libraries; @@ -317,12 +255,9 @@ A9438DC61B7A3F0300F1A54B = { isa = PBXGroup; children = ( - A9200E8E1C64C7DA00A6AEEB /* Shared */, A9F507721BC7B8DE00275C6C /* Alamofire.xcodeproj */, A9438DD11B7A3F0400F1A54B /* JustUsed */, A9438DE41B7A3F0400F1A54B /* JustUsedTests */, - A9200E7B1C64B67300A6AEEB /* Companion */, - A9200E781C64B67300A6AEEB /* Frameworks */, A9438DD01B7A3F0400F1A54B /* Products */, ); sourceTree = ""; @@ -332,7 +267,6 @@ children = ( A9438DCF1B7A3F0400F1A54B /* JustUsed.app */, A9438DE11B7A3F0400F1A54B /* JustUsedTests.xctest */, - A9200E771C64B67300A6AEEB /* fi.hiit.JustUsedCompanion.appex */, ); name = Products; sourceTree = ""; @@ -379,7 +313,9 @@ A9F5076C1BC7B6F000275C6C /* DiMe Data */ = { isa = PBXGroup; children = ( + A92411F01C69FD8F00DD6449 /* Person.swift */, A9F5078A1BC7BF1300275C6C /* HistoryManager.swift */, + A92411EE1C69FB3F00DD6449 /* CalendarEvent.swift */, A9F5078C1BC7BF5900275C6C /* DesktopEvent.swift */, A9F5078D1BC7BF5900275C6C /* DiMeData.swift */, A9F5078E1BC7BF5900275C6C /* DocumentInformationElement.swift */, @@ -418,23 +354,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - A9200E761C64B67300A6AEEB /* fi.hiit.JustUsedCompanion */ = { - isa = PBXNativeTarget; - buildConfigurationList = A9200E8C1C64B67300A6AEEB /* Build configuration list for PBXNativeTarget "fi.hiit.JustUsedCompanion" */; - buildPhases = ( - A9200E731C64B67300A6AEEB /* Sources */, - A9200E741C64B67300A6AEEB /* Frameworks */, - A9200E751C64B67300A6AEEB /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = fi.hiit.JustUsedCompanion; - productName = fi.hiit.JustUsedCompanion; - productReference = A9200E771C64B67300A6AEEB /* fi.hiit.JustUsedCompanion.appex */; - productType = "com.apple.product-type.app-extension"; - }; A9438DCE1B7A3F0400F1A54B /* JustUsed */ = { isa = PBXNativeTarget; buildConfigurationList = A9438DEB1B7A3F0400F1A54B /* Build configuration list for PBXNativeTarget "JustUsed" */; @@ -443,13 +362,11 @@ A9438DCC1B7A3F0400F1A54B /* Frameworks */, A9438DCD1B7A3F0400F1A54B /* Resources */, A9D894231BD00EED00314475 /* Embed Frameworks */, - A9200E8D1C64B67300A6AEEB /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( A9D894221BD00EED00314475 /* PBXTargetDependency */, - A9200E821C64B67300A6AEEB /* PBXTargetDependency */, ); name = JustUsed; productName = JustUsed; @@ -481,14 +398,10 @@ isa = PBXProject; attributes = { LastSwiftMigration = 0700; - LastSwiftUpdateCheck = 0720; + LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0700; ORGANIZATIONNAME = HIIT; TargetAttributes = { - A9200E761C64B67300A6AEEB = { - CreatedOnToolsVersion = 7.2.1; - DevelopmentTeam = JYV3R5X7JQ; - }; A9438DCE1B7A3F0400F1A54B = { CreatedOnToolsVersion = 6.4; DevelopmentTeam = JYV3R5X7JQ; @@ -520,7 +433,6 @@ targets = ( A9438DCE1B7A3F0400F1A54B /* JustUsed */, A9438DE01B7A3F0400F1A54B /* JustUsedTests */, - A9200E761C64B67300A6AEEB /* fi.hiit.JustUsedCompanion */, ); }; /* End PBXProject section */ @@ -578,13 +490,6 @@ /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ - A9200E751C64B67300A6AEEB /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; A9438DCD1B7A3F0400F1A54B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -604,15 +509,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - A9200E731C64B67300A6AEEB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A9200E7F1C64B67300A6AEEB /* ExtensionCompanionRequestHandler.swift in Sources */, - A9200E921C64C81000A6AEEB /* CalendarManager.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; A9438DCB1B7A3F0400F1A54B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -620,16 +516,19 @@ A92574621BCF906A004866B4 /* AppDelegate.swift in Sources */, A92574631BCF906A004866B4 /* AppSingleton.swift in Sources */, A9F507921BC7BF5900275C6C /* DocumentInformationElement.swift in Sources */, + A92411F31C69FDCB00DD6449 /* SwiftyJSON.swift in Sources */, A92574761BCF9190004866B4 /* FMDatabaseAdditions.m in Sources */, A9F507881BC7BA4B00275C6C /* DiMePreferencesViewController.swift in Sources */, A9F507901BC7BF5900275C6C /* DesktopEvent.swift in Sources */, + A92411EF1C69FB3F00DD6449 /* CalendarEvent.swift in Sources */, A9F5078B1BC7BF1300275C6C /* HistoryManager.swift in Sources */, A92574651BCF906A004866B4 /* SafariHistoryFetcher.swift in Sources */, + A92411F11C69FD8F00DD6449 /* Person.swift in Sources */, A92574781BCF9190004866B4 /* FMDatabaseQueue.m in Sources */, A9F507891BC7BA4B00275C6C /* ViewController.swift in Sources */, A914066E1C0348EF00C3C2E0 /* SpotlightDocumentTracker.swift in Sources */, A9F3807B1C199E38009251AB /* GenericBrowserHistory.swift in Sources */, - A9200E901C64C81000A6AEEB /* CalendarManager.swift in Sources */, + A92411E71C69FAC900DD6449 /* CalendarManager.swift in Sources */, A92574591BCF8FF9004866B4 /* XCGLogger.swift in Sources */, A91406731C036D5500C3C2E0 /* FirefoxHistoryFetcher.swift in Sources */, A92574641BCF906A004866B4 /* LocationSingleton.swift in Sources */, @@ -652,18 +551,12 @@ buildActionMask = 2147483647; files = ( A9438DE81B7A3F0400F1A54B /* JustUsedTests.swift in Sources */, - A9200E911C64C81000A6AEEB /* CalendarManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - A9200E821C64B67300A6AEEB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = A9200E761C64B67300A6AEEB /* fi.hiit.JustUsedCompanion */; - targetProxy = A9200E811C64B67300A6AEEB /* PBXContainerItemProxy */; - }; A9438DE31B7A3F0400F1A54B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A9438DCE1B7A3F0400F1A54B /* JustUsed */; @@ -689,36 +582,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - A9200E841C64B67300A6AEEB /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = fi.hiit.JustUsedCompanion/fi_hiit_JustUsedCompanion.entitlements; - CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; - INFOPLIST_FILE = fi.hiit.JustUsedCompanion/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = "fi.hiit.JustUsed.fi-hiit-JustUsedCompanion"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Debug; - }; - A9200E851C64B67300A6AEEB /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = fi.hiit.JustUsedCompanion/fi_hiit_JustUsedCompanion.entitlements; - CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; - INFOPLIST_FILE = fi.hiit.JustUsedCompanion/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = "fi.hiit.JustUsed.fi-hiit-JustUsedCompanion"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Release; - }; A9438DE91B7A3F0400F1A54B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -877,14 +740,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - A9200E8C1C64B67300A6AEEB /* Build configuration list for PBXNativeTarget "fi.hiit.JustUsedCompanion" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A9200E841C64B67300A6AEEB /* Debug */, - A9200E851C64B67300A6AEEB /* Release */, - ); - defaultConfigurationIsVisible = 0; - }; A9438DCA1B7A3F0300F1A54B /* Build configuration list for PBXProject "JustUsed" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/JustUsed/DiMe Data/CalendarEvent.swift b/JustUsed/DiMe Data/CalendarEvent.swift new file mode 100644 index 0000000..2c77921 --- /dev/null +++ b/JustUsed/DiMe Data/CalendarEvent.swift @@ -0,0 +1,116 @@ +// +// Copyright (c) 2015 Aalto University +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +import Foundation +import EventKit + +/// Represents an calendar event, as understood by dime +class CalendarEvent: Event { + + private(set) var participants: [Person]? + private(set) var name: String + private(set) var calendar: String + private(set) var location: Location? + private(set) var locString: String? + let id: String + + init(fromEKEvent event: EKEvent) { + + self.id = event.eventIdentifier + self.name = event.title + self.calendar = event.calendar.title + if #available(OSX 10.11, *) { + if let structLoc = event.structuredLocation, clloc = structLoc.geoLocation { + location = Location(fromCLLocation: clloc) + } + } + self.locString = event.location + + super.init() + + setStart(event.startDate) + setEnd(event.endDate) + + } + + init(fromJSON json: JSON) { + + self.id = json["appId"].stringValue + self.name = json["name"].stringValue + self.calendar = json["calendar"].stringValue + self.locString = json["locString"].string + + if let participants = json["participants"].array { + if participants.count > 0 { + self.participants = [Person]() + for participant in participants { + self.participants!.append(Person(fromJson: participant)) + } + } + } + + if let _ = json["location"].dictionary { + location = Location(fromJSON: json["location"]) + } + + super.init() + + let start = NSDate(timeIntervalSince1970: NSTimeInterval(json["start"].intValue / 1000)) + let end = NSDate(timeIntervalSince1970: NSTimeInterval(json["end"].intValue / 1000)) + setStart(start) + setEnd(end) + + } + + /// getDict for calendar is overridden to update return value with internal state. + override func getDict() -> [String : AnyObject] { + var retDict = theDictionary // fetch current values + + // update values + retDict["calendar"] = calendar + retDict["name"] = name + if let participants = participants { + var partArray = [[String: AnyObject]]() + for participant in participants { + partArray.append(participant.getDict()) + } + retDict["participants"] = partArray + } + + if let loc = location { + retDict["location"] = loc.getDict() + } + if let ls = locString { + retDict["locString"] = ls + } + + // required + retDict["appId"] = id + // re-define inherited fields + retDict["@type"] = "CalendarEvent" + retDict["type"] = "http://www.hiit.fi/ontologies/dime/#CalendarEvent" + + return retDict + } +} \ No newline at end of file diff --git a/JustUsed/DiMe Data/Event.swift b/JustUsed/DiMe Data/Event.swift index a298612..d517d2f 100644 --- a/JustUsed/DiMe Data/Event.swift +++ b/JustUsed/DiMe Data/Event.swift @@ -47,4 +47,9 @@ class Event: DiMeBase { func setEnd(endDate: NSDate) { theDictionary["end"] = JustUsedConstants.diMeDateFormatter.stringFromDate(endDate) } + + /// Set a start date for this item (updates old value) + func setStart(endDate: NSDate) { + theDictionary["start"] = JustUsedConstants.diMeDateFormatter.stringFromDate(endDate) + } } \ No newline at end of file diff --git a/JustUsed/DiMe Data/HistoryManager.swift b/JustUsed/DiMe Data/HistoryManager.swift index 8060195..e9fabb3 100644 --- a/JustUsed/DiMe Data/HistoryManager.swift +++ b/JustUsed/DiMe Data/HistoryManager.swift @@ -30,7 +30,7 @@ import Foundation import Alamofire -class HistoryManager: NSObject, RecentDocumentUpdateDelegate, BrowserHistoryUpdateDelegate { +class HistoryManager: NSObject { /// Returns a shared instance of this class. This is the designed way of accessing the history manager. static let sharedManager = HistoryManager() @@ -91,25 +91,6 @@ class HistoryManager: NSObject, RecentDocumentUpdateDelegate, BrowserHistoryUpda self.dimeConnectState(false) } - // MARK: - Protocol implementation - - func newHistoryItems(newURLs: [BrowserHistItem]) { - for newURL in newURLs { - let sendingToBrowser = NSUserDefaults.standardUserDefaults().valueForKey(JustUsedConstants.prefSendSafariHistory) as! Bool - if !newURL.excludedFromDiMe && sendingToBrowser { - let infoElem = DocumentInformationElement(fromSafariHist: newURL) - let event = DesktopEvent(infoElem: infoElem, ofType: TrackingType.Browser(newURL.browser), withDate: newURL.date, andLocation: newURL.location) - sendToDiMe(event) - } - } - } - - func newRecentDocument(newItem: RecentDocItem) { - let infoElem = DocumentInformationElement(fromRecentDoc: newItem) - let event = DesktopEvent(infoElem: infoElem, ofType: TrackingType.Spotlight, withDate: newItem.lastAccessDate, andLocation: newItem.location) - sendToDiMe(event) - } - // MARK: - Internal functions /// Connection to dime successful / failed @@ -169,3 +150,29 @@ class HistoryManager: NSObject, RecentDocumentUpdateDelegate, BrowserHistoryUpda } } + +// MARK: - Protocol implementations + +/// Protocol implementations for browser and document history updates +extension HistoryManager: RecentDocumentUpdateDelegate, BrowserHistoryUpdateDelegate { + + func newHistoryItems(newURLs: [BrowserHistItem]) { + for newURL in newURLs { + let sendingToBrowser = NSUserDefaults.standardUserDefaults().valueForKey(JustUsedConstants.prefSendSafariHistory) as! Bool + if !newURL.excludedFromDiMe && sendingToBrowser { + let infoElem = DocumentInformationElement(fromSafariHist: newURL) + let event = DesktopEvent(infoElem: infoElem, ofType: TrackingType.Browser(newURL.browser), withDate: newURL.date, andLocation: newURL.location) + sendToDiMe(event) + } + } + } + + func newRecentDocument(newItem: RecentDocItem) { + let infoElem = DocumentInformationElement(fromRecentDoc: newItem) + let event = DesktopEvent(infoElem: infoElem, ofType: TrackingType.Spotlight, withDate: newItem.lastAccessDate, andLocation: newItem.location) + sendToDiMe(event) + } + +} + +/// Protocol implementations for calendar updating \ No newline at end of file diff --git a/JustUsed/DiMe Data/Location.swift b/JustUsed/DiMe Data/Location.swift index abac21e..c9c6d24 100644 --- a/JustUsed/DiMe Data/Location.swift +++ b/JustUsed/DiMe Data/Location.swift @@ -59,6 +59,17 @@ struct Location: Dictionariable, Equatable { } } + init(fromJSON json: JSON) { + latitude = json["latitude"].doubleValue + longitude = json["longitude"].doubleValue + altitude = json["altitude"].double + horizAccuracy = json["horizAccuracy"].doubleValue + vertAccuracy = json["vertAccuracy"].double + bearing = json["bearing"].double + speed = json["speed"].double + descriptionLine = json["descriptionLine"].string + } + /// Returns itself in a (json-able) dict func getDict() -> [String: AnyObject] { var retDict = [String: AnyObject]() diff --git a/JustUsed/DiMe Data/Person.swift b/JustUsed/DiMe Data/Person.swift new file mode 100644 index 0000000..ff45752 --- /dev/null +++ b/JustUsed/DiMe Data/Person.swift @@ -0,0 +1,110 @@ +// +// Person.swift +// PeyeDF +// +// Created by Marco Filetti on 20/10/2015. +// Copyright © 2015 HIIT. All rights reserved. +// + +import Foundation + +/// A person is represented by this struct (not a class) +class Person: DiMeBase { + + private(set) var firstName: String = "N/A" + private(set) var lastName: String = "N/A" + private(set) var middleNames: [String] = [String]() + private(set) var email: String? + + /// Returns the name in a string separated by spaces, such as "FistName MiddleName1 MiddleName2 LastName" + override var description: String { get { + var outVal = firstName + " " + if middleNames.count > 0 { + for midName in middleNames { + outVal += midName + " " + } + } + outVal += lastName + return outVal + } } + + /// Generates a person from a string. If there is a comma in the string, it is assumed that the first name after the comma, otherwise first name is the first non-whitespace separated string, and last name is the last. Middle names are assumed to all come after the first name if there was a comma, between first and last if there is no comma. + /// **Fails (returns nil) if the string could not be parsed.** + init?(fromString string: String) { + super.init() + + if string.containsChar(",") { + let splitted = string.split(",") + guard let spl = splitted else { + return nil + } + if spl.count == 2 { + self.lastName = spl[0] + + // check if there are middle names in the following part + if spl[1].containsChar(" ") { + var resplitted = spl[1].split(" ") + self.firstName = resplitted!.removeAtIndex(0) + if resplitted!.count > 0 { + for remName in resplitted! { + middleNames.append(remName) + } + } + } + else { + self.firstName = spl[1] + } + } else { + return nil + } + } else { + let splitted = string.split(" ") + guard let spl = splitted else { + return nil + } + if spl.count >= 2 { + self.firstName = spl.first! + self.lastName = spl.last! + if spl.count > 2 { + for i in 1.. [String : AnyObject] { + theDictionary["firstName"] = firstName + theDictionary["lastName"] = lastName + if middleNames.count > 0 { + theDictionary["middleNames"] = middleNames + } + if let em = self.email { + theDictionary["emailAccount"] = em + } + + // dime-required + theDictionary["@type"] = "Person" + theDictionary["type"] = "http://www.hiit.fi/ontologies/dime/#Person" + + return theDictionary + } +} \ No newline at end of file diff --git a/JustUsed/Libraries/SwiftyJSON.swift b/JustUsed/Libraries/SwiftyJSON.swift new file mode 100755 index 0000000..a63ef84 --- /dev/null +++ b/JustUsed/Libraries/SwiftyJSON.swift @@ -0,0 +1,1352 @@ +// SwiftyJSON.swift +// +// Copyright (c) 2014 Ruoyu Fu, Pinglin Tang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +// MARK: - Error + +///Error domain +public let ErrorDomain: String! = "SwiftyJSONErrorDomain" + +///Error code +public let ErrorUnsupportedType: Int! = 999 +public let ErrorIndexOutOfBounds: Int! = 900 +public let ErrorWrongType: Int! = 901 +public let ErrorNotExist: Int! = 500 +public let ErrorInvalidJSON: Int! = 490 + +// MARK: - JSON Type + +/** +JSON's type definitions. + +See http://tools.ietf.org/html/rfc7231#section-4.3 +*/ +public enum Type :Int{ + + case Number + case String + case Bool + case Array + case Dictionary + case Null + case Unknown +} + +// MARK: - JSON Base + +public struct JSON { + + /** + Creates a JSON using the data. + + - parameter data: The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary + - parameter opt: The JSON serialization reading options. `.AllowFragments` by default. + - parameter error: error The NSErrorPointer used to return the error. `nil` by default. + + - returns: The created JSON + */ + public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) { + do { + let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt) + self.init(object) + } catch let aError as NSError { + if error != nil { + error.memory = aError + } + self.init(NSNull()) + } + } + + /** + Creates a JSON using the object. + + - parameter object: The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity. + + - returns: The created JSON + */ + public init(_ object: AnyObject) { + self.object = object + } + + /** + Creates a JSON from a [JSON] + + - parameter jsonArray: A Swift array of JSON objects + + - returns: The created JSON + */ + public init(_ jsonArray:[JSON]) { + self.init(jsonArray.map { $0.object }) + } + + /** + Creates a JSON from a [String: JSON] + + - parameter jsonDictionary: A Swift dictionary of JSON objects + + - returns: The created JSON + */ + public init(_ jsonDictionary:[String: JSON]) { + var dictionary = [String: AnyObject]() + for (key, json) in jsonDictionary { + dictionary[key] = json.object + } + self.init(dictionary) + } + + /// Private object + private var rawArray: [AnyObject] = [] + private var rawDictionary: [String : AnyObject] = [:] + private var rawString: String = "" + private var rawNumber: NSNumber = 0 + private var rawNull: NSNull = NSNull() + /// Private type + private var _type: Type = .Null + /// prviate error + private var _error: NSError? = nil + + /// Object in JSON + public var object: AnyObject { + get { + switch self.type { + case .Array: + return self.rawArray + case .Dictionary: + return self.rawDictionary + case .String: + return self.rawString + case .Number: + return self.rawNumber + case .Bool: + return self.rawNumber + default: + return self.rawNull + } + } + set { + _error = nil + switch newValue { + case let number as NSNumber: + if number.isBool { + _type = .Bool + } else { + _type = .Number + } + self.rawNumber = number + case let string as String: + _type = .String + self.rawString = string + case _ as NSNull: + _type = .Null + case let array as [AnyObject]: + _type = .Array + self.rawArray = array + case let dictionary as [String : AnyObject]: + _type = .Dictionary + self.rawDictionary = dictionary + default: + _type = .Unknown + _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"]) + } + } + } + + /// json type + public var type: Type { get { return _type } } + + /// Error in JSON + public var error: NSError? { get { return self._error } } + + /// The static null json + @available(*, unavailable, renamed="null") + public static var nullJSON: JSON { get { return null } } + public static var null: JSON { get { return JSON(NSNull()) } } +} + +// MARK: - CollectionType, SequenceType, Indexable +extension JSON : Swift.CollectionType, Swift.SequenceType, Swift.Indexable { + + public typealias Generator = JSONGenerator + + public typealias Index = JSONIndex + + public var startIndex: JSON.Index { + switch self.type { + case .Array: + return JSONIndex(arrayIndex: self.rawArray.startIndex) + case .Dictionary: + return JSONIndex(dictionaryIndex: self.rawDictionary.startIndex) + default: + return JSONIndex() + } + } + + public var endIndex: JSON.Index { + switch self.type { + case .Array: + return JSONIndex(arrayIndex: self.rawArray.endIndex) + case .Dictionary: + return JSONIndex(dictionaryIndex: self.rawDictionary.endIndex) + default: + return JSONIndex() + } + } + + public subscript (position: JSON.Index) -> JSON.Generator.Element { + switch self.type { + case .Array: + return (String(position.arrayIndex), JSON(self.rawArray[position.arrayIndex!])) + case .Dictionary: + let (key, value) = self.rawDictionary[position.dictionaryIndex!] + return (key, JSON(value)) + default: + return ("", JSON.null) + } + } + + /// If `type` is `.Array` or `.Dictionary`, return `array.empty` or `dictonary.empty` otherwise return `false`. + public var isEmpty: Bool { + get { + switch self.type { + case .Array: + return self.rawArray.isEmpty + case .Dictionary: + return self.rawDictionary.isEmpty + default: + return true + } + } + } + + /// If `type` is `.Array` or `.Dictionary`, return `array.count` or `dictonary.count` otherwise return `0`. + public var count: Int { + switch self.type { + case .Array: + return self.rawArray.count + case .Dictionary: + return self.rawDictionary.count + default: + return 0 + } + } + + public func underestimateCount() -> Int { + switch self.type { + case .Array: + return self.rawArray.underestimateCount() + case .Dictionary: + return self.rawDictionary.underestimateCount() + default: + return 0 + } + } + + /** + If `type` is `.Array` or `.Dictionary`, return a generator over the elements like `Array` or `Dictionary`, otherwise return a generator over empty. + + - returns: Return a *generator* over the elements of JSON. + */ + public func generate() -> JSON.Generator { + return JSON.Generator(self) + } +} + +public struct JSONIndex: ForwardIndexType, _Incrementable, Equatable, Comparable { + + let arrayIndex: Int? + let dictionaryIndex: DictionaryIndex? + + let type: Type + + init(){ + self.arrayIndex = nil + self.dictionaryIndex = nil + self.type = .Unknown + } + + init(arrayIndex: Int) { + self.arrayIndex = arrayIndex + self.dictionaryIndex = nil + self.type = .Array + } + + init(dictionaryIndex: DictionaryIndex) { + self.arrayIndex = nil + self.dictionaryIndex = dictionaryIndex + self.type = .Dictionary + } + + public func successor() -> JSONIndex { + switch self.type { + case .Array: + return JSONIndex(arrayIndex: self.arrayIndex!.successor()) + case .Dictionary: + return JSONIndex(dictionaryIndex: self.dictionaryIndex!.successor()) + default: + return JSONIndex() + } + } +} + +public func ==(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex == rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex == rhs.dictionaryIndex + default: + return false + } +} + +public func <(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex < rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex < rhs.dictionaryIndex + default: + return false + } +} + +public func <=(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex <= rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex <= rhs.dictionaryIndex + default: + return false + } +} + +public func >=(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex >= rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex >= rhs.dictionaryIndex + default: + return false + } +} + +public func >(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex > rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex > rhs.dictionaryIndex + default: + return false + } +} + +public struct JSONGenerator : GeneratorType { + + public typealias Element = (String, JSON) + + private let type: Type + private var dictionayGenerate: DictionaryGenerator? + private var arrayGenerate: IndexingGenerator<[AnyObject]>? + private var arrayIndex: Int = 0 + + init(_ json: JSON) { + self.type = json.type + if type == .Array { + self.arrayGenerate = json.rawArray.generate() + }else { + self.dictionayGenerate = json.rawDictionary.generate() + } + } + + public mutating func next() -> JSONGenerator.Element? { + switch self.type { + case .Array: + if let o = self.arrayGenerate!.next() { + return (String(self.arrayIndex++), JSON(o)) + } else { + return nil + } + case .Dictionary: + if let (k, v): (String, AnyObject) = self.dictionayGenerate!.next() { + return (k, JSON(v)) + } else { + return nil + } + default: + return nil + } + } +} + +// MARK: - Subscript + +/** +* To mark both String and Int can be used in subscript. +*/ +public protocol JSONSubscriptType {} + +extension Int: JSONSubscriptType {} + +extension String: JSONSubscriptType {} + +extension JSON { + + /// If `type` is `.Array`, return json which's object is `array[index]`, otherwise return null json with error. + private subscript(index index: Int) -> JSON { + get { + if self.type != .Array { + var r = JSON.null + r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"]) + return r + } else if index >= 0 && index < self.rawArray.count { + return JSON(self.rawArray[index]) + } else { + var r = JSON.null + r._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"]) + return r + } + } + set { + if self.type == .Array { + if self.rawArray.count > index && newValue.error == nil { + self.rawArray[index] = newValue.object + } + } + } + } + + /// If `type` is `.Dictionary`, return json which's object is `dictionary[key]` , otherwise return null json with error. + private subscript(key key: String) -> JSON { + get { + var r = JSON.null + if self.type == .Dictionary { + if let o = self.rawDictionary[key] { + r = JSON(o) + } else { + r._error = NSError(domain: ErrorDomain, code: ErrorNotExist, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] does not exist"]) + } + } else { + r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] failure, It is not an dictionary"]) + } + return r + } + set { + if self.type == .Dictionary && newValue.error == nil { + self.rawDictionary[key] = newValue.object + } + } + } + + /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`. + private subscript(sub sub: JSONSubscriptType) -> JSON { + get { + if sub is String { + return self[key:sub as! String] + } else { + return self[index:sub as! Int] + } + } + set { + if sub is String { + self[key:sub as! String] = newValue + } else { + self[index:sub as! Int] = newValue + } + } + } + + /** + Find a json in the complex data structuresby using the Int/String's array. + + - parameter path: The target json's path. Example: + + let json = JSON[data] + let path = [9,"list","person","name"] + let name = json[path] + + The same as: let name = json[9]["list"]["person"]["name"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: [JSONSubscriptType]) -> JSON { + get { + return path.reduce(self) { $0[sub: $1] } + } + set { + switch path.count { + case 0: + return + case 1: + self[sub:path[0]].object = newValue.object + default: + var aPath = path; aPath.removeAtIndex(0) + var nextJSON = self[sub: path[0]] + nextJSON[aPath] = newValue + self[sub: path[0]] = nextJSON + } + } + } + + /** + Find a json in the complex data structuresby using the Int/String's array. + + - parameter path: The target json's path. Example: + + let name = json[9,"list","person","name"] + + The same as: let name = json[9]["list"]["person"]["name"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: JSONSubscriptType...) -> JSON { + get { + return self[path] + } + set { + self[path] = newValue + } + } +} + +// MARK: - LiteralConvertible + +extension JSON: Swift.StringLiteralConvertible { + + public init(stringLiteral value: StringLiteralType) { + self.init(value) + } + + public init(extendedGraphemeClusterLiteral value: StringLiteralType) { + self.init(value) + } + + public init(unicodeScalarLiteral value: StringLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.IntegerLiteralConvertible { + + public init(integerLiteral value: IntegerLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.BooleanLiteralConvertible { + + public init(booleanLiteral value: BooleanLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.FloatLiteralConvertible { + + public init(floatLiteral value: FloatLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.DictionaryLiteralConvertible { + + public init(dictionaryLiteral elements: (String, AnyObject)...) { + self.init(elements.reduce([String : AnyObject]()){(dictionary: [String : AnyObject], element:(String, AnyObject)) -> [String : AnyObject] in + var d = dictionary + d[element.0] = element.1 + return d + }) + } +} + +extension JSON: Swift.ArrayLiteralConvertible { + + public init(arrayLiteral elements: AnyObject...) { + self.init(elements) + } +} + +extension JSON: Swift.NilLiteralConvertible { + + public init(nilLiteral: ()) { + self.init(NSNull()) + } +} + +// MARK: - Raw + +extension JSON: Swift.RawRepresentable { + + public init?(rawValue: AnyObject) { + if JSON(rawValue).type == .Unknown { + return nil + } else { + self.init(rawValue) + } + } + + public var rawValue: AnyObject { + return self.object + } + + public func rawData(options opt: NSJSONWritingOptions = NSJSONWritingOptions(rawValue: 0)) throws -> NSData { + guard NSJSONSerialization.isValidJSONObject(self.object) else { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "JSON is invalid"]) + } + + return try NSJSONSerialization.dataWithJSONObject(self.object, options: opt) + } + + public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String? { + switch self.type { + case .Array, .Dictionary: + do { + let data = try self.rawData(options: opt) + return NSString(data: data, encoding: encoding) as? String + } catch _ { + return nil + } + case .String: + return self.rawString + case .Number: + return self.rawNumber.stringValue + case .Bool: + return self.rawNumber.boolValue.description + case .Null: + return "null" + default: + return nil + } + } +} + +// MARK: - Printable, DebugPrintable + +extension JSON: Swift.Printable, Swift.DebugPrintable { + + public var description: String { + if let string = self.rawString(options:.PrettyPrinted) { + return string + } else { + return "unknown" + } + } + + public var debugDescription: String { + return description + } +} + +// MARK: - Array + +extension JSON { + + //Optional [JSON] + public var array: [JSON]? { + get { + if self.type == .Array { + return self.rawArray.map{ JSON($0) } + } else { + return nil + } + } + } + + //Non-optional [JSON] + public var arrayValue: [JSON] { + get { + return self.array ?? [] + } + } + + //Optional [AnyObject] + public var arrayObject: [AnyObject]? { + get { + switch self.type { + case .Array: + return self.rawArray + default: + return nil + } + } + set { + if let array = newValue { + self.object = array + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Dictionary + +extension JSON { + + //Optional [String : JSON] + public var dictionary: [String : JSON]? { + if self.type == .Dictionary { + return self.rawDictionary.reduce([String : JSON]()) { (dictionary: [String : JSON], element: (String, AnyObject)) -> [String : JSON] in + var d = dictionary + d[element.0] = JSON(element.1) + return d + } + } else { + return nil + } + } + + //Non-optional [String : JSON] + public var dictionaryValue: [String : JSON] { + return self.dictionary ?? [:] + } + + //Optional [String : AnyObject] + public var dictionaryObject: [String : AnyObject]? { + get { + switch self.type { + case .Dictionary: + return self.rawDictionary + default: + return nil + } + } + set { + if let v = newValue { + self.object = v + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Bool + +extension JSON: Swift.BooleanType { + + //Optional bool + public var bool: Bool? { + get { + switch self.type { + case .Bool: + return self.rawNumber.boolValue + default: + return nil + } + } + set { + if newValue != nil { + self.object = NSNumber(bool: newValue!) + } else { + self.object = NSNull() + } + } + } + + //Non-optional bool + public var boolValue: Bool { + get { + switch self.type { + case .Bool, .Number, .String: + return self.object.boolValue + default: + return false + } + } + set { + self.object = NSNumber(bool: newValue) + } + } +} + +// MARK: - String + +extension JSON { + + //Optional string + public var string: String? { + get { + switch self.type { + case .String: + return self.object as? String + default: + return nil + } + } + set { + if newValue != nil { + self.object = NSString(string:newValue!) + } else { + self.object = NSNull() + } + } + } + + //Non-optional string + public var stringValue: String { + get { + switch self.type { + case .String: + return self.object as! String + case .Number: + return self.object.stringValue + case .Bool: + return (self.object as! Bool).description + default: + return "" + } + } + set { + self.object = NSString(string:newValue) + } + } +} + +// MARK: - Number +extension JSON { + + //Optional number + public var number: NSNumber? { + get { + switch self.type { + case .Number, .Bool: + return self.rawNumber + default: + return nil + } + } + set { + self.object = newValue ?? NSNull() + } + } + + //Non-optional number + public var numberValue: NSNumber { + get { + switch self.type { + case .String: + let decimal = NSDecimalNumber(string: self.object as? String) + if decimal == NSDecimalNumber.notANumber() { // indicates parse error + return NSDecimalNumber.zero() + } + return decimal + case .Number, .Bool: + return self.object as! NSNumber + default: + return NSNumber(double: 0.0) + } + } + set { + self.object = newValue + } + } +} + +//MARK: - Null +extension JSON { + + public var null: NSNull? { + get { + switch self.type { + case .Null: + return self.rawNull + default: + return nil + } + } + set { + self.object = NSNull() + } + } + public func isExists() -> Bool{ + if let errorValue = error where errorValue.code == ErrorNotExist{ + return false + } + return true + } +} + +//MARK: - URL +extension JSON { + + //Optional URL + public var URL: NSURL? { + get { + switch self.type { + case .String: + if let encodedString_ = self.rawString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) { + return NSURL(string: encodedString_) + } else { + return nil + } + default: + return nil + } + } + set { + self.object = newValue?.absoluteString ?? NSNull() + } + } +} + +// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64 + +extension JSON { + + public var double: Double? { + get { + return self.number?.doubleValue + } + set { + if newValue != nil { + self.object = NSNumber(double: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var doubleValue: Double { + get { + return self.numberValue.doubleValue + } + set { + self.object = NSNumber(double: newValue) + } + } + + public var float: Float? { + get { + return self.number?.floatValue + } + set { + if newValue != nil { + self.object = NSNumber(float: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var floatValue: Float { + get { + return self.numberValue.floatValue + } + set { + self.object = NSNumber(float: newValue) + } + } + + public var int: Int? { + get { + return self.number?.longValue + } + set { + if newValue != nil { + self.object = NSNumber(integer: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var intValue: Int { + get { + return self.numberValue.integerValue + } + set { + self.object = NSNumber(integer: newValue) + } + } + + public var uInt: UInt? { + get { + return self.number?.unsignedLongValue + } + set { + if newValue != nil { + self.object = NSNumber(unsignedLong: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var uIntValue: UInt { + get { + return self.numberValue.unsignedLongValue + } + set { + self.object = NSNumber(unsignedLong: newValue) + } + } + + public var int8: Int8? { + get { + return self.number?.charValue + } + set { + if newValue != nil { + self.object = NSNumber(char: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var int8Value: Int8 { + get { + return self.numberValue.charValue + } + set { + self.object = NSNumber(char: newValue) + } + } + + public var uInt8: UInt8? { + get { + return self.number?.unsignedCharValue + } + set { + if newValue != nil { + self.object = NSNumber(unsignedChar: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var uInt8Value: UInt8 { + get { + return self.numberValue.unsignedCharValue + } + set { + self.object = NSNumber(unsignedChar: newValue) + } + } + + public var int16: Int16? { + get { + return self.number?.shortValue + } + set { + if newValue != nil { + self.object = NSNumber(short: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var int16Value: Int16 { + get { + return self.numberValue.shortValue + } + set { + self.object = NSNumber(short: newValue) + } + } + + public var uInt16: UInt16? { + get { + return self.number?.unsignedShortValue + } + set { + if newValue != nil { + self.object = NSNumber(unsignedShort: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var uInt16Value: UInt16 { + get { + return self.numberValue.unsignedShortValue + } + set { + self.object = NSNumber(unsignedShort: newValue) + } + } + + public var int32: Int32? { + get { + return self.number?.intValue + } + set { + if newValue != nil { + self.object = NSNumber(int: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var int32Value: Int32 { + get { + return self.numberValue.intValue + } + set { + self.object = NSNumber(int: newValue) + } + } + + public var uInt32: UInt32? { + get { + return self.number?.unsignedIntValue + } + set { + if newValue != nil { + self.object = NSNumber(unsignedInt: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var uInt32Value: UInt32 { + get { + return self.numberValue.unsignedIntValue + } + set { + self.object = NSNumber(unsignedInt: newValue) + } + } + + public var int64: Int64? { + get { + return self.number?.longLongValue + } + set { + if newValue != nil { + self.object = NSNumber(longLong: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var int64Value: Int64 { + get { + return self.numberValue.longLongValue + } + set { + self.object = NSNumber(longLong: newValue) + } + } + + public var uInt64: UInt64? { + get { + return self.number?.unsignedLongLongValue + } + set { + if newValue != nil { + self.object = NSNumber(unsignedLongLong: newValue!) + } else { + self.object = NSNull() + } + } + } + + public var uInt64Value: UInt64 { + get { + return self.numberValue.unsignedLongLongValue + } + set { + self.object = NSNumber(unsignedLongLong: newValue) + } + } +} + +//MARK: - Comparable +extension JSON : Swift.Comparable {} + +public func ==(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber == rhs.rawNumber + case (.String, .String): + return lhs.rawString == rhs.rawString + case (.Bool, .Bool): + return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue + case (.Array, .Array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.Dictionary, .Dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.Null, .Null): + return true + default: + return false + } +} + +public func <=(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber <= rhs.rawNumber + case (.String, .String): + return lhs.rawString <= rhs.rawString + case (.Bool, .Bool): + return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue + case (.Array, .Array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.Dictionary, .Dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.Null, .Null): + return true + default: + return false + } +} + +public func >=(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber >= rhs.rawNumber + case (.String, .String): + return lhs.rawString >= rhs.rawString + case (.Bool, .Bool): + return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue + case (.Array, .Array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.Dictionary, .Dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.Null, .Null): + return true + default: + return false + } +} + +public func >(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber > rhs.rawNumber + case (.String, .String): + return lhs.rawString > rhs.rawString + default: + return false + } +} + +public func <(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber < rhs.rawNumber + case (.String, .String): + return lhs.rawString < rhs.rawString + default: + return false + } +} + +private let trueNumber = NSNumber(bool: true) +private let falseNumber = NSNumber(bool: false) +private let trueObjCType = String.fromCString(trueNumber.objCType) +private let falseObjCType = String.fromCString(falseNumber.objCType) + +// MARK: - NSNumber: Comparable + +extension NSNumber { + var isBool:Bool { + get { + let objCType = String.fromCString(self.objCType) + if (self.compare(trueNumber) == NSComparisonResult.OrderedSame && objCType == trueObjCType) + || (self.compare(falseNumber) == NSComparisonResult.OrderedSame && objCType == falseObjCType){ + return true + } else { + return false + } + } + } +} + +public func ==(lhs: NSNumber, rhs: NSNumber) -> Bool { + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == NSComparisonResult.OrderedSame + } +} + +public func !=(lhs: NSNumber, rhs: NSNumber) -> Bool { + return !(lhs == rhs) +} + +public func <(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == NSComparisonResult.OrderedAscending + } +} + +public func >(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == NSComparisonResult.OrderedDescending + } +} + +public func <=(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != NSComparisonResult.OrderedDescending + } +} + +public func >=(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != NSComparisonResult.OrderedAscending + } +} diff --git a/Shared/CalendarManager.swift b/JustUsed/Model/CalendarManager.swift similarity index 96% rename from Shared/CalendarManager.swift rename to JustUsed/Model/CalendarManager.swift index e058aab..0393355 100644 --- a/Shared/CalendarManager.swift +++ b/JustUsed/Model/CalendarManager.swift @@ -31,6 +31,9 @@ import EventKit /// To get the current event title: `CalendarManager.sharedInstance.currentEventName` public class CalendarManager { + /// Geocoder user to reverse location information + private let geoMan = CLGeocoder() + private let store = EKEventStore() static let sharedInstance = CalendarManager() diff --git a/JustUsed/Model/LocationSingleton.swift b/JustUsed/Model/LocationSingleton.swift index 9c1de4c..3ae726b 100644 --- a/JustUsed/Model/LocationSingleton.swift +++ b/JustUsed/Model/LocationSingleton.swift @@ -69,32 +69,10 @@ class LocationController: NSObject, CLLocationManagerDelegate { } func locationManager(manager: CLLocationManager, didUpdateLocations locations: [AnyObject]) { - if let retLoc = locations[0] as? CLLocation { - geoMan.reverseGeocodeLocation(retLoc) { - placemarkA, error in - - self.location = Location(fromCLLocation: retLoc) - if let error = error { - self.location?.descriptionLine = "** Error reversing: \(error.description)" - } else { - let placemark = placemarkA![0] - var builtString = "" - if let country = placemark.country { - builtString += "Country: \(country)" - } - if let locality = placemark.locality { - builtString += ", Locality: \(locality)" - } - if let subLoc = placemark.subLocality { - builtString += ", Neighborhood: \(subLoc)" - } - if let street = placemark.thoroughfare { - builtString += ", Street: \(street)" - } - - self.location?.descriptionLine = builtString - } - + if let loc = locations[0] as? CLLocation { + geoMan.getDescription(fromLoc: loc) { + describedLocation in + self.location = describedLocation } } } diff --git a/JustUsed/Utils/Utils.swift b/JustUsed/Utils/Utils.swift index 5f97b40..57aafec 100644 --- a/JustUsed/Utils/Utils.swift +++ b/JustUsed/Utils/Utils.swift @@ -27,6 +27,7 @@ import Foundation import Cocoa import Quartz +import CoreLocation // MARK: - Extensions to standard types @@ -128,4 +129,88 @@ extension String { let hexBytes = digest.map { String(format: "%02hhx", $0) } return hexBytes.joinWithSeparator("") } + + /// Trims whitespace and newlines using foundation + func trimmed() -> String { + let nss: NSString = self + return nss.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + } + + /// Checks if this string contains the given character + func containsChar(theChar: Character) -> Bool { + for c in self.characters { + if c == theChar { + return true + } + } + return false + } + + /// Splits the string, trimming whitespaces, between the given characters (note: slow for very long strings) + func split(theChar: Character) -> [String]? { + var outVal = [String]() + var remainingString = self + while remainingString.containsChar(theChar) { + var outString = "" + var nextChar = remainingString.removeAtIndex(remainingString.startIndex) + while nextChar != theChar { + outString.append(nextChar) + nextChar = remainingString.removeAtIndex(remainingString.startIndex) + } + if !outString.trimmed().isEmpty { + outVal.append(outString.trimmed()) + } + } + if !remainingString.trimmed().isEmpty { + outVal.append(remainingString.trimmed()) + } + if outVal.count > 0 { + return outVal + } else { + return nil + } + } + + /// Skips the first x characters + func skipPrefix(nOfChars: Int) -> String { + return self.substringFromIndex(self.startIndex.advancedBy(nOfChars)) + } + +} + +extension CLGeocoder { + + /// Asynchronously creates a Location object by using a CLLocation and + /// reversing its location. Calls the given block with a Location that includes description. + func getDescription(fromLoc inLoc: CLLocation, block: (describedLocation: Location) -> Void) { + self.reverseGeocodeLocation(inLoc) { + placemarkA, error in + + var outLoc = Location(fromCLLocation: inLoc) + if let error = error { + outLoc.descriptionLine = "** Error reversing: \(error.description)" + } else { + let placemark = placemarkA![0] + var builtString = "" + if let country = placemark.country { + builtString += "Country: \(country)" + } + if let locality = placemark.locality { + builtString += ", Locality: \(locality)" + } + if let subLoc = placemark.subLocality { + builtString += ", Neighborhood: \(subLoc)" + } + if let street = placemark.thoroughfare { + builtString += ", Street: \(street)" + } + + outLoc.descriptionLine = builtString + } + + block(describedLocation: outLoc) + + } + } + } \ No newline at end of file