본문 바로가기
# 애플 [Apple]/macOS

[🍎 macOS] 파일 또는 폴더 파일 시스템 이벤트 모니터링 (How to monitor a folder for new files)

by cy_mos 2024. 9. 6.
반응형

파일 시스템 이벤트에 대한 세부적인 내용은 아래와 같습니다.

 

  • static let all: DispatchSource.FileSystemEvent → 모든 파일 시스템 이벤트를 감지합니다.
  • static let attrib: DispatchSource.FileSystemEvent   파일 또는 디렉토리의 속성 (파일 소유자, 권한, 수정 시간 등) 변경을 감지합니다.
  • static let delete: DispatchSource.FileSystemEvent   파일 또는 디렉토리 삭제를 감지합니다.
  • static let extend: DispatchSource.FileSystemEvent → 파일 용량 증가를 감지합니다.
  • static let funlock: DispatchSource.FileSystemEvent  파일 잠금 해제를 감지합니다.
  • static let link: DispatchSource.FileSystemEvent → 파일 또는 폴더에 링크 생성 또는 삭제를 감지합니다.
  • static let rename: DispatchSource.FileSystemEvent  파일 또는 폴더에 이름 변경을 감지합니다.
  • static let revoke: DispatchSource.FileSystemEvent  파일 디스크립터가 닫히거나 접근 권한 변경을 감지합니다.
  • static let write: DispatchSource.FileSystemEvent  파일 또는 폴더에 데이터가 생성, 변경 등의 작업을 감지합니다.

🗂️ [Swift] 폴더 내부에 파일 이벤트 모니터링 가져오기

/*
 * Copyright (c) 2024 Universal-SystemKit. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import Dispatch
import Foundation

@objc public class SKDispatchFileMonitor: NSObject, SKClass {

	// MARK: - Typealias
	public typealias SKFileEventHandler = (DispatchSource.FileSystemEvent) -> Swift.Void
    
    // MARK: - Object Properties
    public static let label: String = "com.SystemKit.SKDispatchFileMonitor"
    public static let identifier: String = "A2BC9AF4-45B0-4C49-8D6B-1B0BAB8AC3FA"
    public static let name = Notification.Name(SKDispatchFileMonitor.label)
    
    private let source: DispatchSourceFileSystemObject
    private var handler: Optional<SKFileEventHandler>
    
    // MARK: - Initalize
    public init?(filePath: String, eventMask: DispatchSource.FileSystemEvent, handler: Optional<SKFileEventHandler> = nil) {
        
        // Creating a FileHandle
        guard let fileDescriptor = SKDispatchFileMonitor.makeFileDescriptor(filePath: filePath) else { return nil }
        
        // Creating a DispatchSourceFileSystemObject
        guard let source = SKDispatchFileMonitor.makeFileSystemObjectSource(fileDescriptor: fileDescriptor,
                                                                            eventMask: eventMask) else { return nil }
        
        self.source = source
        self.handler = handler
    }
    
    // MARK: - Deinitalize
    deinit { stop() }
}

// MARK: - Private Extension SKDispatchFileMonitor
private extension SKDispatchFileMonitor {
    
    static func makeFileDescriptor(filePath: String) -> Optional<Int32> {
        
        // Creating a FileDescriptor
        let fileDescriptor = open(filePath, O_EVTONLY | F_NOCACHE)
        
        // Failed to create FileDescriptor
        if fileDescriptor < 0 { return nil }
        
        return fileDescriptor
    }
    
    static func makeFileSystemObjectSource(fileDescriptor: Int32, 
                                           eventMask: DispatchSource.FileSystemEvent) -> Optional<DispatchSourceFileSystemObject> {

        let queue = DispatchQueue(label: SKDispatchFileMonitor.label, qos: .background, attributes: .concurrent)
        
        do { return DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileDescriptor, eventMask: eventMask, queue: queue) }
        catch let error as NSError {
            logger.error("[SKDispatchFileMonitor] Failed to create file-system events: \(error.description)")
            return nil
        }
    }
    
    final func occurEventHandler() {
        
        logger.info("[SKDispatchFileMonitor] Setting up an EventHandler for FileSystem")
        
        self.handler?(self.source.data)
        
        NotificationCenter.default.post(name: SKDispatchFileMonitor.name, object: self.source.data)
    }
    
    final func occurCancelHandler() {
        
        logger.info("[SKDispatchFileMonitor] Setting up an CancelEventHandler for FileSystem")
        
        // Cleaning up objects used in FileSystemEvent
        self.handler = nil
        close(self.source.handle)
        
        NotificationCenter.default.post(name: SKDispatchFileMonitor.name, object: nil)
    }
}

// MARK: - Public Extension SKDispatchFileMonitor
public extension SKDispatchFileMonitor {
    
    final func start() {
        
        // Setting up an EventHandler for DispatchSourceFileSystem
        self.source.setEventHandler { [unowned self] in self.occurEventHandler() }
        
        // Setting up an CancelEventHandler for DispatchSourceFileSystem
        self.source.setCancelHandler { [unowned self] in self.occurCancelHandler() }
        
        // Resumes the DispatchSourceFileSystem
        self.source.resume()
    }
    
    final func stop() {
        
        logger.info("[SKDispatchFileMonitor] Stopping FileSystemEvent monitoring")
        
        // cancel the DispatchSourceFileSystem
        self.source.cancel()
    }
}

🚀 REFERENCE

더보기
반응형

댓글