├── .gitignore ├── CloudStatus.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Interface ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── 1024.png │ │ ├── 128-2.png │ │ ├── 16.png │ │ ├── 256-1.png │ │ ├── 256.png │ │ ├── 32-1.png │ │ ├── 32.png │ │ ├── 512-1.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── Contents.json │ ├── Cloud-Download.imageset │ │ ├── Contents.json │ │ ├── cloud-download.png │ │ └── cloud-download@2x.png │ ├── Cloud-Unknown.imageset │ │ ├── Contents.json │ │ ├── cloud-unknown.png │ │ └── cloud-unknown@2x.png │ ├── Cloud-UpToDate.imageset │ │ ├── Contents.json │ │ ├── cloud.png │ │ └── cloud@2x.png │ ├── Cloud-Upload.imageset │ │ ├── Contents.json │ │ ├── cloud-upload.png │ │ └── cloud-upload@2x.png │ ├── Contents.json │ ├── Menu-Download.imageset │ │ ├── Contents.json │ │ ├── down@1x.png │ │ └── down@2x.png │ └── Menu-Upload.imageset │ │ ├── Contents.json │ │ ├── up@1x.png │ │ └── up@2x.png └── Base.lproj │ └── Main.storyboard ├── LICENSE ├── README.md └── Source ├── Controller ├── AppDelegate.swift └── StatusItemController.swift ├── Info.plist └── Model ├── BrctlAppStatus.swift ├── BrctlStatus.swift ├── NSTaskExtensions.swift └── TransferStatus.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/screenshots 64 | -------------------------------------------------------------------------------- /CloudStatus.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 790E27771C5E749B00CDD8E0 /* BrctlAppStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790E27761C5E749B00CDD8E0 /* BrctlAppStatus.swift */; }; 11 | 790E27791C5E75D100CDD8E0 /* TransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790E27781C5E75D100CDD8E0 /* TransferStatus.swift */; }; 12 | 793490821C2EF6A800B4C67B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7934907F1C2EF6A800B4C67B /* AppDelegate.swift */; }; 13 | 7934908D1C2EF70400B4C67B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7934908A1C2EF70400B4C67B /* Assets.xcassets */; }; 14 | 793490911C2EF77C00B4C67B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7934908F1C2EF77C00B4C67B /* Main.storyboard */; }; 15 | 793490931C2EF88A00B4C67B /* BrctlStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793490921C2EF88A00B4C67B /* BrctlStatus.swift */; }; 16 | 79D65D841C3878170047A5ED /* NSTaskExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79D65D831C3878170047A5ED /* NSTaskExtensions.swift */; }; 17 | 79D65D871C387BC30047A5ED /* StatusItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79D65D851C387A580047A5ED /* StatusItemController.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 790E27761C5E749B00CDD8E0 /* BrctlAppStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrctlAppStatus.swift; sourceTree = ""; }; 22 | 790E27781C5E75D100CDD8E0 /* TransferStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferStatus.swift; sourceTree = ""; }; 23 | 793490491C2EF45700B4C67B /* CloudStatus.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CloudStatus.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 793490791C2EF67B00B4C67B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 25 | 7934907F1C2EF6A800B4C67B /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | 7934908A1C2EF70400B4C67B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 793490901C2EF77C00B4C67B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 793490921C2EF88A00B4C67B /* BrctlStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrctlStatus.swift; sourceTree = ""; }; 29 | 79D65D831C3878170047A5ED /* NSTaskExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTaskExtensions.swift; sourceTree = ""; }; 30 | 79D65D851C387A580047A5ED /* StatusItemController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemController.swift; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 793490461C2EF45700B4C67B /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 793490401C2EF45700B4C67B = { 45 | isa = PBXGroup; 46 | children = ( 47 | 793490771C2EF67B00B4C67B /* Source */, 48 | 793490891C2EF70400B4C67B /* Interface */, 49 | 7934904A1C2EF45700B4C67B /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | 7934904A1C2EF45700B4C67B /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 793490491C2EF45700B4C67B /* CloudStatus.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | 793490771C2EF67B00B4C67B /* Source */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 7934907E1C2EF6A800B4C67B /* Controller */, 65 | 793490811C2EF6A800B4C67B /* Model */, 66 | 793490791C2EF67B00B4C67B /* Info.plist */, 67 | ); 68 | path = Source; 69 | sourceTree = ""; 70 | }; 71 | 7934907E1C2EF6A800B4C67B /* Controller */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 7934907F1C2EF6A800B4C67B /* AppDelegate.swift */, 75 | 79D65D851C387A580047A5ED /* StatusItemController.swift */, 76 | ); 77 | path = Controller; 78 | sourceTree = ""; 79 | }; 80 | 793490811C2EF6A800B4C67B /* Model */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 793490921C2EF88A00B4C67B /* BrctlStatus.swift */, 84 | 790E27761C5E749B00CDD8E0 /* BrctlAppStatus.swift */, 85 | 790E27781C5E75D100CDD8E0 /* TransferStatus.swift */, 86 | 79D65D831C3878170047A5ED /* NSTaskExtensions.swift */, 87 | ); 88 | path = Model; 89 | sourceTree = ""; 90 | }; 91 | 793490891C2EF70400B4C67B /* Interface */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 7934908A1C2EF70400B4C67B /* Assets.xcassets */, 95 | 7934908F1C2EF77C00B4C67B /* Main.storyboard */, 96 | ); 97 | path = Interface; 98 | sourceTree = ""; 99 | }; 100 | /* End PBXGroup section */ 101 | 102 | /* Begin PBXNativeTarget section */ 103 | 793490481C2EF45700B4C67B /* CloudStatus */ = { 104 | isa = PBXNativeTarget; 105 | buildConfigurationList = 7934906E1C2EF45700B4C67B /* Build configuration list for PBXNativeTarget "CloudStatus" */; 106 | buildPhases = ( 107 | 793490451C2EF45700B4C67B /* Sources */, 108 | 793490461C2EF45700B4C67B /* Frameworks */, 109 | 793490471C2EF45700B4C67B /* Resources */, 110 | ); 111 | buildRules = ( 112 | ); 113 | dependencies = ( 114 | ); 115 | name = CloudStatus; 116 | productName = CloudStatus; 117 | productReference = 793490491C2EF45700B4C67B /* CloudStatus.app */; 118 | productType = "com.apple.product-type.application"; 119 | }; 120 | /* End PBXNativeTarget section */ 121 | 122 | /* Begin PBXProject section */ 123 | 793490411C2EF45700B4C67B /* Project object */ = { 124 | isa = PBXProject; 125 | attributes = { 126 | LastSwiftUpdateCheck = 0720; 127 | LastUpgradeCheck = 0720; 128 | ORGANIZATIONNAME = "Friedrich Gräter"; 129 | TargetAttributes = { 130 | 793490481C2EF45700B4C67B = { 131 | CreatedOnToolsVersion = 7.2; 132 | }; 133 | }; 134 | }; 135 | buildConfigurationList = 793490441C2EF45700B4C67B /* Build configuration list for PBXProject "CloudStatus" */; 136 | compatibilityVersion = "Xcode 3.2"; 137 | developmentRegion = English; 138 | hasScannedForEncodings = 0; 139 | knownRegions = ( 140 | en, 141 | Base, 142 | ); 143 | mainGroup = 793490401C2EF45700B4C67B; 144 | productRefGroup = 7934904A1C2EF45700B4C67B /* Products */; 145 | projectDirPath = ""; 146 | projectRoot = ""; 147 | targets = ( 148 | 793490481C2EF45700B4C67B /* CloudStatus */, 149 | ); 150 | }; 151 | /* End PBXProject section */ 152 | 153 | /* Begin PBXResourcesBuildPhase section */ 154 | 793490471C2EF45700B4C67B /* Resources */ = { 155 | isa = PBXResourcesBuildPhase; 156 | buildActionMask = 2147483647; 157 | files = ( 158 | 793490911C2EF77C00B4C67B /* Main.storyboard in Resources */, 159 | 7934908D1C2EF70400B4C67B /* Assets.xcassets in Resources */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXResourcesBuildPhase section */ 164 | 165 | /* Begin PBXSourcesBuildPhase section */ 166 | 793490451C2EF45700B4C67B /* Sources */ = { 167 | isa = PBXSourcesBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | 790E27791C5E75D100CDD8E0 /* TransferStatus.swift in Sources */, 171 | 79D65D871C387BC30047A5ED /* StatusItemController.swift in Sources */, 172 | 790E27771C5E749B00CDD8E0 /* BrctlAppStatus.swift in Sources */, 173 | 79D65D841C3878170047A5ED /* NSTaskExtensions.swift in Sources */, 174 | 793490821C2EF6A800B4C67B /* AppDelegate.swift in Sources */, 175 | 793490931C2EF88A00B4C67B /* BrctlStatus.swift in Sources */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXSourcesBuildPhase section */ 180 | 181 | /* Begin PBXVariantGroup section */ 182 | 7934908F1C2EF77C00B4C67B /* Main.storyboard */ = { 183 | isa = PBXVariantGroup; 184 | children = ( 185 | 793490901C2EF77C00B4C67B /* Base */, 186 | ); 187 | name = Main.storyboard; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXVariantGroup section */ 191 | 192 | /* Begin XCBuildConfiguration section */ 193 | 7934906C1C2EF45700B4C67B /* Debug */ = { 194 | isa = XCBuildConfiguration; 195 | buildSettings = { 196 | ALWAYS_SEARCH_USER_PATHS = NO; 197 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 198 | CLANG_CXX_LIBRARY = "libc++"; 199 | CLANG_ENABLE_MODULES = YES; 200 | CLANG_ENABLE_OBJC_ARC = YES; 201 | CLANG_WARN_BOOL_CONVERSION = YES; 202 | CLANG_WARN_CONSTANT_CONVERSION = YES; 203 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 204 | CLANG_WARN_EMPTY_BODY = YES; 205 | CLANG_WARN_ENUM_CONVERSION = YES; 206 | CLANG_WARN_INT_CONVERSION = YES; 207 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 208 | CLANG_WARN_UNREACHABLE_CODE = YES; 209 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 210 | CODE_SIGN_IDENTITY = "-"; 211 | COPY_PHASE_STRIP = NO; 212 | DEBUG_INFORMATION_FORMAT = dwarf; 213 | ENABLE_STRICT_OBJC_MSGSEND = YES; 214 | ENABLE_TESTABILITY = YES; 215 | GCC_C_LANGUAGE_STANDARD = gnu99; 216 | GCC_DYNAMIC_NO_PIC = NO; 217 | GCC_NO_COMMON_BLOCKS = YES; 218 | GCC_OPTIMIZATION_LEVEL = 0; 219 | GCC_PREPROCESSOR_DEFINITIONS = ( 220 | "DEBUG=1", 221 | "$(inherited)", 222 | ); 223 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 224 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 225 | GCC_WARN_UNDECLARED_SELECTOR = YES; 226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 227 | GCC_WARN_UNUSED_FUNCTION = YES; 228 | GCC_WARN_UNUSED_VARIABLE = YES; 229 | MACOSX_DEPLOYMENT_TARGET = 10.11; 230 | MTL_ENABLE_DEBUG_INFO = YES; 231 | ONLY_ACTIVE_ARCH = YES; 232 | SDKROOT = macosx; 233 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 234 | }; 235 | name = Debug; 236 | }; 237 | 7934906D1C2EF45700B4C67B /* Release */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ALWAYS_SEARCH_USER_PATHS = NO; 241 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 242 | CLANG_CXX_LIBRARY = "libc++"; 243 | CLANG_ENABLE_MODULES = YES; 244 | CLANG_ENABLE_OBJC_ARC = YES; 245 | CLANG_WARN_BOOL_CONVERSION = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 248 | CLANG_WARN_EMPTY_BODY = YES; 249 | CLANG_WARN_ENUM_CONVERSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 252 | CLANG_WARN_UNREACHABLE_CODE = YES; 253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 254 | CODE_SIGN_IDENTITY = "-"; 255 | COPY_PHASE_STRIP = NO; 256 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 257 | ENABLE_NS_ASSERTIONS = NO; 258 | ENABLE_STRICT_OBJC_MSGSEND = YES; 259 | GCC_C_LANGUAGE_STANDARD = gnu99; 260 | GCC_NO_COMMON_BLOCKS = YES; 261 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 262 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 263 | GCC_WARN_UNDECLARED_SELECTOR = YES; 264 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 265 | GCC_WARN_UNUSED_FUNCTION = YES; 266 | GCC_WARN_UNUSED_VARIABLE = YES; 267 | MACOSX_DEPLOYMENT_TARGET = 10.11; 268 | MTL_ENABLE_DEBUG_INFO = NO; 269 | SDKROOT = macosx; 270 | }; 271 | name = Release; 272 | }; 273 | 7934906F1C2EF45700B4C67B /* Debug */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 277 | COMBINE_HIDPI_IMAGES = YES; 278 | INFOPLIST_FILE = Source/Info.plist; 279 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 280 | PRODUCT_BUNDLE_IDENTIFIER = net.hydrixos.cloudstatus; 281 | PRODUCT_NAME = "$(TARGET_NAME)"; 282 | }; 283 | name = Debug; 284 | }; 285 | 793490701C2EF45700B4C67B /* Release */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | COMBINE_HIDPI_IMAGES = YES; 290 | INFOPLIST_FILE = Source/Info.plist; 291 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 292 | PRODUCT_BUNDLE_IDENTIFIER = net.hydrixos.cloudstatus; 293 | PRODUCT_NAME = "$(TARGET_NAME)"; 294 | }; 295 | name = Release; 296 | }; 297 | /* End XCBuildConfiguration section */ 298 | 299 | /* Begin XCConfigurationList section */ 300 | 793490441C2EF45700B4C67B /* Build configuration list for PBXProject "CloudStatus" */ = { 301 | isa = XCConfigurationList; 302 | buildConfigurations = ( 303 | 7934906C1C2EF45700B4C67B /* Debug */, 304 | 7934906D1C2EF45700B4C67B /* Release */, 305 | ); 306 | defaultConfigurationIsVisible = 0; 307 | defaultConfigurationName = Release; 308 | }; 309 | 7934906E1C2EF45700B4C67B /* Build configuration list for PBXNativeTarget "CloudStatus" */ = { 310 | isa = XCConfigurationList; 311 | buildConfigurations = ( 312 | 7934906F1C2EF45700B4C67B /* Debug */, 313 | 793490701C2EF45700B4C67B /* Release */, 314 | ); 315 | defaultConfigurationIsVisible = 0; 316 | defaultConfigurationName = Release; 317 | }; 318 | /* End XCConfigurationList section */ 319 | }; 320 | rootObject = 793490411C2EF45700B4C67B /* Project object */; 321 | } 322 | -------------------------------------------------------------------------------- /CloudStatus.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/128-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/128-2.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/256-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/256-1.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/32-1.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/512-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/512-1.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "32-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "128-2.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "256-1.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "512-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Download.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cloud-download.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "cloud-download@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Download.imageset/cloud-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-Download.imageset/cloud-download.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Download.imageset/cloud-download@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-Download.imageset/cloud-download@2x.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Unknown.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cloud-unknown.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "cloud-unknown@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Unknown.imageset/cloud-unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-Unknown.imageset/cloud-unknown.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Unknown.imageset/cloud-unknown@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-Unknown.imageset/cloud-unknown@2x.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-UpToDate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cloud.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "cloud@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-UpToDate.imageset/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-UpToDate.imageset/cloud.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-UpToDate.imageset/cloud@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-UpToDate.imageset/cloud@2x.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Upload.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cloud-upload.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "cloud-upload@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Upload.imageset/cloud-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-Upload.imageset/cloud-upload.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Cloud-Upload.imageset/cloud-upload@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Cloud-Upload.imageset/cloud-upload@2x.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Menu-Download.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "down@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "down@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Menu-Download.imageset/down@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Menu-Download.imageset/down@1x.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Menu-Download.imageset/down@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Menu-Download.imageset/down@2x.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Menu-Upload.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "up@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "up@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Menu-Upload.imageset/up@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Menu-Upload.imageset/up@1x.png -------------------------------------------------------------------------------- /Interface/Assets.xcassets/Menu-Upload.imageset/up@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydrixos/cloudstatus/1ab5d0d794e6364db1fff343fb86422277d06cdd/Interface/Assets.xcassets/Menu-Upload.imageset/up@2x.png -------------------------------------------------------------------------------- /Interface/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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Friedrich Gräter 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloud Status 2 | A simple menubar status item tracking your iCloud Drive uploading / downloading status globally and per app. 3 | 4 | Icons generated from Font Awesome by Dave Gandy - http://fontawesome.io. If you are a gifted icon designer: A better app icon and an icon for bidirectional transfers are highly appreciated ;). 5 | 6 | ## Binary 7 | Recent builds can be found here: https://github.com/hydrixos/cloudstatus/releases 8 | 9 | ## Legal 10 | See "LICENSE" for licensing. iCloud is a registered trademark of Apple Inc. 11 | -------------------------------------------------------------------------------- /Source/Controller/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CloudStatus 4 | // 5 | // Created by Friedrich Gräter on 31/01/16. 6 | // Copyright © 2016 Friedrich Gräter. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | var statusBarItemController : StatusItemController? 15 | 16 | func applicationDidFinishLaunching(aNotification: NSNotification) { 17 | statusBarItemController = StatusItemController() 18 | } 19 | 20 | @IBAction func quit(sender: AnyObject?) { 21 | NSApplication.sharedApplication().terminate(sender) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Controller/StatusItemController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusItemController.swift 3 | // CloudStatus 4 | // 5 | // Created by Friedrich Gräter on 31/01/16. 6 | // Copyright © 2016 Friedrich Gräter. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class StatusItemController : NSObject { 12 | private let statusItem : NSStatusItem 13 | private let timerSource : dispatch_source_t 14 | 15 | override init() { 16 | statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSVariableStatusItemLength) 17 | statusItem.menu = NSMenu() 18 | statusItem.enabled = true 19 | 20 | timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()) 21 | status = BrctlStatus(appStates: []) 22 | 23 | super.init() 24 | 25 | // Setup menu 26 | statusItem.target = self 27 | 28 | // Set up timer 29 | dispatch_source_set_timer(timerSource, DISPATCH_TIME_NOW, NSEC_PER_SEC, NSEC_PER_SEC) 30 | dispatch_source_set_event_handler(timerSource) { [weak self] in 31 | BrctlStatus.statusWithCompletionHandler { status in 32 | self?.status = status 33 | } 34 | } 35 | dispatch_resume(timerSource) 36 | } 37 | 38 | deinit { 39 | dispatch_source_cancel(timerSource) 40 | } 41 | 42 | private(set) var status : BrctlStatus { 43 | didSet { 44 | statusItem.image = status.transferStatus.statusItemImage 45 | 46 | guard let menu = statusItem.menu else { 47 | return; 48 | } 49 | 50 | // Remove outdated menu entries 51 | menu.removeAllItems() 52 | 53 | // Add new menu items 54 | for appStatus in status.appStates { 55 | if ((appStatus.transferStatus == .Unknown) || (appStatus.transferStatus == .UpToDate)) { 56 | continue; 57 | } 58 | 59 | var loadingStatusTitles : [String] = [] 60 | if appStatus.downloadingCount > 0 { 61 | loadingStatusTitles += ["\(appStatus.downloadingCount) down"] 62 | } 63 | if appStatus.uploadingCount > 0 { 64 | loadingStatusTitles += ["\(appStatus.uploadingCount) up"] 65 | } 66 | 67 | let appTitle = appStatus.appName + " (\(loadingStatusTitles.joinWithSeparator("; ")))" 68 | 69 | let newItem = NSMenuItem(title: appTitle, action: nil, keyEquivalent: "") 70 | newItem.enabled = false 71 | newItem.image = status.transferStatus.menuImage 72 | 73 | menu.addItem(newItem) 74 | } 75 | 76 | // Add separator before "quit" item 77 | if (menu.itemArray.count > 0) { 78 | menu.addItem(NSMenuItem.separatorItem()) 79 | } 80 | 81 | // Add other menu items 82 | statusItem.menu?.addItemWithTitle("About", action: "orderFrontStandardAboutPanel:", keyEquivalent: "") 83 | statusItem.menu?.addItemWithTitle("Quit", action: "quit:", keyEquivalent: "") 84 | } 85 | } 86 | } 87 | 88 | extension TransferStatus { 89 | var statusItemImage : NSImage? { 90 | switch(self) { 91 | case .Unknown: 92 | return NSImage(named: "Cloud-Unknown") 93 | case .UpToDate: 94 | return NSImage(named: "Cloud-UpToDate") 95 | case .Uploading: 96 | return NSImage(named: "Cloud-Upload") 97 | case .Downloading: 98 | return NSImage(named: "Cloud-Download") 99 | case .Bidirectional: 100 | return NSImage(named: "Cloud-Upload") 101 | } 102 | } 103 | 104 | var menuImage : NSImage? { 105 | switch(self) { 106 | case .Unknown, .UpToDate: 107 | return nil 108 | case .Uploading: 109 | return NSImage(named: "Menu-Upload") 110 | case .Downloading: 111 | return NSImage(named: "Menu-Download") 112 | case .Bidirectional: 113 | return NSImage(named: "Menu-Upload") 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Source/Info.plist: -------------------------------------------------------------------------------- 1 | 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 | 0.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2016 Friedrich Gräter 31 | NSMainStoryboardFile 32 | Main 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /Source/Model/BrctlAppStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BrctlAppStatus.swift 3 | // CloudStatus 4 | // 5 | // Created by Friedrich Gräter on 31/01/16. 6 | // Copyright © 2016 Friedrich Gräter. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct BrctlAppStatus { 12 | let appName : String 13 | 14 | var uploadingCount : UInt = 0 15 | var downloadingCount : UInt = 0 16 | 17 | init(appName: String) { 18 | self.appName = appName 19 | } 20 | 21 | var transferStatus : TransferStatus { 22 | get { 23 | let isDownloading = self.downloadingCount > 0 24 | let isUploading = self.uploadingCount > 0 25 | 26 | if (isDownloading && isUploading) { 27 | return .Bidirectional 28 | } 29 | else if (isDownloading) { 30 | return .Downloading 31 | } 32 | else if (isUploading) { 33 | return .Uploading 34 | } 35 | 36 | return .UpToDate 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Model/BrctlStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BrctlStatus 3 | // CloudStatus 4 | // 5 | // Created by Friedrich Gräter on 31/01/16. 6 | // Copyright © 2016 Friedrich Gräter. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct BrctlStatus { 12 | let appStates : [BrctlAppStatus] 13 | let transferStatus : TransferStatus 14 | 15 | init(appStates: [BrctlAppStatus]) { 16 | self.appStates = appStates 17 | var transferStatus = TransferStatus.UpToDate 18 | 19 | for appState in appStates { 20 | transferStatus = appState.transferStatus.combineWithTransferStatus(transferStatus) 21 | } 22 | 23 | self.transferStatus = transferStatus 24 | } 25 | 26 | static func statusWithCompletionHandler(completionHandler: BrctlStatus -> Void) { 27 | NSTask.runTaskWithLaunchPath("/usr/bin/brctl", arguments: ["status"]) { (task, output) -> Void in 28 | completionHandler(BrctlStatus(appStates: parseAppStatesFromOutput(output))) 29 | } 30 | } 31 | 32 | private static func parseAppStatesFromOutput(output: String) -> [BrctlAppStatus] { 33 | var appStates : [BrctlAppStatus] = [] 34 | 35 | var lastAppStatus : BrctlAppStatus? 36 | 37 | output.enumerateLines { (line, stop) -> () in 38 | // Start new app status 39 | if line.hasPrefix("<") { 40 | guard let nameRange = line.rangeOfString("[a-zA-Z0-9.]+", options: .RegularExpressionSearch, range: Range(start: line.startIndex.advancedBy(1), end: line.endIndex)) else { 41 | return; 42 | } 43 | 44 | let currentAppIdentifier = line.substringWithRange(nameRange) 45 | if (currentAppIdentifier.characters.count == 0) { 46 | return; 47 | } 48 | 49 | let currentAppName = currentAppIdentifier.componentsSeparatedByString(".").last!.capitalizedString 50 | 51 | if let lastAppStatus = lastAppStatus { 52 | appStates.append(lastAppStatus) 53 | } 54 | 55 | lastAppStatus = BrctlAppStatus(appName: currentAppName) 56 | } 57 | 58 | // Increment download counter 59 | if line.rangeOfString("\\s*\\> download", options: .RegularExpressionSearch) != nil { 60 | lastAppStatus?.downloadingCount += 1 61 | } 62 | 63 | // Increment upload counter 64 | if line.rangeOfString("\\s*\\> upload", options: .RegularExpressionSearch) != nil { 65 | lastAppStatus?.uploadingCount += 1 66 | } 67 | } 68 | 69 | return appStates 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Source/Model/NSTaskExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSTaskExtensions.swift 3 | // CloudStatus 4 | // 5 | // Created by Friedrich Gräter on 31/01/16. 6 | // Copyright © 2016 Friedrich Gräter. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSTask { 12 | class func runTaskWithLaunchPath(launchPath: String, arguments: [String], completionHandler: (NSTask, String) -> Void) { 13 | // Setup task 14 | let task = NSTask() 15 | task.launchPath = launchPath 16 | task.arguments = arguments 17 | 18 | // Setup output pipe 19 | let pipe = NSPipe() 20 | task.standardOutput = pipe 21 | var output = String() 22 | 23 | pipe.fileHandleForReading.readabilityHandler = { readHandle in 24 | guard let string = String(data: readHandle.availableData, encoding: NSUTF8StringEncoding) else { 25 | return; 26 | } 27 | 28 | output.appendContentsOf(string) 29 | } 30 | 31 | // Setup completion handler 32 | task.terminationHandler = { task in 33 | completionHandler(task, output) 34 | } 35 | 36 | // Start execution 37 | task.launch() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Model/TransferStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransferStatus.swift 3 | // CloudStatus 4 | // 5 | // Created by Friedrich Gräter on 31/01/16. 6 | // Copyright © 2016 Friedrich Gräter. All rights reserved. 7 | // 8 | 9 | enum TransferStatus { 10 | case UpToDate, Uploading, Downloading, Bidirectional, Unknown 11 | 12 | func combineWithTransferStatus(transferStatus: TransferStatus) -> TransferStatus { 13 | if (transferStatus == self) { 14 | return self 15 | } 16 | else if (transferStatus == .UpToDate) { 17 | return self 18 | } 19 | else if (self == .UpToDate) { 20 | return transferStatus 21 | } 22 | else { 23 | return .Bidirectional 24 | } 25 | } 26 | } 27 | --------------------------------------------------------------------------------