Skip to content

Commit

Permalink
change FolderContentMonitor observable initialization
Browse files Browse the repository at this point in the history
See discussion here for details:
RxSwiftCommunity/contributors#16
  • Loading branch information
DivineDominion committed Dec 13, 2016
1 parent bc243cf commit a30a212
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 48 deletions.
8 changes: 7 additions & 1 deletion Example/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {

report(url)

Monitoring.folderMonitor(url: url)
FolderContentMonitor(url: url)
.asObservable()

// Ignore Finder folder settings
.filter { $0.filename != ".DS_Store" }

// Report changes into app's main log
.subscribeOn(MainScheduler.asyncInstance)
.subscribe(onNext: { event in
self.report("\(event.filename) changed (\(event.change))")
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import RxFileMonitor
let disposeBag = DisposeBag()
let folderUrl = URL(fileURLWithPath: "/path/to/monitor/")

Monitoring.folderMonitor(url: folderUrl)
FolderContentMonitor(url: folderUrl)
.asObservable()
.subscribe(onNext: { event in
print("Folder contents changed at \(event.url) (\(event.change))")
})
Expand All @@ -37,10 +38,11 @@ Monitoring.folderMonitor(url: folderUrl)
Say you want to update a cache of a folder's notes' contents, you'll be interested in files only:

```swift
let changedFile = Monitoring.folderMonitor(url: folderUrl)
let changedFile = FolderContentMonitor(url: folderUrl)
.asObservable()
// Files only ...
.filter { $0.change.contains(.isFile) }
// ... except the Spotlight cache.
// ... except the user's folder settings.
.filter { $0.filename != ".DS_Store" }
.map { $0.filename }
.observeOn(MainScheduler.instance)
Expand All @@ -55,7 +57,8 @@ changedFile.subscribe(onNext: cache.updateFile)
Or if you simply rebuild the whole cache when anything changed, you can stop after filtering for accepted events:

```swift
let changedFile = Monitoring.folderMonitor(url: folderUrl)
let changedFile = FolderContentMonitor(url: folderUrl)
.asObservable()
.filter { $0.change.contains(.isFile) }
.filter { $0.filename != ".DS_Store" }
.observeOn(MainScheduler.instance)
Expand Down
8 changes: 4 additions & 4 deletions RxFileMonitor.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
5096B2B81DD2216B00076058 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5096B2B61DD2216B00076058 /* RxSwift.framework */; };
5096B2B91DD2217900076058 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5096B2B61DD2216B00076058 /* RxSwift.framework */; };
5096B2BA1DD2217900076058 /* RxSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5096B2B61DD2216B00076058 /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5096B2BC1DD221D700076058 /* Monitoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5096B2BB1DD221D700076058 /* Monitoring.swift */; };
5096B2BC1DD221D700076058 /* RxFileMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5096B2BB1DD221D700076058 /* RxFileMonitor.swift */; };
5096B2C11DD22D6000076058 /* FolderContentChangeEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5096B2C01DD22D6000076058 /* FolderContentChangeEvent.swift */; };
50E8354D1DD1B26900783B62 /* RxFileMonitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50E835431DD1B26900783B62 /* RxFileMonitor.framework */; };
50E835521DD1B26900783B62 /* RxFileMonitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E835511DD1B26900783B62 /* RxFileMonitorTests.swift */; };
Expand Down Expand Up @@ -61,7 +61,7 @@
/* Begin PBXFileReference section */
5096B2B41DD21D0B00076058 /* Change.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Change.swift; sourceTree = "<group>"; };
5096B2B61DD2216B00076058 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/Mac/RxSwift.framework; sourceTree = "<group>"; };
5096B2BB1DD221D700076058 /* Monitoring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Monitoring.swift; sourceTree = "<group>"; };
5096B2BB1DD221D700076058 /* RxFileMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxFileMonitor.swift; sourceTree = "<group>"; };
5096B2C01DD22D6000076058 /* FolderContentChangeEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderContentChangeEvent.swift; sourceTree = "<group>"; };
5096B2C21DD22FC200076058 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
5096B2C41DD2333E00076058 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
Expand Down Expand Up @@ -148,7 +148,7 @@
isa = PBXGroup;
children = (
50E835461DD1B26900783B62 /* RxFileMonitor.h */,
5096B2BB1DD221D700076058 /* Monitoring.swift */,
5096B2BB1DD221D700076058 /* RxFileMonitor.swift */,
5096B2BD1DD222D000076058 /* Monitors */,
50E835471DD1B26900783B62 /* Info.plist */,
);
Expand Down Expand Up @@ -325,7 +325,7 @@
files = (
50E835761DD1D1C200783B62 /* FolderContentMonitor.swift in Sources */,
5096B2C11DD22D6000076058 /* FolderContentChangeEvent.swift in Sources */,
5096B2BC1DD221D700076058 /* Monitoring.swift in Sources */,
5096B2BC1DD221D700076058 /* RxFileMonitor.swift in Sources */,
50E8356F1DD1BAEE00783B62 /* FileMonitor.swift in Sources */,
5096B2B51DD21D0B00076058 /* Change.swift in Sources */,
);
Expand Down
2 changes: 1 addition & 1 deletion RxFileMonitor/FileMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class FileMonitor {
public let url: URL
}

