├── .gitignore ├── .travis.yml ├── FileWatch.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── FileWatch.xcscheme ├── FileWatch ├── FileWatch.swift └── Info.plist ├── FileWatchTests ├── FileWatchTests.swift └── Info.plist ├── LICENSE └── README.md /.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 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | 28 | ## Playgrounds 29 | timeline.xctimeline 30 | playground.xcworkspace 31 | 32 | # Swift Package Manager 33 | # 34 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 35 | # Packages/ 36 | .build/ 37 | 38 | # CocoaPods 39 | # 40 | # We recommend against adding the Pods directory to your .gitignore. However 41 | # you should judge for yourself, the pros and cons are mentioned at: 42 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 43 | # 44 | # Pods/ 45 | 46 | # Carthage 47 | # 48 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 49 | # Carthage/Checkouts 50 | 51 | Carthage/Build 52 | 53 | # fastlane 54 | # 55 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 56 | # screenshots whenever they are needed. 57 | # For more information about the recommended setup visit: 58 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 59 | 60 | fastlane/report.xml 61 | fastlane/Preview.html 62 | fastlane/screenshots 63 | fastlane/test_output 64 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9 3 | 4 | env: 5 | global: 6 | - LC_CTYPE=en_US.UTF-8 7 | 8 | before_install: 9 | - brew update 10 | 11 | script: 12 | - xcodebuild test -scheme FileWatch -configuration Release | xcpretty -c 13 | - carthage build --no-skip-current 14 | 15 | notifications: 16 | email: false 17 | -------------------------------------------------------------------------------- /FileWatch.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A4A9D9321CA6C8C500C6880A /* FileWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A9D9311CA6C8C500C6880A /* FileWatch.swift */; }; 11 | A4A9D93A1CA6C99400C6880A /* FileWatchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A9D9391CA6C99400C6880A /* FileWatchTests.swift */; }; 12 | A4A9D93C1CA6C99400C6880A /* FileWatch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4A9D9261CA6C8AA00C6880A /* FileWatch.framework */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXContainerItemProxy section */ 16 | A4A9D93D1CA6C99400C6880A /* PBXContainerItemProxy */ = { 17 | isa = PBXContainerItemProxy; 18 | containerPortal = A4A9D91D1CA6C8AA00C6880A /* Project object */; 19 | proxyType = 1; 20 | remoteGlobalIDString = A4A9D9251CA6C8AA00C6880A; 21 | remoteInfo = FileWatch; 22 | }; 23 | /* End PBXContainerItemProxy section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | A4A9D9261CA6C8AA00C6880A /* FileWatch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FileWatch.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | A4A9D92B1CA6C8AA00C6880A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | A4A9D9311CA6C8C500C6880A /* FileWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileWatch.swift; sourceTree = ""; }; 29 | A4A9D9371CA6C99400C6880A /* FileWatchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FileWatchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | A4A9D9391CA6C99400C6880A /* FileWatchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWatchTests.swift; sourceTree = ""; }; 31 | A4A9D93B1CA6C99400C6880A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | A4A9D9221CA6C8AA00C6880A /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | A4A9D9341CA6C99400C6880A /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | A4A9D93C1CA6C99400C6880A /* FileWatch.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | A4A9D91C1CA6C8AA00C6880A = { 54 | isa = PBXGroup; 55 | children = ( 56 | A4A9D9281CA6C8AA00C6880A /* FileWatch */, 57 | A4A9D9381CA6C99400C6880A /* FileWatchTests */, 58 | A4A9D9271CA6C8AA00C6880A /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | A4A9D9271CA6C8AA00C6880A /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | A4A9D9261CA6C8AA00C6880A /* FileWatch.framework */, 66 | A4A9D9371CA6C99400C6880A /* FileWatchTests.xctest */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | A4A9D9281CA6C8AA00C6880A /* FileWatch */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | A4A9D92B1CA6C8AA00C6880A /* Info.plist */, 75 | A4A9D9311CA6C8C500C6880A /* FileWatch.swift */, 76 | ); 77 | path = FileWatch; 78 | sourceTree = ""; 79 | }; 80 | A4A9D9381CA6C99400C6880A /* FileWatchTests */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | A4A9D9391CA6C99400C6880A /* FileWatchTests.swift */, 84 | A4A9D93B1CA6C99400C6880A /* Info.plist */, 85 | ); 86 | path = FileWatchTests; 87 | sourceTree = ""; 88 | }; 89 | /* End PBXGroup section */ 90 | 91 | /* Begin PBXHeadersBuildPhase section */ 92 | A4A9D9231CA6C8AA00C6880A /* Headers */ = { 93 | isa = PBXHeadersBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXHeadersBuildPhase section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | A4A9D9251CA6C8AA00C6880A /* FileWatch */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = A4A9D92E1CA6C8AA00C6880A /* Build configuration list for PBXNativeTarget "FileWatch" */; 105 | buildPhases = ( 106 | A4A9D9211CA6C8AA00C6880A /* Sources */, 107 | A4A9D9221CA6C8AA00C6880A /* Frameworks */, 108 | A4A9D9231CA6C8AA00C6880A /* Headers */, 109 | A4A9D9241CA6C8AA00C6880A /* Resources */, 110 | ); 111 | buildRules = ( 112 | ); 113 | dependencies = ( 114 | ); 115 | name = FileWatch; 116 | productName = FileWatch; 117 | productReference = A4A9D9261CA6C8AA00C6880A /* FileWatch.framework */; 118 | productType = "com.apple.product-type.framework"; 119 | }; 120 | A4A9D9361CA6C99400C6880A /* FileWatchTests */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = A4A9D93F1CA6C99400C6880A /* Build configuration list for PBXNativeTarget "FileWatchTests" */; 123 | buildPhases = ( 124 | A4A9D9331CA6C99400C6880A /* Sources */, 125 | A4A9D9341CA6C99400C6880A /* Frameworks */, 126 | A4A9D9351CA6C99400C6880A /* Resources */, 127 | ); 128 | buildRules = ( 129 | ); 130 | dependencies = ( 131 | A4A9D93E1CA6C99400C6880A /* PBXTargetDependency */, 132 | ); 133 | name = FileWatchTests; 134 | productName = FileWatchTests; 135 | productReference = A4A9D9371CA6C99400C6880A /* FileWatchTests.xctest */; 136 | productType = "com.apple.product-type.bundle.unit-test"; 137 | }; 138 | /* End PBXNativeTarget section */ 139 | 140 | /* Begin PBXProject section */ 141 | A4A9D91D1CA6C8AA00C6880A /* Project object */ = { 142 | isa = PBXProject; 143 | attributes = { 144 | LastSwiftUpdateCheck = 0730; 145 | LastUpgradeCheck = 0900; 146 | ORGANIZATIONNAME = "soh kitahara"; 147 | TargetAttributes = { 148 | A4A9D9251CA6C8AA00C6880A = { 149 | CreatedOnToolsVersion = 7.3; 150 | LastSwiftMigration = 0900; 151 | }; 152 | A4A9D9361CA6C99400C6880A = { 153 | CreatedOnToolsVersion = 7.3; 154 | LastSwiftMigration = 0900; 155 | }; 156 | }; 157 | }; 158 | buildConfigurationList = A4A9D9201CA6C8AA00C6880A /* Build configuration list for PBXProject "FileWatch" */; 159 | compatibilityVersion = "Xcode 3.2"; 160 | developmentRegion = English; 161 | hasScannedForEncodings = 0; 162 | knownRegions = ( 163 | en, 164 | ); 165 | mainGroup = A4A9D91C1CA6C8AA00C6880A; 166 | productRefGroup = A4A9D9271CA6C8AA00C6880A /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | A4A9D9251CA6C8AA00C6880A /* FileWatch */, 171 | A4A9D9361CA6C99400C6880A /* FileWatchTests */, 172 | ); 173 | }; 174 | /* End PBXProject section */ 175 | 176 | /* Begin PBXResourcesBuildPhase section */ 177 | A4A9D9241CA6C8AA00C6880A /* Resources */ = { 178 | isa = PBXResourcesBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | A4A9D9351CA6C99400C6880A /* Resources */ = { 185 | isa = PBXResourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXResourcesBuildPhase section */ 192 | 193 | /* Begin PBXSourcesBuildPhase section */ 194 | A4A9D9211CA6C8AA00C6880A /* Sources */ = { 195 | isa = PBXSourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | A4A9D9321CA6C8C500C6880A /* FileWatch.swift in Sources */, 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | A4A9D9331CA6C99400C6880A /* Sources */ = { 203 | isa = PBXSourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | A4A9D93A1CA6C99400C6880A /* FileWatchTests.swift in Sources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXSourcesBuildPhase section */ 211 | 212 | /* Begin PBXTargetDependency section */ 213 | A4A9D93E1CA6C99400C6880A /* PBXTargetDependency */ = { 214 | isa = PBXTargetDependency; 215 | target = A4A9D9251CA6C8AA00C6880A /* FileWatch */; 216 | targetProxy = A4A9D93D1CA6C99400C6880A /* PBXContainerItemProxy */; 217 | }; 218 | /* End PBXTargetDependency section */ 219 | 220 | /* Begin XCBuildConfiguration section */ 221 | A4A9D92C1CA6C8AA00C6880A /* Debug */ = { 222 | isa = XCBuildConfiguration; 223 | buildSettings = { 224 | ALWAYS_SEARCH_USER_PATHS = NO; 225 | CLANG_ANALYZER_NONNULL = YES; 226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 227 | CLANG_CXX_LIBRARY = "libc++"; 228 | CLANG_ENABLE_MODULES = YES; 229 | CLANG_ENABLE_OBJC_ARC = YES; 230 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 231 | CLANG_WARN_BOOL_CONVERSION = YES; 232 | CLANG_WARN_COMMA = YES; 233 | CLANG_WARN_CONSTANT_CONVERSION = YES; 234 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 235 | CLANG_WARN_EMPTY_BODY = YES; 236 | CLANG_WARN_ENUM_CONVERSION = YES; 237 | CLANG_WARN_INFINITE_RECURSION = YES; 238 | CLANG_WARN_INT_CONVERSION = YES; 239 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 240 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 241 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 242 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 243 | CLANG_WARN_STRICT_PROTOTYPES = YES; 244 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 245 | CLANG_WARN_UNREACHABLE_CODE = YES; 246 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 247 | CODE_SIGN_IDENTITY = "-"; 248 | COPY_PHASE_STRIP = NO; 249 | CURRENT_PROJECT_VERSION = 1; 250 | DEBUG_INFORMATION_FORMAT = dwarf; 251 | ENABLE_STRICT_OBJC_MSGSEND = YES; 252 | ENABLE_TESTABILITY = YES; 253 | GCC_C_LANGUAGE_STANDARD = gnu99; 254 | GCC_DYNAMIC_NO_PIC = NO; 255 | GCC_NO_COMMON_BLOCKS = YES; 256 | GCC_OPTIMIZATION_LEVEL = 0; 257 | GCC_PREPROCESSOR_DEFINITIONS = ( 258 | "DEBUG=1", 259 | "$(inherited)", 260 | ); 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 = YES; 269 | ONLY_ACTIVE_ARCH = YES; 270 | SDKROOT = macosx; 271 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 272 | VERSIONING_SYSTEM = "apple-generic"; 273 | VERSION_INFO_PREFIX = ""; 274 | }; 275 | name = Debug; 276 | }; 277 | A4A9D92D1CA6C8AA00C6880A /* Release */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ALWAYS_SEARCH_USER_PATHS = NO; 281 | CLANG_ANALYZER_NONNULL = YES; 282 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 283 | CLANG_CXX_LIBRARY = "libc++"; 284 | CLANG_ENABLE_MODULES = YES; 285 | CLANG_ENABLE_OBJC_ARC = YES; 286 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 287 | CLANG_WARN_BOOL_CONVERSION = YES; 288 | CLANG_WARN_COMMA = YES; 289 | CLANG_WARN_CONSTANT_CONVERSION = YES; 290 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INFINITE_RECURSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 297 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 299 | CLANG_WARN_STRICT_PROTOTYPES = YES; 300 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 301 | CLANG_WARN_UNREACHABLE_CODE = YES; 302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 303 | CODE_SIGN_IDENTITY = "-"; 304 | COPY_PHASE_STRIP = NO; 305 | CURRENT_PROJECT_VERSION = 1; 306 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 307 | ENABLE_NS_ASSERTIONS = NO; 308 | ENABLE_STRICT_OBJC_MSGSEND = YES; 309 | GCC_C_LANGUAGE_STANDARD = gnu99; 310 | GCC_NO_COMMON_BLOCKS = YES; 311 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 312 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 313 | GCC_WARN_UNDECLARED_SELECTOR = YES; 314 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 315 | GCC_WARN_UNUSED_FUNCTION = YES; 316 | GCC_WARN_UNUSED_VARIABLE = YES; 317 | MACOSX_DEPLOYMENT_TARGET = 10.11; 318 | MTL_ENABLE_DEBUG_INFO = NO; 319 | SDKROOT = macosx; 320 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 321 | VERSIONING_SYSTEM = "apple-generic"; 322 | VERSION_INFO_PREFIX = ""; 323 | }; 324 | name = Release; 325 | }; 326 | A4A9D92F1CA6C8AA00C6880A /* Debug */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | CLANG_ENABLE_MODULES = YES; 330 | COMBINE_HIDPI_IMAGES = YES; 331 | DEFINES_MODULE = YES; 332 | DYLIB_COMPATIBILITY_VERSION = 1; 333 | DYLIB_CURRENT_VERSION = 1; 334 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 335 | FRAMEWORK_VERSION = A; 336 | INFOPLIST_FILE = FileWatch/Info.plist; 337 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 338 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 339 | MACOSX_DEPLOYMENT_TARGET = 10.9; 340 | PRODUCT_BUNDLE_IDENTIFIER = io.github.soh335.FileWatch; 341 | PRODUCT_NAME = "$(TARGET_NAME)"; 342 | SKIP_INSTALL = YES; 343 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 344 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 345 | SWIFT_VERSION = 4.0; 346 | }; 347 | name = Debug; 348 | }; 349 | A4A9D9301CA6C8AA00C6880A /* Release */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | CLANG_ENABLE_MODULES = YES; 353 | COMBINE_HIDPI_IMAGES = YES; 354 | DEFINES_MODULE = YES; 355 | DYLIB_COMPATIBILITY_VERSION = 1; 356 | DYLIB_CURRENT_VERSION = 1; 357 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 358 | FRAMEWORK_VERSION = A; 359 | INFOPLIST_FILE = FileWatch/Info.plist; 360 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 361 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 362 | MACOSX_DEPLOYMENT_TARGET = 10.9; 363 | PRODUCT_BUNDLE_IDENTIFIER = io.github.soh335.FileWatch; 364 | PRODUCT_NAME = "$(TARGET_NAME)"; 365 | SKIP_INSTALL = YES; 366 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 367 | SWIFT_VERSION = 4.0; 368 | }; 369 | name = Release; 370 | }; 371 | A4A9D9401CA6C99400C6880A /* Debug */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | COMBINE_HIDPI_IMAGES = YES; 375 | INFOPLIST_FILE = FileWatchTests/Info.plist; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 377 | PRODUCT_BUNDLE_IDENTIFIER = io.github.soh335.FileWatchTests; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 380 | SWIFT_VERSION = 4.0; 381 | }; 382 | name = Debug; 383 | }; 384 | A4A9D9411CA6C99400C6880A /* Release */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | COMBINE_HIDPI_IMAGES = YES; 388 | INFOPLIST_FILE = FileWatchTests/Info.plist; 389 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 390 | PRODUCT_BUNDLE_IDENTIFIER = io.github.soh335.FileWatchTests; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 393 | SWIFT_VERSION = 4.0; 394 | }; 395 | name = Release; 396 | }; 397 | /* End XCBuildConfiguration section */ 398 | 399 | /* Begin XCConfigurationList section */ 400 | A4A9D9201CA6C8AA00C6880A /* Build configuration list for PBXProject "FileWatch" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | A4A9D92C1CA6C8AA00C6880A /* Debug */, 404 | A4A9D92D1CA6C8AA00C6880A /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | A4A9D92E1CA6C8AA00C6880A /* Build configuration list for PBXNativeTarget "FileWatch" */ = { 410 | isa = XCConfigurationList; 411 | buildConfigurations = ( 412 | A4A9D92F1CA6C8AA00C6880A /* Debug */, 413 | A4A9D9301CA6C8AA00C6880A /* Release */, 414 | ); 415 | defaultConfigurationIsVisible = 0; 416 | defaultConfigurationName = Release; 417 | }; 418 | A4A9D93F1CA6C99400C6880A /* Build configuration list for PBXNativeTarget "FileWatchTests" */ = { 419 | isa = XCConfigurationList; 420 | buildConfigurations = ( 421 | A4A9D9401CA6C99400C6880A /* Debug */, 422 | A4A9D9411CA6C99400C6880A /* Release */, 423 | ); 424 | defaultConfigurationIsVisible = 0; 425 | defaultConfigurationName = Release; 426 | }; 427 | /* End XCConfigurationList section */ 428 | }; 429 | rootObject = A4A9D91D1CA6C8AA00C6880A /* Project object */; 430 | } 431 | -------------------------------------------------------------------------------- /FileWatch.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FileWatch.xcodeproj/xcshareddata/xcschemes/FileWatch.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /FileWatch/FileWatch.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class FileWatch { 4 | 5 | // wrap FSEventStreamEventFlags as OptionSetType 6 | public struct EventFlag: OptionSet { 7 | public let rawValue: FSEventStreamEventFlags 8 | public init(rawValue: FSEventStreamEventFlags) { 9 | self.rawValue = rawValue 10 | } 11 | 12 | public static let None = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagNone)) 13 | 14 | public static let MustScanSubDirs = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagMustScanSubDirs)) 15 | public static let UserDropped = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagUserDropped)) 16 | public static let KernelDropped = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagKernelDropped)) 17 | public static let EventIdsWrapped = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagEventIdsWrapped)) 18 | public static let HistoryDone = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagHistoryDone)) 19 | public static let RootChanged = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagRootChanged)) 20 | public static let Mount = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagMount)) 21 | public static let Unmount = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagUnmount)) 22 | 23 | @available(OSX 10.7, *) 24 | public static let ItemCreated = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemCreated)) 25 | 26 | @available(OSX 10.7, *) 27 | public static let ItemRemoved = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemRemoved)) 28 | 29 | @available(OSX 10.7, *) 30 | public static let ItemInodeMetaMod = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemInodeMetaMod)) 31 | 32 | @available(OSX 10.7, *) 33 | public static let ItemRenamed = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemRenamed)) 34 | 35 | @available(OSX 10.7, *) 36 | public static let ItemModified = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemModified)) 37 | 38 | @available(OSX 10.7, *) 39 | public static let ItemFinderInfoMod = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemFinderInfoMod)) 40 | 41 | @available(OSX 10.7, *) 42 | public static let ItemChangeOwner = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemChangeOwner)) 43 | 44 | @available(OSX 10.7, *) 45 | public static let ItemXattrMod = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemXattrMod)) 46 | 47 | @available(OSX 10.7, *) 48 | public static let ItemIsFile = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemIsFile)) 49 | 50 | @available(OSX 10.7, *) 51 | public static let ItemIsDir = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemIsDir)) 52 | 53 | @available(OSX 10.7, *) 54 | public static let ItemIsSymlink = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemIsSymlink)) 55 | 56 | @available(OSX 10.9, *) 57 | public static let OwnEvent = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagOwnEvent)) 58 | 59 | @available(OSX 10.10, *) 60 | public static let ItemIsHardlink = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemIsHardlink)) 61 | 62 | @available(OSX 10.10, *) 63 | public static let ItemIsLastHardlink = EventFlag(rawValue: FSEventStreamEventFlags(kFSEventStreamEventFlagItemIsLastHardlink)) 64 | } 65 | 66 | // wrap FSEventStreamCreateFlags as OptionSetType 67 | public struct CreateFlag: OptionSet { 68 | public let rawValue: FSEventStreamCreateFlags 69 | public init(rawValue: FSEventStreamCreateFlags) { 70 | self.rawValue = rawValue 71 | } 72 | 73 | public static let None = CreateFlag(rawValue: FSEventStreamCreateFlags(kFSEventStreamCreateFlagNone)) 74 | public static let UseCFTypes = CreateFlag(rawValue: FSEventStreamCreateFlags(kFSEventStreamCreateFlagUseCFTypes)) 75 | public static let NoDefer = CreateFlag(rawValue: FSEventStreamCreateFlags(kFSEventStreamCreateFlagNoDefer)) 76 | public static let WatchRoot = CreateFlag(rawValue: FSEventStreamCreateFlags(kFSEventStreamCreateFlagWatchRoot)) 77 | 78 | @available(OSX 10.6, *) 79 | public static let IgnoreSelf = CreateFlag(rawValue: FSEventStreamCreateFlags(kFSEventStreamCreateFlagIgnoreSelf)) 80 | 81 | @available(OSX 10.7, *) 82 | public static let FileEvents = CreateFlag(rawValue: FSEventStreamCreateFlags(kFSEventStreamCreateFlagFileEvents)) 83 | 84 | @available(OSX 10.9, *) 85 | public static let MarkSelf = CreateFlag(rawValue: FSEventStreamCreateFlags(kFSEventStreamCreateFlagMarkSelf)) 86 | } 87 | 88 | public struct Event { 89 | public let path: String 90 | public let flag: EventFlag 91 | public let eventID: FSEventStreamEventId 92 | } 93 | 94 | public enum Error: Swift.Error { 95 | case startFailed 96 | case streamCreateFailed 97 | case notContainUseCFTypes 98 | } 99 | 100 | public typealias EventHandler = (Event) -> Void 101 | 102 | open let eventHandler: EventHandler 103 | private var eventStream: FSEventStreamRef? 104 | 105 | public init(paths: [String], createFlag: CreateFlag, runLoop: RunLoop, latency: CFTimeInterval, eventHandler: @escaping EventHandler) throws { 106 | self.eventHandler = eventHandler 107 | 108 | var ctx = FSEventStreamContext(version: 0, info: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()), retain: nil, release: nil, copyDescription: nil) 109 | 110 | if !createFlag.contains(.UseCFTypes) { 111 | throw Error.notContainUseCFTypes 112 | } 113 | 114 | guard let eventStream = FSEventStreamCreate(kCFAllocatorDefault, streamCallback, &ctx, paths as CFArray, FSEventStreamEventId(kFSEventStreamEventIdSinceNow), latency, createFlag.rawValue) else { 115 | throw Error.streamCreateFailed 116 | } 117 | 118 | FSEventStreamScheduleWithRunLoop(eventStream, runLoop.getCFRunLoop(), CFRunLoopMode.defaultMode.rawValue) 119 | if !FSEventStreamStart(eventStream) { 120 | throw Error.startFailed 121 | } 122 | 123 | self.eventStream = eventStream 124 | } 125 | 126 | deinit { 127 | guard let eventStream = self.eventStream else { 128 | return 129 | } 130 | FSEventStreamStop(eventStream) 131 | FSEventStreamInvalidate(eventStream) 132 | FSEventStreamRelease(eventStream) 133 | self.eventStream = nil 134 | 135 | } 136 | } 137 | 138 | fileprivate func streamCallback(streamRef: ConstFSEventStreamRef, clientCallBackInfo: UnsafeMutableRawPointer?, numEvents: Int, eventPaths: UnsafeMutableRawPointer, eventFlags: UnsafePointer, eventIds: UnsafePointer) -> Void { 139 | let `self` = unsafeBitCast(clientCallBackInfo, to: FileWatch.self) 140 | guard let eventPathArray = unsafeBitCast(eventPaths, to: NSArray.self) as? [String] else { 141 | return 142 | } 143 | var eventFlagArray = Array(UnsafeBufferPointer(start: eventFlags, count: numEvents)) 144 | var eventIdArray = Array(UnsafeBufferPointer(start: eventIds, count: numEvents)) 145 | 146 | for i in 0.. 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 | FMWK 17 | CFBundleShortVersionString 18 | 0.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016年 soh kitahara. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /FileWatchTests/FileWatchTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import FileWatch 3 | 4 | class FileWatchTests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testInitCreateFlag() { 17 | do { 18 | let _ = try FileWatch(paths: [""], createFlag: [], runLoop: RunLoop.current, latency: 1, eventHandler: { _ in }) 19 | } catch let e as FileWatch.Error { 20 | XCTAssertEqual(e, FileWatch.Error.notContainUseCFTypes) 21 | } catch { 22 | XCTFail() 23 | } 24 | } 25 | 26 | func testFileCreate() { 27 | let ex = self.expectation(description: "") 28 | let filename = String(format: "%@_%@", ProcessInfo.processInfo.globallyUniqueString, "file.txt") 29 | let tmpfile = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename) 30 | debugPrint(tmpfile) 31 | 32 | defer { 33 | try! FileManager.default.removeItem(at: tmpfile) 34 | } 35 | 36 | let filewatch = try! FileWatch(paths: [(tmpfile.path as NSString).deletingLastPathComponent], createFlag: [.UseCFTypes, .FileEvents], runLoop: RunLoop.current, latency: 3.0, eventHandler: { event in 37 | if (event.path as NSString).lastPathComponent == (tmpfile.path as NSString).lastPathComponent { 38 | XCTAssertEqual( 39 | try! String(contentsOfFile: event.path, encoding: String.Encoding.utf8), 40 | try! String(contentsOfFile: tmpfile.path, encoding: String.Encoding.utf8) 41 | ) 42 | XCTAssert(event.flag.contains(.ItemIsFile)) 43 | XCTAssert(!event.flag.contains(.ItemIsDir)) 44 | XCTAssert(event.flag.contains(.ItemCreated)) 45 | ex.fulfill() 46 | } 47 | }) 48 | 49 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(NSEC_PER_SEC)) / Double(NSEC_PER_SEC), execute: { 50 | try! "aaa".write(toFile: tmpfile.path, atomically: false, encoding: String.Encoding.utf8) 51 | }) 52 | 53 | self.waitForExpectations(timeout: 10, handler: nil) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /FileWatchTests/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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 soh335 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/soh335/FileWatch.svg?branch=master)](https://travis-ci.org/soh335/FileWatch) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 2 | 3 | # FileWatch 4 | 5 | Simple FSEvents wrapper for Swift 6 | 7 | ## INSTALL 8 | 9 | ### CARTHAGE 10 | 11 | * Add ```github "soh335/FileWatch"``` to your Cartfile. 12 | * Run ```carthage update```. 13 | * Add FileWatch.framework to Embedded Binaries. 14 | 15 | ## USAGE 16 | 17 | ```swift 18 | import FileWatch 19 | 20 | let filewatch = try! FileWatch(paths: ["/path/to/dir"], createFlag: [.UseCFTypes, .FileEvents], runLoop: RunLoop.current, latency: 3.0, eventHandler: { event in 21 | if event.flag.contains(.ItemIsFile) { 22 | debugPrint(event.path) 23 | } 24 | }) 25 | ``` 26 | 27 | ## LICENSE 28 | 29 | * MIT 30 | --------------------------------------------------------------------------------