├── Debter.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── Prontto.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── Prontto.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── Debter.xcscheme │ └── xcschememanagement.plist ├── Debter ├── AddNewViewController.swift ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── CoreDataStackManager.swift ├── Creditor+CoreDataProperties.swift ├── Creditor.swift ├── DebtViewController.swift ├── Debter.xcdatamodeld │ ├── .xccurrentversion │ └── Debter.xcdatamodel │ │ └── contents ├── Event+CoreDataProperties.swift ├── Event.swift ├── Helper.swift ├── Info.plist ├── MainWindowController.swift ├── Ower+CoreDataProperties.swift ├── Ower.swift ├── PersonViewCell.swift ├── ReceivablesViewController.swift ├── SelectedRowView.swift └── VibrancyTable.swift ├── DebterTests ├── DebterTests.swift └── Info.plist ├── DebterUITests ├── DebterUITests.swift └── Info.plist └── README.md /Debter.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 071536A61B29ABA000B5F56C /* Event+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 071536A41B29ABA000B5F56C /* Event+CoreDataProperties.swift */; }; 11 | 071536A71B29ABA000B5F56C /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 071536A51B29ABA000B5F56C /* Event.swift */; }; 12 | 075702351B28CF09001F913D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702341B28CF09001F913D /* AppDelegate.swift */; }; 13 | 075702371B28CF09001F913D /* DebtViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702361B28CF09001F913D /* DebtViewController.swift */; }; 14 | 0757023A1B28CF09001F913D /* Debter.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 075702381B28CF09001F913D /* Debter.xcdatamodeld */; }; 15 | 0757023C1B28CF09001F913D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0757023B1B28CF09001F913D /* Assets.xcassets */; }; 16 | 0757023F1B28CF09001F913D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0757023D1B28CF09001F913D /* Main.storyboard */; }; 17 | 0757024A1B28CF09001F913D /* DebterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702491B28CF09001F913D /* DebterTests.swift */; }; 18 | 075702551B28CF0A001F913D /* DebterUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702541B28CF0A001F913D /* DebterUITests.swift */; }; 19 | 075702631B28CF1A001F913D /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702621B28CF19001F913D /* Helper.swift */; }; 20 | 075702711B28DF92001F913D /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702701B28DF92001F913D /* MainWindowController.swift */; }; 21 | 075702751B28E324001F913D /* ReceivablesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702741B28E324001F913D /* ReceivablesViewController.swift */; }; 22 | 075702771B28E5CE001F913D /* VibrancyTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075702761B28E5CE001F913D /* VibrancyTable.swift */; }; 23 | 07A501C91B2A01A200A9BE46 /* SelectedRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A501C81B2A01A200A9BE46 /* SelectedRowView.swift */; }; 24 | 07AD534A1B28F3430028A9FA /* AddNewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07AD53491B28F3430028A9FA /* AddNewViewController.swift */; }; 25 | 07AD534C1B28F4010028A9FA /* CoreDataStackManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07AD534B1B28F4010028A9FA /* CoreDataStackManager.swift */; }; 26 | 07AD534E1B28F5D50028A9FA /* PersonViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07AD534D1B28F5D50028A9FA /* PersonViewCell.swift */; }; 27 | 07CFCC391B2A29FF00AEFCD3 /* Ower+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CFCC371B2A29FF00AEFCD3 /* Ower+CoreDataProperties.swift */; }; 28 | 07CFCC3A1B2A29FF00AEFCD3 /* Ower.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CFCC381B2A29FF00AEFCD3 /* Ower.swift */; }; 29 | 07CFCC3D1B2A2ACE00AEFCD3 /* Creditor+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CFCC3B1B2A2ACE00AEFCD3 /* Creditor+CoreDataProperties.swift */; }; 30 | 07CFCC3E1B2A2ACE00AEFCD3 /* Creditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CFCC3C1B2A2ACE00AEFCD3 /* Creditor.swift */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | 075702461B28CF09001F913D /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 075702291B28CF09001F913D /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 075702301B28CF09001F913D; 39 | remoteInfo = Debter; 40 | }; 41 | 075702511B28CF0A001F913D /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = 075702291B28CF09001F913D /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = 075702301B28CF09001F913D; 46 | remoteInfo = Debter; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | 071536A41B29ABA000B5F56C /* Event+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Event+CoreDataProperties.swift"; sourceTree = ""; }; 52 | 071536A51B29ABA000B5F56C /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; 53 | 0735D0FB1B29930700F615F0 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 54 | 075702311B28CF09001F913D /* Debter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Debter.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 075702341B28CF09001F913D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 56 | 075702361B28CF09001F913D /* DebtViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebtViewController.swift; sourceTree = ""; }; 57 | 075702391B28CF09001F913D /* Debter.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Debter.xcdatamodel; sourceTree = ""; }; 58 | 0757023B1B28CF09001F913D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 59 | 0757023E1B28CF09001F913D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | 075702401B28CF09001F913D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | 075702451B28CF09001F913D /* DebterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DebterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 075702491B28CF09001F913D /* DebterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebterTests.swift; sourceTree = ""; }; 63 | 0757024B1B28CF09001F913D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 075702501B28CF0A001F913D /* DebterUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DebterUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 075702541B28CF0A001F913D /* DebterUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebterUITests.swift; sourceTree = ""; }; 66 | 075702561B28CF0A001F913D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | 075702621B28CF19001F913D /* Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helper.swift; sourceTree = ""; }; 68 | 075702701B28DF92001F913D /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = ""; }; 69 | 075702741B28E324001F913D /* ReceivablesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceivablesViewController.swift; sourceTree = ""; }; 70 | 075702761B28E5CE001F913D /* VibrancyTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VibrancyTable.swift; sourceTree = ""; }; 71 | 07A501C81B2A01A200A9BE46 /* SelectedRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectedRowView.swift; sourceTree = ""; }; 72 | 07AD53491B28F3430028A9FA /* AddNewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddNewViewController.swift; sourceTree = ""; }; 73 | 07AD534B1B28F4010028A9FA /* CoreDataStackManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStackManager.swift; sourceTree = ""; }; 74 | 07AD534D1B28F5D50028A9FA /* PersonViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersonViewCell.swift; sourceTree = ""; }; 75 | 07CFCC371B2A29FF00AEFCD3 /* Ower+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Ower+CoreDataProperties.swift"; sourceTree = ""; }; 76 | 07CFCC381B2A29FF00AEFCD3 /* Ower.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Ower.swift; sourceTree = ""; }; 77 | 07CFCC3B1B2A2ACE00AEFCD3 /* Creditor+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Creditor+CoreDataProperties.swift"; sourceTree = ""; }; 78 | 07CFCC3C1B2A2ACE00AEFCD3 /* Creditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Creditor.swift; sourceTree = ""; }; 79 | /* End PBXFileReference section */ 80 | 81 | /* Begin PBXFrameworksBuildPhase section */ 82 | 0757022E1B28CF09001F913D /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | 075702421B28CF09001F913D /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | ); 94 | runOnlyForDeploymentPostprocessing = 0; 95 | }; 96 | 0757024D1B28CF0A001F913D /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | /* End PBXFrameworksBuildPhase section */ 104 | 105 | /* Begin PBXGroup section */ 106 | 075702281B28CF09001F913D = { 107 | isa = PBXGroup; 108 | children = ( 109 | 0735D0FB1B29930700F615F0 /* README.md */, 110 | 075702331B28CF09001F913D /* Debter */, 111 | 075702481B28CF09001F913D /* DebterTests */, 112 | 075702531B28CF0A001F913D /* DebterUITests */, 113 | 075702321B28CF09001F913D /* Products */, 114 | ); 115 | sourceTree = ""; 116 | }; 117 | 075702321B28CF09001F913D /* Products */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 075702311B28CF09001F913D /* Debter.app */, 121 | 075702451B28CF09001F913D /* DebterTests.xctest */, 122 | 075702501B28CF0A001F913D /* DebterUITests.xctest */, 123 | ); 124 | name = Products; 125 | sourceTree = ""; 126 | }; 127 | 075702331B28CF09001F913D /* Debter */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 075702341B28CF09001F913D /* AppDelegate.swift */, 131 | 0757023D1B28CF09001F913D /* Main.storyboard */, 132 | 07AD53591B28FB4E0028A9FA /* WindowControllers */, 133 | 07AD534F1B28F7090028A9FA /* ViewControllers */, 134 | 07AD53581B28FAF40028A9FA /* Model */, 135 | 07AD535A1B28FB690028A9FA /* Views */, 136 | 0757023B1B28CF09001F913D /* Assets.xcassets */, 137 | 075702621B28CF19001F913D /* Helper.swift */, 138 | 075702401B28CF09001F913D /* Info.plist */, 139 | ); 140 | path = Debter; 141 | sourceTree = ""; 142 | }; 143 | 075702481B28CF09001F913D /* DebterTests */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 075702491B28CF09001F913D /* DebterTests.swift */, 147 | 0757024B1B28CF09001F913D /* Info.plist */, 148 | ); 149 | path = DebterTests; 150 | sourceTree = ""; 151 | }; 152 | 075702531B28CF0A001F913D /* DebterUITests */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 075702541B28CF0A001F913D /* DebterUITests.swift */, 156 | 075702561B28CF0A001F913D /* Info.plist */, 157 | ); 158 | path = DebterUITests; 159 | sourceTree = ""; 160 | }; 161 | 07AD534F1B28F7090028A9FA /* ViewControllers */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 075702361B28CF09001F913D /* DebtViewController.swift */, 165 | 075702741B28E324001F913D /* ReceivablesViewController.swift */, 166 | 07AD53491B28F3430028A9FA /* AddNewViewController.swift */, 167 | ); 168 | name = ViewControllers; 169 | sourceTree = ""; 170 | }; 171 | 07AD53581B28FAF40028A9FA /* Model */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 07CFCC3B1B2A2ACE00AEFCD3 /* Creditor+CoreDataProperties.swift */, 175 | 07CFCC3C1B2A2ACE00AEFCD3 /* Creditor.swift */, 176 | 07CFCC371B2A29FF00AEFCD3 /* Ower+CoreDataProperties.swift */, 177 | 07CFCC381B2A29FF00AEFCD3 /* Ower.swift */, 178 | 071536A41B29ABA000B5F56C /* Event+CoreDataProperties.swift */, 179 | 071536A51B29ABA000B5F56C /* Event.swift */, 180 | 07AD534B1B28F4010028A9FA /* CoreDataStackManager.swift */, 181 | 075702381B28CF09001F913D /* Debter.xcdatamodeld */, 182 | ); 183 | name = Model; 184 | sourceTree = ""; 185 | }; 186 | 07AD53591B28FB4E0028A9FA /* WindowControllers */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 075702701B28DF92001F913D /* MainWindowController.swift */, 190 | ); 191 | name = WindowControllers; 192 | sourceTree = ""; 193 | }; 194 | 07AD535A1B28FB690028A9FA /* Views */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 07AD534D1B28F5D50028A9FA /* PersonViewCell.swift */, 198 | 07A501C81B2A01A200A9BE46 /* SelectedRowView.swift */, 199 | 075702761B28E5CE001F913D /* VibrancyTable.swift */, 200 | ); 201 | name = Views; 202 | sourceTree = ""; 203 | }; 204 | /* End PBXGroup section */ 205 | 206 | /* Begin PBXNativeTarget section */ 207 | 075702301B28CF09001F913D /* Debter */ = { 208 | isa = PBXNativeTarget; 209 | buildConfigurationList = 075702591B28CF0A001F913D /* Build configuration list for PBXNativeTarget "Debter" */; 210 | buildPhases = ( 211 | 0757022D1B28CF09001F913D /* Sources */, 212 | 0757022E1B28CF09001F913D /* Frameworks */, 213 | 0757022F1B28CF09001F913D /* Resources */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | ); 219 | name = Debter; 220 | productName = Debter; 221 | productReference = 075702311B28CF09001F913D /* Debter.app */; 222 | productType = "com.apple.product-type.application"; 223 | }; 224 | 075702441B28CF09001F913D /* DebterTests */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = 0757025C1B28CF0A001F913D /* Build configuration list for PBXNativeTarget "DebterTests" */; 227 | buildPhases = ( 228 | 075702411B28CF09001F913D /* Sources */, 229 | 075702421B28CF09001F913D /* Frameworks */, 230 | 075702431B28CF09001F913D /* Resources */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | 075702471B28CF09001F913D /* PBXTargetDependency */, 236 | ); 237 | name = DebterTests; 238 | productName = DebterTests; 239 | productReference = 075702451B28CF09001F913D /* DebterTests.xctest */; 240 | productType = "com.apple.product-type.bundle.unit-test"; 241 | }; 242 | 0757024F1B28CF0A001F913D /* DebterUITests */ = { 243 | isa = PBXNativeTarget; 244 | buildConfigurationList = 0757025F1B28CF0A001F913D /* Build configuration list for PBXNativeTarget "DebterUITests" */; 245 | buildPhases = ( 246 | 0757024C1B28CF0A001F913D /* Sources */, 247 | 0757024D1B28CF0A001F913D /* Frameworks */, 248 | 0757024E1B28CF0A001F913D /* Resources */, 249 | ); 250 | buildRules = ( 251 | ); 252 | dependencies = ( 253 | 075702521B28CF0A001F913D /* PBXTargetDependency */, 254 | ); 255 | name = DebterUITests; 256 | productName = DebterUITests; 257 | productReference = 075702501B28CF0A001F913D /* DebterUITests.xctest */; 258 | productType = "com.apple.product-type.bundle.ui-testing"; 259 | }; 260 | /* End PBXNativeTarget section */ 261 | 262 | /* Begin PBXProject section */ 263 | 075702291B28CF09001F913D /* Project object */ = { 264 | isa = PBXProject; 265 | attributes = { 266 | LastUpgradeCheck = 0700; 267 | ORGANIZATIONNAME = "Samu Tuominen"; 268 | TargetAttributes = { 269 | 075702301B28CF09001F913D = { 270 | CreatedOnToolsVersion = 7.0; 271 | }; 272 | 075702441B28CF09001F913D = { 273 | CreatedOnToolsVersion = 7.0; 274 | TestTargetID = 075702301B28CF09001F913D; 275 | }; 276 | 0757024F1B28CF0A001F913D = { 277 | CreatedOnToolsVersion = 7.0; 278 | TestTargetID = 075702301B28CF09001F913D; 279 | }; 280 | }; 281 | }; 282 | buildConfigurationList = 0757022C1B28CF09001F913D /* Build configuration list for PBXProject "Debter" */; 283 | compatibilityVersion = "Xcode 3.2"; 284 | developmentRegion = English; 285 | hasScannedForEncodings = 0; 286 | knownRegions = ( 287 | en, 288 | Base, 289 | ); 290 | mainGroup = 075702281B28CF09001F913D; 291 | productRefGroup = 075702321B28CF09001F913D /* Products */; 292 | projectDirPath = ""; 293 | projectRoot = ""; 294 | targets = ( 295 | 075702301B28CF09001F913D /* Debter */, 296 | 075702441B28CF09001F913D /* DebterTests */, 297 | 0757024F1B28CF0A001F913D /* DebterUITests */, 298 | ); 299 | }; 300 | /* End PBXProject section */ 301 | 302 | /* Begin PBXResourcesBuildPhase section */ 303 | 0757022F1B28CF09001F913D /* Resources */ = { 304 | isa = PBXResourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | 0757023C1B28CF09001F913D /* Assets.xcassets in Resources */, 308 | 0757023F1B28CF09001F913D /* Main.storyboard in Resources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | 075702431B28CF09001F913D /* Resources */ = { 313 | isa = PBXResourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | 0757024E1B28CF0A001F913D /* Resources */ = { 320 | isa = PBXResourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | /* End PBXResourcesBuildPhase section */ 327 | 328 | /* Begin PBXSourcesBuildPhase section */ 329 | 0757022D1B28CF09001F913D /* Sources */ = { 330 | isa = PBXSourcesBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | 07CFCC391B2A29FF00AEFCD3 /* Ower+CoreDataProperties.swift in Sources */, 334 | 07CFCC3A1B2A29FF00AEFCD3 /* Ower.swift in Sources */, 335 | 075702371B28CF09001F913D /* DebtViewController.swift in Sources */, 336 | 07AD534E1B28F5D50028A9FA /* PersonViewCell.swift in Sources */, 337 | 071536A61B29ABA000B5F56C /* Event+CoreDataProperties.swift in Sources */, 338 | 075702771B28E5CE001F913D /* VibrancyTable.swift in Sources */, 339 | 07AD534A1B28F3430028A9FA /* AddNewViewController.swift in Sources */, 340 | 075702351B28CF09001F913D /* AppDelegate.swift in Sources */, 341 | 07A501C91B2A01A200A9BE46 /* SelectedRowView.swift in Sources */, 342 | 07CFCC3D1B2A2ACE00AEFCD3 /* Creditor+CoreDataProperties.swift in Sources */, 343 | 0757023A1B28CF09001F913D /* Debter.xcdatamodeld in Sources */, 344 | 071536A71B29ABA000B5F56C /* Event.swift in Sources */, 345 | 07AD534C1B28F4010028A9FA /* CoreDataStackManager.swift in Sources */, 346 | 07CFCC3E1B2A2ACE00AEFCD3 /* Creditor.swift in Sources */, 347 | 075702631B28CF1A001F913D /* Helper.swift in Sources */, 348 | 075702751B28E324001F913D /* ReceivablesViewController.swift in Sources */, 349 | 075702711B28DF92001F913D /* MainWindowController.swift in Sources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | 075702411B28CF09001F913D /* Sources */ = { 354 | isa = PBXSourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | 0757024A1B28CF09001F913D /* DebterTests.swift in Sources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | 0757024C1B28CF0A001F913D /* Sources */ = { 362 | isa = PBXSourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | 075702551B28CF0A001F913D /* DebterUITests.swift in Sources */, 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | }; 369 | /* End PBXSourcesBuildPhase section */ 370 | 371 | /* Begin PBXTargetDependency section */ 372 | 075702471B28CF09001F913D /* PBXTargetDependency */ = { 373 | isa = PBXTargetDependency; 374 | target = 075702301B28CF09001F913D /* Debter */; 375 | targetProxy = 075702461B28CF09001F913D /* PBXContainerItemProxy */; 376 | }; 377 | 075702521B28CF0A001F913D /* PBXTargetDependency */ = { 378 | isa = PBXTargetDependency; 379 | target = 075702301B28CF09001F913D /* Debter */; 380 | targetProxy = 075702511B28CF0A001F913D /* PBXContainerItemProxy */; 381 | }; 382 | /* End PBXTargetDependency section */ 383 | 384 | /* Begin PBXVariantGroup section */ 385 | 0757023D1B28CF09001F913D /* Main.storyboard */ = { 386 | isa = PBXVariantGroup; 387 | children = ( 388 | 0757023E1B28CF09001F913D /* Base */, 389 | ); 390 | name = Main.storyboard; 391 | sourceTree = ""; 392 | }; 393 | /* End PBXVariantGroup section */ 394 | 395 | /* Begin XCBuildConfiguration section */ 396 | 075702571B28CF0A001F913D /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 401 | CLANG_CXX_LIBRARY = "libc++"; 402 | CLANG_ENABLE_MODULES = YES; 403 | CLANG_ENABLE_OBJC_ARC = YES; 404 | CLANG_WARN_BOOL_CONVERSION = YES; 405 | CLANG_WARN_CONSTANT_CONVERSION = YES; 406 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 407 | CLANG_WARN_EMPTY_BODY = YES; 408 | CLANG_WARN_ENUM_CONVERSION = YES; 409 | CLANG_WARN_INT_CONVERSION = YES; 410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 411 | CLANG_WARN_UNREACHABLE_CODE = YES; 412 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 413 | CODE_SIGN_IDENTITY = "-"; 414 | COPY_PHASE_STRIP = NO; 415 | DEBUG_INFORMATION_FORMAT = dwarf; 416 | ENABLE_STRICT_OBJC_MSGSEND = YES; 417 | ENABLE_TESTABILITY = YES; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_DYNAMIC_NO_PIC = NO; 420 | GCC_NO_COMMON_BLOCKS = YES; 421 | GCC_OPTIMIZATION_LEVEL = 0; 422 | GCC_PREPROCESSOR_DEFINITIONS = ( 423 | "DEBUG=1", 424 | "$(inherited)", 425 | ); 426 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 428 | GCC_WARN_UNDECLARED_SELECTOR = YES; 429 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 430 | GCC_WARN_UNUSED_FUNCTION = YES; 431 | GCC_WARN_UNUSED_VARIABLE = YES; 432 | MACOSX_DEPLOYMENT_TARGET = 10.10; 433 | MTL_ENABLE_DEBUG_INFO = YES; 434 | ONLY_ACTIVE_ARCH = YES; 435 | SDKROOT = macosx; 436 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 437 | }; 438 | name = Debug; 439 | }; 440 | 075702581B28CF0A001F913D /* Release */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | ALWAYS_SEARCH_USER_PATHS = NO; 444 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 445 | CLANG_CXX_LIBRARY = "libc++"; 446 | CLANG_ENABLE_MODULES = YES; 447 | CLANG_ENABLE_OBJC_ARC = YES; 448 | CLANG_WARN_BOOL_CONVERSION = YES; 449 | CLANG_WARN_CONSTANT_CONVERSION = YES; 450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 451 | CLANG_WARN_EMPTY_BODY = YES; 452 | CLANG_WARN_ENUM_CONVERSION = YES; 453 | CLANG_WARN_INT_CONVERSION = YES; 454 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 455 | CLANG_WARN_UNREACHABLE_CODE = YES; 456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 457 | CODE_SIGN_IDENTITY = "-"; 458 | COPY_PHASE_STRIP = NO; 459 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 460 | ENABLE_NS_ASSERTIONS = NO; 461 | ENABLE_STRICT_OBJC_MSGSEND = YES; 462 | GCC_C_LANGUAGE_STANDARD = gnu99; 463 | GCC_NO_COMMON_BLOCKS = YES; 464 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 465 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 466 | GCC_WARN_UNDECLARED_SELECTOR = YES; 467 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 468 | GCC_WARN_UNUSED_FUNCTION = YES; 469 | GCC_WARN_UNUSED_VARIABLE = YES; 470 | MACOSX_DEPLOYMENT_TARGET = 10.10; 471 | MTL_ENABLE_DEBUG_INFO = NO; 472 | SDKROOT = macosx; 473 | }; 474 | name = Release; 475 | }; 476 | 0757025A1B28CF0A001F913D /* Debug */ = { 477 | isa = XCBuildConfiguration; 478 | buildSettings = { 479 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 480 | COMBINE_HIDPI_IMAGES = YES; 481 | INFOPLIST_FILE = Debter/Info.plist; 482 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 483 | PRODUCT_BUNDLE_IDENTIFIER = com.samutuominen.Debter; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | }; 486 | name = Debug; 487 | }; 488 | 0757025B1B28CF0A001F913D /* Release */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | COMBINE_HIDPI_IMAGES = YES; 493 | INFOPLIST_FILE = Debter/Info.plist; 494 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 495 | PRODUCT_BUNDLE_IDENTIFIER = com.samutuominen.Debter; 496 | PRODUCT_NAME = "$(TARGET_NAME)"; 497 | }; 498 | name = Release; 499 | }; 500 | 0757025D1B28CF0A001F913D /* Debug */ = { 501 | isa = XCBuildConfiguration; 502 | buildSettings = { 503 | BUNDLE_LOADER = "$(TEST_HOST)"; 504 | COMBINE_HIDPI_IMAGES = YES; 505 | INFOPLIST_FILE = DebterTests/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 507 | PRODUCT_BUNDLE_IDENTIFIER = com.samutuominen.DebterTests; 508 | PRODUCT_NAME = "$(TARGET_NAME)"; 509 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Debter.app/Contents/MacOS/Debter"; 510 | }; 511 | name = Debug; 512 | }; 513 | 0757025E1B28CF0A001F913D /* Release */ = { 514 | isa = XCBuildConfiguration; 515 | buildSettings = { 516 | BUNDLE_LOADER = "$(TEST_HOST)"; 517 | COMBINE_HIDPI_IMAGES = YES; 518 | INFOPLIST_FILE = DebterTests/Info.plist; 519 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 520 | PRODUCT_BUNDLE_IDENTIFIER = com.samutuominen.DebterTests; 521 | PRODUCT_NAME = "$(TARGET_NAME)"; 522 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Debter.app/Contents/MacOS/Debter"; 523 | }; 524 | name = Release; 525 | }; 526 | 075702601B28CF0A001F913D /* Debug */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | COMBINE_HIDPI_IMAGES = YES; 530 | INFOPLIST_FILE = DebterUITests/Info.plist; 531 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 532 | PRODUCT_BUNDLE_IDENTIFIER = com.samutuominen.DebterUITests; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | TEST_TARGET_NAME = Debter; 535 | USES_XCTRUNNER = YES; 536 | }; 537 | name = Debug; 538 | }; 539 | 075702611B28CF0A001F913D /* Release */ = { 540 | isa = XCBuildConfiguration; 541 | buildSettings = { 542 | COMBINE_HIDPI_IMAGES = YES; 543 | INFOPLIST_FILE = DebterUITests/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 545 | PRODUCT_BUNDLE_IDENTIFIER = com.samutuominen.DebterUITests; 546 | PRODUCT_NAME = "$(TARGET_NAME)"; 547 | TEST_TARGET_NAME = Debter; 548 | USES_XCTRUNNER = YES; 549 | }; 550 | name = Release; 551 | }; 552 | /* End XCBuildConfiguration section */ 553 | 554 | /* Begin XCConfigurationList section */ 555 | 0757022C1B28CF09001F913D /* Build configuration list for PBXProject "Debter" */ = { 556 | isa = XCConfigurationList; 557 | buildConfigurations = ( 558 | 075702571B28CF0A001F913D /* Debug */, 559 | 075702581B28CF0A001F913D /* Release */, 560 | ); 561 | defaultConfigurationIsVisible = 0; 562 | defaultConfigurationName = Release; 563 | }; 564 | 075702591B28CF0A001F913D /* Build configuration list for PBXNativeTarget "Debter" */ = { 565 | isa = XCConfigurationList; 566 | buildConfigurations = ( 567 | 0757025A1B28CF0A001F913D /* Debug */, 568 | 0757025B1B28CF0A001F913D /* Release */, 569 | ); 570 | defaultConfigurationIsVisible = 0; 571 | defaultConfigurationName = Release; 572 | }; 573 | 0757025C1B28CF0A001F913D /* Build configuration list for PBXNativeTarget "DebterTests" */ = { 574 | isa = XCConfigurationList; 575 | buildConfigurations = ( 576 | 0757025D1B28CF0A001F913D /* Debug */, 577 | 0757025E1B28CF0A001F913D /* Release */, 578 | ); 579 | defaultConfigurationIsVisible = 0; 580 | defaultConfigurationName = Release; 581 | }; 582 | 0757025F1B28CF0A001F913D /* Build configuration list for PBXNativeTarget "DebterUITests" */ = { 583 | isa = XCConfigurationList; 584 | buildConfigurations = ( 585 | 075702601B28CF0A001F913D /* Debug */, 586 | 075702611B28CF0A001F913D /* Release */, 587 | ); 588 | defaultConfigurationIsVisible = 0; 589 | defaultConfigurationName = Release; 590 | }; 591 | /* End XCConfigurationList section */ 592 | 593 | /* Begin XCVersionGroup section */ 594 | 075702381B28CF09001F913D /* Debter.xcdatamodeld */ = { 595 | isa = XCVersionGroup; 596 | children = ( 597 | 075702391B28CF09001F913D /* Debter.xcdatamodel */, 598 | ); 599 | currentVersion = 075702391B28CF09001F913D /* Debter.xcdatamodel */; 600 | path = Debter.xcdatamodeld; 601 | sourceTree = ""; 602 | versionGroupType = wrapper.xcdatamodel; 603 | }; 604 | /* End XCVersionGroup section */ 605 | }; 606 | rootObject = 075702291B28CF09001F913D /* Project object */; 607 | } 608 | -------------------------------------------------------------------------------- /Debter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Debter.xcodeproj/project.xcworkspace/xcuserdata/Prontto.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prontto/Debter/43fa90e3b92f4963001e8dfe3d5f5d051867c8ea/Debter.xcodeproj/project.xcworkspace/xcuserdata/Prontto.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Debter.xcodeproj/xcuserdata/Prontto.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Debter.xcodeproj/xcuserdata/Prontto.xcuserdatad/xcschemes/Debter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Debter.xcodeproj/xcuserdata/Prontto.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Debter.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 075702301B28CF09001F913D 16 | 17 | primary 18 | 19 | 20 | 075702441B28CF09001F913D 21 | 22 | primary 23 | 24 | 25 | 0757024F1B28CF0A001F913D 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Debter/AddNewViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddNewViewController.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /// Informs MainWindowController that new event has been added, so it knows calculate sum for windows title. 12 | @objc protocol EventAddedDelegate { 13 | func newEventAdded(sender: AddNewViewController, isDebt: Bool, isReceivable: Bool) 14 | } 15 | 16 | class AddNewViewController: NSViewController { 17 | 18 | // MARK: - IBoutlets 19 | @IBOutlet weak var recRadioButton: NSButton! 20 | @IBOutlet weak var debtRadioButton: NSButton! 21 | @IBOutlet weak var nameField: NSTextField! 22 | @IBOutlet weak var sumField: NSTextField! 23 | @IBOutlet weak var descField: NSTextField! 24 | @IBOutlet weak var datePicker: NSDatePicker! 25 | 26 | lazy var moc = CoreDataStackManager.sharedManager.managedObjectContext 27 | 28 | weak var debtDelegate: EventAddedDelegate? 29 | weak var recDelegate: EventAddedDelegate? 30 | weak var titleDelegate: EventAddedDelegate? 31 | 32 | // For current selected radiobutton, 0 = receivable & 1 = debt 33 | var current = 0 34 | 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | 38 | datePicker.dateValue = NSDate() 39 | recRadioButton.state = 1 40 | debtRadioButton.state = 0 41 | } 42 | 43 | /// Add new debt and if someone with same name exists, this new debt will be inserted to his debts. 44 | private func addDebt() { 45 | 46 | // Check that user have leaved these fields empty. Could also use NSAlert here. 47 | guard nameField.stringValue != "" && sumField.stringValue != "" else { 48 | return 49 | } 50 | 51 | defer { 52 | debtDelegate?.newEventAdded(self, isDebt: true, isReceivable: false) 53 | titleDelegate?.newEventAdded(self, isDebt: true, isReceivable: false) 54 | emptyFields() 55 | dismissViewController(self) 56 | } 57 | 58 | let predi = NSPredicate(format: "name == %@", nameField.stringValue) 59 | let fetchResults = Helper.fetchEntities("Creditor", predicate: predi, moc: moc) as! [Creditor] 60 | 61 | guard fetchResults.count > 0 else { 62 | 63 | let person = Helper.insertManagedObject("Creditor", moc: moc) as! Creditor 64 | person.name = nameField.stringValue 65 | 66 | let newDebt = Helper.insertManagedObject("Event", moc: moc) as! Event 67 | newDebt.sum = sumField.doubleValue 68 | newDebt.date = datePicker.dateValue 69 | newDebt.desc = descField.stringValue 70 | newDebt.creditor = person 71 | 72 | return 73 | } 74 | 75 | let newDebt = Helper.insertManagedObject("Event", moc: moc) as! Event 76 | newDebt.sum = sumField.doubleValue 77 | newDebt.date = datePicker.dateValue 78 | newDebt.desc = descField.stringValue 79 | newDebt.creditor = fetchResults[0] 80 | } 81 | 82 | // Add new receivable and if someone with same name exists, this new debt will be inserted to his receivables. 83 | private func addReceivable() { 84 | guard nameField.stringValue != "" && sumField.stringValue != "" else { 85 | return 86 | } 87 | 88 | defer { 89 | recDelegate?.newEventAdded(self, isDebt: false, isReceivable: true) 90 | titleDelegate?.newEventAdded(self, isDebt: false, isReceivable: true) 91 | emptyFields() 92 | dismissViewController(self) 93 | } 94 | 95 | let predi = NSPredicate(format: "name == %@", nameField.stringValue) 96 | let fetchResults = Helper.fetchEntities("Ower", predicate: predi, moc: moc) as! [Ower] 97 | 98 | guard fetchResults.count > 0 else { 99 | 100 | let person = Helper.insertManagedObject("Ower", moc: moc) as! Ower 101 | person.name = nameField.stringValue 102 | 103 | let newRec = Helper.insertManagedObject("Event", moc: moc) as! Event 104 | newRec.sum = sumField.doubleValue 105 | newRec.date = datePicker.dateValue 106 | newRec.desc = descField.stringValue 107 | newRec.ower = person 108 | 109 | return 110 | } 111 | 112 | let newRec = Helper.insertManagedObject("Event", moc: moc) as! Event 113 | newRec.sum = sumField.doubleValue 114 | newRec.date = datePicker.dateValue 115 | newRec.desc = descField.stringValue 116 | newRec.ower = fetchResults[0] 117 | } 118 | 119 | private func emptyFields() { 120 | let fields = [nameField, sumField, descField] 121 | for item in fields { 122 | item.stringValue = "" 123 | } 124 | } 125 | 126 | // MARK: - IBActions 127 | @IBAction func ready(sender: AnyObject) { 128 | if recRadioButton.state == 1 { addReceivable() } 129 | else { addDebt() } 130 | } 131 | 132 | @IBAction func cancel(sender: AnyObject) { 133 | emptyFields() 134 | dismissViewController(self) 135 | } 136 | 137 | @IBAction func recSelected(sender: AnyObject) { 138 | guard current == 1 else { 139 | return 140 | } 141 | recRadioButton.state = 1 142 | debtRadioButton.state = 0 143 | current = 0 144 | } 145 | 146 | @IBAction func debtSelected(sender: AnyObject) { 147 | guard current == 0 else { 148 | return 149 | } 150 | recRadioButton.state = 0 151 | debtRadioButton.state = 1 152 | current = 1 153 | } 154 | 155 | 156 | } 157 | -------------------------------------------------------------------------------- /Debter/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 10.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | func applicationDidFinishLaunching(aNotification: NSNotification) { 15 | // Insert code here to initialize your application 16 | } 17 | 18 | func applicationWillTerminate(aNotification: NSNotification) { 19 | // Insert code here to tear down your application 20 | do { 21 | try CoreDataStackManager.sharedManager.managedObjectContext.save() 22 | } catch { 23 | print("Error while saving.") 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Debter/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Debter/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 381 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 512 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 537 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 780 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 911 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 936 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1064 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1099 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | -------------------------------------------------------------------------------- /Debter/CoreDataStackManager.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Singleton-controller for Core Data. 3 | */ 4 | 5 | import Cocoa 6 | 7 | class CoreDataStackManager { 8 | 9 | static let sharedManager = CoreDataStackManager() 10 | 11 | private init() { } 12 | 13 | // MARK: - Core Data stack 14 | lazy var applicationDocumentsDirectory: NSURL = { 15 | // The directory the application uses to store the Core Data store file. This code uses a directory named "com.samutuominen.Debter" in the user's Application Support directory. 16 | let urls = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask) 17 | let appSupportURL = urls[urls.count - 1] 18 | return appSupportURL.URLByAppendingPathComponent("com.samutuominen.Debter") 19 | }() 20 | 21 | lazy var managedObjectModel: NSManagedObjectModel = { 22 | // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. 23 | let modelURL = NSBundle.mainBundle().URLForResource("Debter", withExtension: "momd")! 24 | return NSManagedObjectModel(contentsOfURL: modelURL)! 25 | }() 26 | 27 | lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { 28 | // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.) This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. 29 | let fileManager = NSFileManager.defaultManager() 30 | var failError: NSError? = nil 31 | var shouldFail = false 32 | var failureReason = "There was an error creating or loading the application's saved data." 33 | 34 | // Make sure the application files directory is there 35 | do { 36 | let properties = try self.applicationDocumentsDirectory.resourceValuesForKeys([NSURLIsDirectoryKey]) 37 | if !properties[NSURLIsDirectoryKey]!.boolValue { 38 | failureReason = "Expected a folder to store application data, found a file \(self.applicationDocumentsDirectory.path)." 39 | shouldFail = true 40 | } 41 | } catch { 42 | let nserror = error as NSError 43 | if nserror.code == NSFileReadNoSuchFileError { 44 | do { 45 | try fileManager.createDirectoryAtPath(self.applicationDocumentsDirectory.path!, withIntermediateDirectories: true, attributes: nil) 46 | } catch { 47 | failError = nserror 48 | } 49 | } else { 50 | failError = nserror 51 | } 52 | } 53 | 54 | // Create the coordinator and store 55 | var coordinator: NSPersistentStoreCoordinator? = nil 56 | if failError == nil { 57 | coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) 58 | let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("Debter.sqlite") 59 | do { 60 | try coordinator!.addPersistentStoreWithType(NSXMLStoreType, configuration: nil, URL: url, options: nil) 61 | } catch { 62 | failError = error as NSError 63 | } 64 | } 65 | 66 | if shouldFail || (failError != nil) { 67 | // Report any error we got. 68 | var dict = [String: AnyObject]() 69 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" 70 | dict[NSLocalizedFailureReasonErrorKey] = failureReason 71 | if failError != nil { 72 | dict[NSUnderlyingErrorKey] = failError 73 | } 74 | let error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) 75 | NSApplication.sharedApplication().presentError(error) 76 | abort() 77 | } else { 78 | return coordinator! 79 | } 80 | }() 81 | 82 | lazy var managedObjectContext: NSManagedObjectContext = { 83 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. 84 | let coordinator = self.persistentStoreCoordinator 85 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) 86 | managedObjectContext.persistentStoreCoordinator = coordinator 87 | return managedObjectContext 88 | }() 89 | 90 | // MARK: - Core Data Saving and Undo support 91 | 92 | @IBAction func saveAction(sender: AnyObject!) { 93 | // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user. 94 | if !managedObjectContext.commitEditing() { 95 | NSLog("\(NSStringFromClass(self.dynamicType)) unable to commit editing before saving") 96 | } 97 | if managedObjectContext.hasChanges { 98 | do { 99 | try managedObjectContext.save() 100 | } catch { 101 | let nserror = error as NSError 102 | NSApplication.sharedApplication().presentError(nserror) 103 | } 104 | } 105 | } 106 | 107 | func windowWillReturnUndoManager(window: NSWindow) -> NSUndoManager? { 108 | // Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application. 109 | return managedObjectContext.undoManager 110 | } 111 | 112 | func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply { 113 | // Save changes in the application's managed object context before the application terminates. 114 | 115 | if !managedObjectContext.commitEditing() { 116 | NSLog("\(NSStringFromClass(self.dynamicType)) unable to commit editing to terminate") 117 | return .TerminateCancel 118 | } 119 | 120 | if !managedObjectContext.hasChanges { 121 | return .TerminateNow 122 | } 123 | 124 | do { 125 | try managedObjectContext.save() 126 | } catch { 127 | let nserror = error as NSError 128 | // Customize this code block to include application-specific recovery steps. 129 | let result = sender.presentError(nserror) 130 | if (result) { 131 | return .TerminateCancel 132 | } 133 | 134 | let question = NSLocalizedString("Could not save changes while quitting. Quit anyway?", comment: "Quit without saves error question message") 135 | let info = NSLocalizedString("Quitting now will lose any changes you have made since the last successful save", comment: "Quit without saves error question info"); 136 | let quitButton = NSLocalizedString("Quit anyway", comment: "Quit anyway button title") 137 | let cancelButton = NSLocalizedString("Cancel", comment: "Cancel button title") 138 | let alert = NSAlert() 139 | alert.messageText = question 140 | alert.informativeText = info 141 | alert.addButtonWithTitle(quitButton) 142 | alert.addButtonWithTitle(cancelButton) 143 | 144 | let answer = alert.runModal() 145 | if answer == NSAlertFirstButtonReturn { 146 | return .TerminateCancel 147 | } 148 | } 149 | // If we got here, it is time to quit. 150 | return .TerminateNow 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Debter/Creditor+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Creditor+CoreDataProperties.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | // Delete this file and regenerate it using "Create NSManagedObject Subclass…" 9 | // to keep your implementation up to date with your model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension Creditor { 16 | 17 | @NSManaged var name: String 18 | @NSManaged var events: NSSet 19 | var amount: Double { 20 | let allEvents = self.events.allObjects as! [Event] 21 | var sum = 0.0 22 | for i in allEvents { 23 | sum += i.sum.doubleValue 24 | } 25 | return sum 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Debter/Creditor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Creditor.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | @objc(Creditor) 13 | class Creditor: NSManagedObject { 14 | 15 | // Insert code here to add functionality to your managed object subclass 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Debter/DebtViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DebtViewController.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 10.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @objc protocol DebtDelegate { 12 | func debtOrCreditorDeleted(sender: DebtViewController) 13 | } 14 | 15 | class DebtViewController: NSViewController, EventAddedDelegate { 16 | 17 | @IBOutlet weak var creditorTable: VibrancyTable! 18 | @IBOutlet weak var debtTable: VibrancyTable! 19 | @IBOutlet weak var removeCreditorButton: NSButton! 20 | @IBOutlet weak var removeDebtButton: NSButton! 21 | @IBOutlet weak var creditorController: NSArrayController! 22 | @IBOutlet weak var debtController: NSArrayController! 23 | 24 | lazy var moc = CoreDataStackManager.sharedManager.managedObjectContext 25 | 26 | weak var delegate: DebtDelegate? 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | } 31 | 32 | // EventAddedDelegate 33 | func newEventAdded(sender: AddNewViewController, isDebt: Bool, isReceivable: Bool) { 34 | guard isDebt == true else { 35 | return 36 | } 37 | if creditorTable != nil { 38 | creditorTable.reloadData() 39 | } 40 | } 41 | 42 | 43 | @IBAction func removeCreditor(sender: AnyObject) { 44 | 45 | let selected = creditorController.arrangedObjects as! [Ower] 46 | guard selected.count > 0 else { 47 | return 48 | } 49 | // Only one ower can be selected. 50 | moc.deleteObject(selected[0]) 51 | do { 52 | try moc.save() 53 | } catch { 54 | print("Error while removing Ower") 55 | } 56 | 57 | delegate?.debtOrCreditorDeleted(self) 58 | 59 | if creditorController.arrangedObjects.count < 1 { 60 | removeCreditorButton.enabled = false 61 | removeDebtButton.enabled = false 62 | } 63 | } 64 | 65 | @IBAction func removeDebt(sender: AnyObject) { 66 | let selections = debtTable.selectedRowIndexes 67 | guard selections.count > 0 else { 68 | return 69 | } 70 | 71 | for item in selections { 72 | let obj = debtController.arrangedObjects[item] as! Event 73 | moc.deleteObject(obj) 74 | } 75 | 76 | do { 77 | try moc.save() 78 | } catch { 79 | print("Error while saving after removing debts") 80 | } 81 | 82 | // Update totalcolumn 83 | let columns = creditorTable.tableColumns as [NSTableColumn] 84 | for index in 0.. NSTableRowView? { 97 | return SelectedRowView() 98 | } 99 | 100 | func tableViewSelectionDidChange(notification: NSNotification) { 101 | if creditorTable.selectedRowIndexes.count > 0 { removeCreditorButton.enabled = true } 102 | else { removeCreditorButton.enabled = false } 103 | 104 | if debtTable.selectedRowIndexes.count > 0 { removeDebtButton.enabled = true } 105 | else { removeDebtButton.enabled = false } 106 | 107 | } 108 | } 109 | 110 | -------------------------------------------------------------------------------- /Debter/Debter.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | Debter.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Debter/Debter.xcdatamodeld/Debter.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Debter/Event+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Event+CoreDataProperties.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | // Delete this file and regenerate it using "Create NSManagedObject Subclass…" 9 | // to keep your implementation up to date with your model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension Event { 16 | 17 | @NSManaged var date: NSDate 18 | @NSManaged var desc: String? 19 | @NSManaged var sum: NSNumber 20 | @NSManaged var creditor: Creditor? 21 | @NSManaged var ower: Ower? 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Debter/Event.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Event.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | @objc(Event) 13 | class Event: NSManagedObject { 14 | 15 | // Insert code here to add functionality to your managed object subclass 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Debter/Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Helper.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 10.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class Helper: NSObject { 12 | 13 | class func insertManagedObject(className: String, moc: NSManagedObjectContext) -> AnyObject { 14 | let managedObject = NSEntityDescription.insertNewObjectForEntityForName(className, inManagedObjectContext: moc) as NSManagedObject 15 | return managedObject 16 | } 17 | 18 | class func fetchEntities(className: String, predicate: NSPredicate?, moc: NSManagedObjectContext) -> [AnyObject] { 19 | 20 | let request = NSFetchRequest(entityName: className) 21 | 22 | if predicate != nil { 23 | request.predicate = predicate 24 | } 25 | 26 | request.returnsObjectsAsFaults = false 27 | 28 | var results = [AnyObject]() 29 | 30 | do { 31 | results = try moc.executeFetchRequest(request) 32 | return results 33 | } catch { 34 | print("Error when fetching") 35 | } 36 | 37 | return results 38 | } 39 | 40 | class func removeSelectedPerson(table: NSTableView, controller: NSArrayController, context: NSManagedObjectContext, button: NSButton) { 41 | 42 | let num = table.selectedRow 43 | guard num > -1 else { 44 | return 45 | } 46 | 47 | let person = controller.arrangedObjects[num] as! NSManagedObject 48 | context.deleteObject(person) 49 | 50 | do { 51 | try context.save() 52 | } catch { 53 | print("Error while saving after removing persons!") 54 | } 55 | 56 | controller.selectPrevious(self) 57 | if table.selectedColumnIndexes.count < 1 { button.enabled = false } 58 | } 59 | 60 | class func removeSelectedEvents(personTable: NSTableView, eventTable: NSTableView, controller: NSArrayController, context: NSManagedObjectContext, button: NSButton) { 61 | 62 | let selections = eventTable.selectedRowIndexes 63 | guard selections.count > 0 else { 64 | return 65 | } 66 | 67 | for item in selections { 68 | let obj = controller.arrangedObjects[item] as! NSManagedObject 69 | context.deleteObject(obj) 70 | } 71 | 72 | do { 73 | try context.save() 74 | } catch { 75 | print("Error while saving after removing events") 76 | } 77 | 78 | // Update totalcolums 79 | let columns = personTable.tableColumns as [NSTableColumn] 80 | for i in 0.. 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015 Samu Tuominen. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Debter/MainWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainWindowController.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MainWindowController: NSWindowController, EventAddedDelegate, DebtDelegate, ReceivableDelegate { 12 | 13 | @IBOutlet weak var segmentControl: NSSegmentedControl! // their debts = 0, my debts = 1 14 | 15 | // I have only two tabs and I think it's easier just changing windows contentViewController's than using TabViewController, especially when I working with NSVisualEffectView. 16 | private var recsVC: ReceivablesViewController? 17 | private var debtVC: DebtViewController? 18 | 19 | // ManagedObjectContext 20 | lazy var moc = CoreDataStackManager.sharedManager.managedObjectContext 21 | 22 | // For selected segment, 0 = Receivables, 1 = Debts 23 | private var current = 0 24 | 25 | override func windowDidLoad() { 26 | super.windowDidLoad() 27 | 28 | window?.appearance = NSAppearance(named: NSAppearanceNameVibrantDark) 29 | 30 | recsVC = storyboard?.instantiateControllerWithIdentifier("RecsVC") as? ReceivablesViewController 31 | debtVC = storyboard!.instantiateControllerWithIdentifier("DebtVC") as? DebtViewController 32 | 33 | debtVC?.delegate = self 34 | recsVC?.delegate = self 35 | 36 | window?.contentViewController = recsVC 37 | 38 | calculateTotalsForWindowsTitle() 39 | 40 | } 41 | 42 | override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) { 43 | guard segue.identifier == "addNewEventSegue" else { 44 | return 45 | } 46 | let addNewVC = segue.destinationController as! AddNewViewController 47 | addNewVC.titleDelegate = self 48 | addNewVC.debtDelegate = debtVC 49 | addNewVC.recDelegate = recsVC 50 | } 51 | 52 | // Keyboard shortcuts for Command + 1 & Command + 2 53 | override func keyDown(theEvent: NSEvent) { 54 | if theEvent.keyCode == 18 && current != 0 { 55 | changeViewToRecs() 56 | segmentControl.setSelected(true, forSegment: 0) 57 | segmentControl.setSelected(false, forSegment: 1) 58 | } 59 | if theEvent.keyCode == 19 && current != 1 { 60 | changeViewToDebt() 61 | segmentControl.setSelected(false, forSegment: 0) 62 | segmentControl.setSelected(true, forSegment: 1) 63 | } 64 | } 65 | 66 | private func changeViewToDebt() { 67 | debtVC?.view.frame = (contentViewController?.view.frame)! 68 | contentViewController = debtVC 69 | current = 1 70 | } 71 | 72 | private func changeViewToRecs() { 73 | recsVC?.view.frame = (contentViewController?.view.frame)! 74 | contentViewController = recsVC 75 | current = 0 76 | } 77 | 78 | // Delegates 79 | func newEventAdded(sender: AddNewViewController, isDebt: Bool, isReceivable: Bool) { 80 | calculateTotalsForWindowsTitle() 81 | } 82 | 83 | func debtOrCreditorDeleted(sender: DebtViewController) { 84 | calculateTotalsForWindowsTitle() 85 | } 86 | 87 | func recOrOwerDeleted(sender: ReceivablesViewController) { 88 | calculateTotalsForWindowsTitle() 89 | } 90 | 91 | @IBAction func selectedSegmentChanged(sender: AnyObject) { 92 | guard current != segmentControl.selectedSegment else { 93 | return 94 | } 95 | if segmentControl.selectedSegment == 0 { changeViewToRecs() } 96 | else if segmentControl.selectedSegment == 1 { changeViewToDebt() } 97 | else { print("This should never be executed!") } 98 | } 99 | 100 | func calculateTotalsForWindowsTitle() { 101 | 102 | let personsForMyDebts = Helper.fetchEntities("Creditor", predicate: nil, moc: moc) 103 | let personsForTheirDebts = Helper.fetchEntities("Ower", predicate: nil, moc: moc) 104 | 105 | var debtsTotal = 0.0 106 | var recsTotal = 0.0 107 | 108 | for person in personsForMyDebts as! [Creditor]{ 109 | let events = person.events.allObjects as! [Event] 110 | 111 | for event in events { 112 | debtsTotal += event.sum.doubleValue 113 | } 114 | } 115 | 116 | for person in personsForTheirDebts as! [Ower]{ 117 | let events = person.events.allObjects as! [Event] 118 | 119 | for event in events { 120 | recsTotal += event.sum.doubleValue 121 | } 122 | } 123 | 124 | let formatter = NSNumberFormatter() 125 | formatter.numberStyle = .CurrencyStyle 126 | 127 | window?.title = "Their debts total: \(formatter.stringFromNumber(recsTotal)!) - My debts total: \(formatter.stringFromNumber(debtsTotal)!)" 128 | 129 | } 130 | 131 | } 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /Debter/Ower+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ower+CoreDataProperties.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | // Delete this file and regenerate it using "Create NSManagedObject Subclass…" 9 | // to keep your implementation up to date with your model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension Ower { 16 | 17 | @NSManaged var name: String 18 | @NSManaged var events: NSSet 19 | 20 | var amount: Double { 21 | let allEvents = self.events.allObjects as! [Event] 22 | var sum = 0.0 23 | for i in allEvents { 24 | sum += i.sum.doubleValue 25 | } 26 | return sum 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Debter/Ower.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ower.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | @objc(Ower) 13 | class Ower: NSManagedObject { 14 | 15 | // Insert code here to add functionality to your managed object subclass 16 | } 17 | -------------------------------------------------------------------------------- /Debter/PersonViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HenkiloSolu.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PersonViewCell: NSView { 12 | 13 | @IBOutlet weak var sumField: NSTextField! 14 | @IBOutlet weak var nameField: NSTextField! 15 | 16 | var name: String { 17 | get { 18 | return nameField.stringValue 19 | } set { 20 | nameField.stringValue = newValue 21 | } 22 | } 23 | 24 | var sum: Double { 25 | get { 26 | return sumField.doubleValue 27 | } set { 28 | sumField.doubleValue = newValue 29 | } 30 | } 31 | 32 | override func drawRect(dirtyRect: NSRect) { 33 | super.drawRect(dirtyRect) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Debter/ReceivablesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReceivablesViewController.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @objc protocol ReceivableDelegate { 12 | func recOrOwerDeleted(sender: ReceivablesViewController) 13 | } 14 | 15 | class ReceivablesViewController: NSViewController, EventAddedDelegate { 16 | 17 | @IBOutlet weak var owerTable: VibrancyTable! 18 | @IBOutlet weak var incomeTable: VibrancyTable! 19 | @IBOutlet weak var removeOwerButton: NSButton! 20 | @IBOutlet weak var removeEventButton: NSButton! 21 | @IBOutlet weak var recsController: NSArrayController! 22 | @IBOutlet weak var owerController: NSArrayController! 23 | 24 | lazy var moc = CoreDataStackManager.sharedManager.managedObjectContext 25 | 26 | weak var delegate: ReceivableDelegate? 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | } 31 | 32 | // EventAddedDelegate 33 | func newEventAdded(sender: AddNewViewController, isDebt: Bool, isReceivable: Bool) { 34 | guard isReceivable == true else { 35 | return 36 | } 37 | owerTable.reloadData() 38 | } 39 | 40 | @IBAction func removeOwer(sender: AnyObject) { 41 | let selected = owerController.arrangedObjects as! [Ower] 42 | guard selected.count > 0 else { 43 | return 44 | } 45 | // Only one ower can be selected. 46 | moc.deleteObject(selected[0]) 47 | do { 48 | try moc.save() 49 | } catch { 50 | print("Error while removing Ower") 51 | } 52 | 53 | delegate?.recOrOwerDeleted(self) 54 | 55 | if owerController.arrangedObjects.count < 1 { 56 | removeOwerButton.enabled = false 57 | removeEventButton.enabled = false 58 | } 59 | 60 | } 61 | 62 | @IBAction func removeEvent(sender: AnyObject) { 63 | Helper.removeSelectedEvents(owerTable, eventTable: incomeTable, controller: recsController, context: moc, button: removeEventButton) 64 | delegate?.recOrOwerDeleted(self) 65 | if incomeTable.selectedRowIndexes.count < 1 { removeEventButton.enabled = false } 66 | } 67 | } 68 | 69 | // MARK: NSTableViewDelegate 70 | extension ReceivablesViewController: NSTableViewDelegate { 71 | 72 | func tableView(tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { 73 | return SelectedRowView() 74 | } 75 | 76 | func tableViewSelectionDidChange(notification: NSNotification) { 77 | 78 | if owerTable.selectedRowIndexes.count > 0 { removeOwerButton.enabled = true } 79 | else { removeOwerButton.enabled = false } 80 | 81 | if incomeTable.selectedRowIndexes.count > 0 { removeEventButton.enabled = true } 82 | else { removeEventButton.enabled = false } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Debter/SelectedRowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectedRowView.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /// By default, XCode gives second selection color too white, so user can't see text anymore. That's why I sublassed NSTableRowView and made both selection colors blue. 12 | class SelectedRowView: NSTableRowView { 13 | 14 | override func drawRect(dirtyRect: NSRect) { 15 | super.drawRect(dirtyRect) 16 | 17 | if selected { 18 | NSColor(SRGBRed: 75/255, green: 109/255, blue: 255/255, alpha: 1).set() 19 | NSRectFill(dirtyRect) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Debter/VibrancyTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SaatavaTaulukko.swift 3 | // Debter 4 | // 5 | // Created by Samu Tuominen on 11.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class VibrancyTable: NSTableView { 12 | // Just overriding this method so I get that nice alternative row colors by default. 13 | override var allowsVibrancy: Bool { return true } 14 | } 15 | -------------------------------------------------------------------------------- /DebterTests/DebterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DebterTests.swift 3 | // DebterTests 4 | // 5 | // Created by Samu Tuominen on 10.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class DebterTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | // Use XCTAssert and related functions to verify your tests produce the correct results. 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /DebterTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /DebterUITests/DebterUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DebterUITests.swift 3 | // DebterUITests 4 | // 5 | // Created by Samu Tuominen on 10.6.2015. 6 | // Copyright © 2015 Samu Tuominen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | class DebterUITests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | 19 | // In UI tests it is usually best to stop immediately when a failure occurs. 20 | continueAfterFailure = false 21 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 22 | XCUIApplication().launch() 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | super.tearDown() 28 | } 29 | 30 | func testExample() { 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /DebterUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Easy way to track your personal debts. 3 | 4 | ![alt screenshot](https://i.gyazo.com/8a52edb9152c5c91ec20265d142491dd.gif) 5 | 6 | ## TODO 7 | - Better english at least for window's title. 8 | - Maybe NSAlert if required textfield are empty when adding new events. 9 | --------------------------------------------------------------------------------