public static let monitorQueue = DispatchQueue(label: "com.cleancocoa.rxfilemonitor.monitorqueue", qos: .background, attributes: [.concurrent])
public static let monitorQueue = DispatchQueue(label: "org.rxswift.rxfilemonitor.monitorqueue", qos: .background, attributes: [.concurrent])

var monitoredFileDescriptor: Int32?
var monitorSource: DispatchSourceFileSystemObject?
Expand Down
47 changes: 38 additions & 9 deletions RxFileMonitor/FolderContentMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,36 @@ import Foundation

public class FolderContentMonitor {

let callback: (FolderContentChangeEvent) -> Void
var callback: ((FolderContentChangeEvent) -> Void)?

public let pathsToWatch: [String]
public private(set) var hasStarted = false
private var streamRef: FSEventStreamRef!

public private(set) var lastEventId: FSEventStreamEventId

public init(pathsToWatch: [String], sinceWhen: FSEventStreamEventId = FSEventStreamEventId(kFSEventStreamEventIdSinceNow), callback: @escaping (FolderContentChangeEvent) -> Void) {
/// - parameter url: Folder to monitor.
/// - parameter sinceWhen: Reference event for the subscription. Default
/// is `kFSEventStreamEventIdSinceNow`.
/// - parameter callback: Callback for incoming file system events. Can be ignored
/// when you use the monitor `asObservable`
public convenience init(
url: URL,
sinceWhen: FSEventStreamEventId = FSEventStreamEventId(kFSEventStreamEventIdSinceNow),
callback: ((FolderContentChangeEvent) -> Void)? = nil) {

self.init(pathsToWatch: [url.path], sinceWhen: sinceWhen, callback: callback)
}

/// - parameter pathsToWatch: Collection of file or folder paths.
/// - parameter sinceWhen: Reference event for the subscription. Default
/// is `kFSEventStreamEventIdSinceNow`.
/// - parameter callback: Callback for incoming file system events. Can be ignored
/// when you use the monitor `asObservable`
public init(
pathsToWatch: [String],
sinceWhen: FSEventStreamEventId = FSEventStreamEventId(kFSEventStreamEventIdSinceNow),
callback: ((FolderContentChangeEvent) -> Void)? = nil) {

self.lastEventId = sinceWhen
self.pathsToWatch = pathsToWatch
Expand All @@ -31,7 +52,7 @@ public class FolderContentMonitor {

public func start() {

guard hasStarted == false else { assertionFailure("Start must not be called twice. (Ignoring)"); return }
guard !hasStarted else { assertionFailure("Start must not be called twice. (Ignoring)"); return }

var context = FSEventStreamContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
context.info = Unmanaged.passUnretained(self).toOpaque()
Expand All @@ -44,26 +65,34 @@ public class FolderContentMonitor {
hasStarted = true
}

private let eventCallback: FSEventStreamCallback = { (stream: ConstFSEventStreamRef, contextInfo: UnsafeMutableRawPointer?, numEvents: Int, eventPaths: UnsafeMutableRawPointer, eventFlags: UnsafePointer<FSEventStreamEventFlags>?, eventIds: UnsafePointer<FSEventStreamEventId>?) in
private let eventCallback: FSEventStreamCallback = {
(stream: ConstFSEventStreamRef,
contextInfo: UnsafeMutableRawPointer?,
numEvents: Int,
eventPaths: UnsafeMutableRawPointer,
eventFlags: UnsafePointer<FSEventStreamEventFlags>?,
eventIds: UnsafePointer<FSEventStreamEventId>?) in

let fileSystemWatcher: FolderContentMonitor = unsafeBitCast(contextInfo, to: FolderContentMonitor.self)

guard let eventIds = eventIds,
guard let callback = fileSystemWatcher.callback,
let eventIds = eventIds,
let eventFlags = eventFlags,
let paths = unsafeBitCast(eventPaths, to: NSArray.self) as? [String]
else { return }

let fileSystemWatcher: FolderContentMonitor = unsafeBitCast(contextInfo, to: FolderContentMonitor.self)

(0..<numEvents)
.map { (index: Int) -> FolderContentChangeEvent in
let change = Change(eventFlags: eventFlags[index])
return FolderContentChangeEvent(eventId: eventIds[index], eventPath: paths[index], change: change)
}.forEach(fileSystemWatcher.callback)
}.forEach(callback)

fileSystemWatcher.lastEventId = eventIds[numEvents - 1]
}

public func stop() {
guard hasStarted == true else { return }

guard hasStarted else { return }

FSEventStreamStop(streamRef)
FSEventStreamInvalidate(streamRef)
Expand Down
29 changes: 0 additions & 29 deletions RxFileMonitor/Monitoring.swift

This file was deleted.

35 changes: 35 additions & 0 deletions RxFileMonitor/RxFileMonitor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Monitoring.swift
// RxFileMonitor
//
// Created by Christian Tietze on 08/11/16.
// Copyright © 2016 RxSwiftCommunity https://github.com/RxSwiftCommunity
//

import Foundation
import RxSwift

extension FolderContentMonitor: ObservableConvertibleType {

public func asObservable() -> Observable<FolderContentChangeEvent> {

return Observable.create { observer in

// Wrap existing callback
let oldCallback = self.callback

self.callback = { event in
oldCallback?(event)
observer.on(.next(event))
}

if !self.hasStarted {
self.start()
}

return Disposables.create {
self.stop()
}
}
}
}

0 comments on commit a30a212

Please sign in to comment.