import Foundation import WebKit class File: NSObject, WKScriptMessageHandler { let fm = FileManager.default; let baseDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! static let escapeRegex = try! NSRegularExpression(pattern: "([`$\\\\])", options: [.caseInsensitive]) override init() { super.init(); try! fm.createDirectory(at: baseDir, withIntermediateDirectories: true, attributes: nil) } static func escape(_ str: String) -> String { return "`" + escapeRegex.stringByReplacingMatches(in: str, range: NSMakeRange(0, str.count), withTemplate: "\\\\$1") + "`" } func userContentController(_ controller: WKUserContentController, didReceive message: WKScriptMessage) { let data = message.body as! [String: AnyObject] let key = data["_id"] as! String do { var result: String? switch(data["_type"] as! String) { case "read": result = try read(data["name"] as! String) case "write": try write(data["name"] as! String, data["data"] as! String) case "listDirectories": result = try list(data["name"] as! String, directories: true) case "listFiles": result = try list(data["name"] as! String, directories: false) case "getSize": result = try getSize(data["name"] as! String) case "ensureDirectory": try ensureDirectory(data["name"] as! String) default: message.webView!.evaluateJavaScript("nativeError('\(key)',new Error('Unknown message type'))") return } let output = result == nil ? "undefined" : result!; message.webView!.evaluateJavaScript("nativeMessage('\(key)',\(output))") } catch(let error) { message.webView!.evaluateJavaScript("nativeError('\(key)',new Error('File-\(data["_type"]!): \(error.localizedDescription)'))") } } func read(_ name: String) throws -> String? { let url = baseDir.appendingPathComponent(name, isDirectory: false) if(!fm.fileExists(atPath: url.path)) { return nil; } return File.escape(try String(contentsOf: url, encoding: .utf8)) } func write(_ name: String, _ data: String) throws { try data.write(to: baseDir.appendingPathComponent(name, isDirectory: false), atomically: true, encoding: .utf8) } func list(_ name: String, directories: Bool) throws -> String { let url = baseDir.appendingPathComponent(name, isDirectory: true) let entries = try fm.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]).filter { try $0.resourceValues(forKeys: [.isDirectoryKey]).isDirectory == directories }.map { $0.lastPathComponent } return String(data: try JSONSerialization.data(withJSONObject: entries), encoding: .utf8)!; } func getSize(_ name: String) throws -> String { let path = baseDir.appendingPathComponent(name, isDirectory: false).path; if(!fm.fileExists(atPath: path)) { return "0"; } return String(try fm.attributesOfItem(atPath: path)[.size] as! UInt64) } func ensureDirectory(_ name: String) throws { try fm.createDirectory(at: baseDir.appendingPathComponent(name, isDirectory: true), withIntermediateDirectories: true, attributes: nil) } }