passforios/passKit/Models/PasswordEntity.swift

218 lines
8.2 KiB
Swift
Raw Normal View History

2017-02-11 23:45:00 +08:00
//
// PasswordEntity.swift
2021-08-28 07:32:31 +02:00
// passKit
2017-02-11 23:45:00 +08:00
//
// Created by Mingshen Sun on 11/2/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
2025-01-25 15:40:12 -08:00
import CoreData
2017-02-11 23:45:00 +08:00
import Foundation
2025-01-25 15:40:12 -08:00
import ObjectiveGit
2017-02-11 23:45:00 +08:00
import SwiftyUserDefaults
2025-01-25 15:40:12 -08:00
public final class PasswordEntity: NSManagedObject, Identifiable {
/// Name of the password, i.e., filename without extension.
@NSManaged public var name: String
/// A Boolean value indicating whether the entity is a directory.
@NSManaged public var isDir: Bool
/// A Boolean value indicating whether the entity is synced with remote repository.
@NSManaged public var isSynced: Bool
/// The relative file path of the password or directory.
@NSManaged public var path: String
/// The thumbnail image of the password if there is a url entry in the password.
@NSManaged public var image: Data?
/// The parent password entity.
@NSManaged public var parent: PasswordEntity?
/// A set of child password entities.
@NSManaged public var children: Set<PasswordEntity>
@nonobjc
public static func fetchRequest() -> NSFetchRequest<PasswordEntity> {
NSFetchRequest<PasswordEntity>(entityName: "PasswordEntity")
2017-03-16 00:38:38 +08:00
}
2018-12-09 16:59:07 -08:00
2025-01-25 15:40:12 -08:00
/// A String value with password directory and name, i.e., path without extension.
public var nameWithDir: String {
(path as NSString).deletingPathExtension
}
2018-12-09 16:59:07 -08:00
2025-01-25 15:40:12 -08:00
public var dirText: String {
getDirArray().joined(separator: " > ")
}
public func fileURL(in directoryURL: URL) -> URL {
directoryURL.appendingPathComponent(path)
}
2025-01-25 15:40:12 -08:00
public func getDirArray() -> [String] {
2017-03-10 23:01:56 -08:00
var parentEntity = parent
var passwordCategoryArray: [String] = []
2025-01-25 15:40:12 -08:00
while let current = parentEntity {
passwordCategoryArray.append(current.name)
parentEntity = current.parent
2017-03-10 23:01:56 -08:00
}
passwordCategoryArray.reverse()
return passwordCategoryArray
2017-03-10 23:01:56 -08:00
}
2018-12-09 16:59:07 -08:00
2025-01-25 15:40:12 -08:00
public static func fetchAll(in context: NSManagedObjectContext) -> [PasswordEntity] {
let request = Self.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
return (try? context.fetch(request) as? [Self]) ?? []
}
public static func fetchAllPassword(in context: NSManagedObjectContext) -> [PasswordEntity] {
let request = Self.fetchRequest()
request.predicate = NSPredicate(format: "isDir = false")
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
return (try? context.fetch(request) as? [Self]) ?? []
}
public static func totalNumber(in context: NSManagedObjectContext) -> Int {
let request = Self.fetchRequest()
request.predicate = NSPredicate(format: "isDir = false")
return (try? context.count(for: request)) ?? 0
}
public static func fetchUnsynced(in context: NSManagedObjectContext) -> [PasswordEntity] {
let request = Self.fetchRequest()
request.predicate = NSPredicate(format: "isSynced = false")
return (try? context.fetch(request) as? [Self]) ?? []
}
public static func fetch(by path: String, in context: NSManagedObjectContext) -> PasswordEntity? {
let request = Self.fetchRequest()
request.predicate = NSPredicate(format: "path = %@", path)
return try? context.fetch(request).first as? Self
}
public static func fetch(by path: String, isDir: Bool, in context: NSManagedObjectContext) -> PasswordEntity? {
let request = Self.fetchRequest()
request.predicate = NSPredicate(format: "path = %@ and isDir = %@", path, isDir as NSNumber)
return try? context.fetch(request).first as? Self
}
public static func fetch(by parent: PasswordEntity?, in context: NSManagedObjectContext) -> [PasswordEntity] {
let request = Self.fetchRequest()
request.predicate = NSPredicate(format: "parent = %@", parent ?? 0)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
return (try? context.fetch(request) as? [Self]) ?? []
}
public static func updateAllToSynced(in context: NSManagedObjectContext) -> Int {
let request = NSBatchUpdateRequest(entity: Self.entity())
request.resultType = .updatedObjectsCountResultType
request.predicate = NSPredicate(format: "isSynced = false")
request.propertiesToUpdate = ["isSynced": true]
let result = try? context.execute(request) as? NSBatchUpdateResult
return result?.result as? Int ?? 0
}
public static func deleteRecursively(entity: PasswordEntity, in context: NSManagedObjectContext) {
var currentEntity: PasswordEntity? = entity
while let node = currentEntity, node.children.isEmpty {
let parent = node.parent
context.delete(node)
try? context.save()
currentEntity = parent
}
}
2025-01-25 15:40:12 -08:00
public static func deleteAll(in context: NSManagedObjectContext) {
let deleteRequest = NSBatchDeleteRequest(fetchRequest: Self.fetchRequest())
_ = try? context.execute(deleteRequest)
}
2025-01-25 15:40:12 -08:00
public static func exists(password: Password, in context: NSManagedObjectContext) -> Bool {
let request = fetchRequest()
request.predicate = NSPredicate(format: "name = %@ and path = %@ and isDir = false", password.name, password.path)
if let count = try? context.count(for: request) {
return count > 0
}
return false
}
2025-01-25 15:40:12 -08:00
@discardableResult
public static func insert(name: String, path: String, isDir: Bool, into context: NSManagedObjectContext) -> PasswordEntity {
let entity = PasswordEntity(context: context)
entity.name = name
entity.path = path
entity.isDir = isDir
entity.isSynced = false
return entity
}
2025-01-25 15:40:12 -08:00
public static func initPasswordEntityCoreData(url: URL, in context: NSManagedObjectContext) {
let localFileManager = FileManager.default
2025-02-15 23:38:23 -08:00
let url = url.resolvingSymlinksInPath()
2025-01-25 15:40:12 -08:00
let root = {
let entity = PasswordEntity(context: context)
entity.name = "root"
entity.isDir = true
entity.path = ""
return entity
}()
var queue = [root]
while !queue.isEmpty {
let current = queue.removeFirst()
let resourceKeys = Set<URLResourceKey>([.nameKey, .isDirectoryKey])
let options = FileManager.DirectoryEnumerationOptions([.skipsHiddenFiles, .skipsSubdirectoryDescendants])
let currentURL = url.appendingPathComponent(current.path)
guard let directoryEnumerator = localFileManager.enumerator(at: currentURL, includingPropertiesForKeys: Array(resourceKeys), options: options) else {
continue
}
for case let fileURL as URL in directoryEnumerator {
guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
let isDirectory = resourceValues.isDirectory,
let name = resourceValues.name
else {
continue
}
let passwordEntity = PasswordEntity(context: context)
passwordEntity.isDir = isDirectory
if isDirectory {
passwordEntity.name = name
queue.append(passwordEntity)
} else {
if (name as NSString).pathExtension == "gpg" {
passwordEntity.name = (name as NSString).deletingPathExtension
} else {
passwordEntity.name = name
}
}
passwordEntity.parent = current
2025-02-15 23:38:23 -08:00
passwordEntity.path = String(fileURL.path.dropFirst(url.path.count + 1))
2025-01-25 15:40:12 -08:00
}
}
context.delete(root)
}
2017-02-11 23:45:00 +08:00
}
2025-01-25 15:40:12 -08:00
public extension PasswordEntity {
@objc(addChildrenObject:)
@NSManaged
func addToChildren(_ value: PasswordEntity)
@objc(removeChildrenObject:)
@NSManaged
func removeFromChildren(_ value: PasswordEntity)
@objc(addChildren:)
@NSManaged
func addToChildren(_ values: NSSet)
@objc(removeChildren:)
@NSManaged
func removeFromChildren(_ values: NSSet)
}