Support folder in password view
- change core data - change data struct to store table view entry - delete unnecessary functions
This commit is contained in:
parent
98b01d16cf
commit
050a960167
7 changed files with 177 additions and 89 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16E163f" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16E175b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
|
||||
<device id="retina5_5" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
|
|
@ -43,9 +43,6 @@
|
|||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="tW4-E9-CGv" kind="show" identifier="showPasswordDetail" id="26n-ZD-G0k"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<sections/>
|
||||
|
|
@ -63,6 +60,7 @@
|
|||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="tableView" destination="Tn1-q5-vaJ" id="UHc-AS-gXh"/>
|
||||
<segue destination="tW4-E9-CGv" kind="show" identifier="showPasswordDetail" id="gXF-zd-527"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="6ju-JT-yds" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
|
|||
numberFormatter.numberStyle = NumberFormatter.Style.decimal
|
||||
let fm = FileManager.default
|
||||
|
||||
let passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData()
|
||||
let passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false)
|
||||
let numberOfPasswords = numberFormatter.string(from: NSNumber(value: passwordEntities.count))!
|
||||
|
||||
var size = UInt64(0)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import SVProgressHUD
|
|||
|
||||
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
|
||||
var passwordEntity: PasswordEntity?
|
||||
var passwordCategoryEntities: [PasswordCategoryEntity]?
|
||||
var passwordCategoryText = ""
|
||||
var password: Password?
|
||||
var passwordImage: UIImage?
|
||||
|
|
@ -62,13 +61,23 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
|
||||
var tableData = Array<TableSection>()
|
||||
|
||||
private func generateCategoryText() -> String {
|
||||
var passwordCategoryArray: [String] = []
|
||||
var parent = passwordEntity?.parent
|
||||
while parent != nil {
|
||||
passwordCategoryArray.append(parent!.name!)
|
||||
parent = parent!.parent
|
||||
}
|
||||
passwordCategoryArray.reverse()
|
||||
return passwordCategoryArray.joined(separator: " > ")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
|
||||
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
|
||||
|
||||
let passwordCategoryArray = passwordCategoryEntities?.map { $0.category! }
|
||||
passwordCategoryText = (passwordCategoryArray?.joined(separator: " > "))!
|
||||
passwordCategoryText = generateCategoryText()
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
|
||||
tableView.addGestureRecognizer(tapGesture)
|
||||
|
|
@ -300,7 +309,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
footerLabel.numberOfLines = 0
|
||||
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
footerLabel.textColor = UIColor.gray
|
||||
let dateString = PasswordStore.shared.getLatestCommitDate(filename: (passwordEntity?.rawPath)!)
|
||||
let dateString = PasswordStore.shared.getLatestCommitDate(filename: (passwordEntity?.path)!)
|
||||
footerLabel.text = "Last Updated: \(dateString ?? "Unknown")"
|
||||
view.addSubview(footerLabel)
|
||||
return view
|
||||
|
|
|
|||
|
|
@ -11,9 +11,29 @@ import SVProgressHUD
|
|||
import SwiftyUserDefaults
|
||||
import PasscodeLock
|
||||
|
||||
enum PasswordsTableEntryType {
|
||||
case password, dir
|
||||
}
|
||||
|
||||
struct PasswordsTableEntry {
|
||||
var title: String
|
||||
var isDir: Bool
|
||||
var passwordEntity: PasswordEntity?
|
||||
}
|
||||
|
||||
class PasswordsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
private var passwordEntities: [PasswordEntity]?
|
||||
var filteredPasswordEntities = [PasswordEntity]()
|
||||
private var passwordsTableEntries: [PasswordsTableEntry] = []
|
||||
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
|
||||
private var parentPasswordEntity: PasswordEntity? = nil
|
||||
|
||||
private func initPasswordsTableEntries() {
|
||||
passwordsTableEntries.removeAll()
|
||||
filteredPasswordsTableEntries.removeAll()
|
||||
passwordsTableEntries = PasswordStore.shared.fetchPasswordEntityCoreData(parent: parentPasswordEntity).map {
|
||||
PasswordsTableEntry(title: $0.name!, isDir: $0.isDir, passwordEntity: $0)
|
||||
}
|
||||
}
|
||||
|
||||
var sections : [(index: Int, length :Int, title: String)] = Array()
|
||||
var searchActive : Bool = false
|
||||
let searchController = UISearchController(searchResultsController: nil)
|
||||
|
|
@ -23,6 +43,10 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
return refreshControl
|
||||
}()
|
||||
let searchBarView = UIView(frame: CGRect(x: 0, y: 64, width: UIScreen.main.bounds.width, height: 44))
|
||||
lazy var backUIBarButtonItem: UIBarButtonItem = {
|
||||
let backUIBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(self.backAction(_:)))
|
||||
return backUIBarButtonItem
|
||||
}()
|
||||
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
|
|
@ -48,6 +72,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func syncPasswords() {
|
||||
SVProgressHUD.setDefaultMaskType(.black)
|
||||
SVProgressHUD.setDefaultStyle(.light)
|
||||
|
|
@ -69,8 +94,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
DispatchQueue.main.async {
|
||||
PasswordStore.shared.updatePasswordEntityCoreData()
|
||||
self.passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData()
|
||||
self.reloadTableView(data: self.passwordEntities!)
|
||||
self.parentPasswordEntity = nil
|
||||
self.initPasswordsTableEntries()
|
||||
self.reloadTableView(data: self.passwordsTableEntries)
|
||||
PasswordStore.shared.setAllSynced()
|
||||
self.setNavigationItemTitle()
|
||||
Defaults[.lastUpdatedTime] = Date()
|
||||
|
|
@ -90,12 +116,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setNavigationItemTitle()
|
||||
passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData()
|
||||
initPasswordsTableEntries()
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnPasswordUpdatedNotification), name: NSNotification.Name(rawValue: "passwordUpdated"), object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnPasswordStoreErasedNotification), name: NSNotification.Name(rawValue: "passwordStoreErased"), object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnSearchNotification), name: NSNotification.Name(rawValue: "search"), object: nil)
|
||||
|
||||
generateSections(item: passwordEntities!)
|
||||
generateSections(item: passwordsTableEntries)
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
searchController.searchResultsUpdater = self
|
||||
|
|
@ -134,17 +160,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
|
||||
var password: PasswordEntity
|
||||
let index = sections[indexPath.section].index + indexPath.row
|
||||
if searchController.isActive && searchController.searchBar.text != "" {
|
||||
password = filteredPasswordEntities[index]
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
if !entry.isDir {
|
||||
if entry.passwordEntity!.synced {
|
||||
cell.textLabel?.text = entry.title
|
||||
} else {
|
||||
cell.textLabel?.text = "↻ \(entry.title)"
|
||||
}
|
||||
} else {
|
||||
password = passwordEntities![index]
|
||||
}
|
||||
if password.synced {
|
||||
cell.textLabel?.text = password.name!
|
||||
} else {
|
||||
cell.textLabel?.text = "↻ \(password.name!)"
|
||||
cell.textLabel?.text = "\(entry.title)/"
|
||||
}
|
||||
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
|
||||
longPressGestureRecognizer.minimumPressDuration = 0.6
|
||||
|
|
@ -152,6 +176,35 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
return cell
|
||||
}
|
||||
|
||||
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry{
|
||||
var entry: PasswordsTableEntry
|
||||
let index = sections[indexPath.section].index + indexPath.row
|
||||
if searchController.isActive && searchController.searchBar.text != "" {
|
||||
entry = filteredPasswordsTableEntries[index]
|
||||
} else {
|
||||
entry = passwordsTableEntries[index]
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
if !entry.isDir {
|
||||
performSegue(withIdentifier: "showPasswordDetail", sender: tableView.cellForRow(at: indexPath))
|
||||
} else {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
parentPasswordEntity = entry.passwordEntity
|
||||
initPasswordsTableEntries()
|
||||
reloadTableView(data: passwordsTableEntries)
|
||||
}
|
||||
}
|
||||
|
||||
func backAction(_ sender: Any?) {
|
||||
parentPasswordEntity = parentPasswordEntity?.parent
|
||||
initPasswordsTableEntries()
|
||||
reloadTableView(data: passwordsTableEntries)
|
||||
}
|
||||
|
||||
func longPressAction(_ gesture: UILongPressGestureRecognizer) {
|
||||
if gesture.state == UIGestureRecognizerState.began {
|
||||
let touchPoint = gesture.location(in: tableView)
|
||||
|
|
@ -185,9 +238,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
let index = sections[indexPath.section].index + indexPath.row
|
||||
let password: PasswordEntity
|
||||
if searchController.isActive && searchController.searchBar.text != "" {
|
||||
password = filteredPasswordEntities[index]
|
||||
password = passwordsTableEntries[index].passwordEntity!
|
||||
} else {
|
||||
password = passwordEntities![index]
|
||||
password = filteredPasswordsTableEntries[index].passwordEntity!
|
||||
}
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
var passphrase = ""
|
||||
|
|
@ -232,33 +285,34 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
|
||||
func generateSections(item: [PasswordEntity]) {
|
||||
func generateSections(item: [PasswordsTableEntry]) {
|
||||
sections.removeAll()
|
||||
if item.count == 0 {
|
||||
guard item.count != 0 else {
|
||||
return
|
||||
}
|
||||
var index = 0
|
||||
for i in 0 ..< item.count {
|
||||
let name = item[index].name!.uppercased()
|
||||
let commonPrefix = item[i].name!.commonPrefix(with: name, options: .caseInsensitive)
|
||||
let title = item[index].title.uppercased()
|
||||
let commonPrefix = item[i].title.commonPrefix(with: title, options: .caseInsensitive)
|
||||
if commonPrefix.characters.count == 0 {
|
||||
let firstCharacter = name[name.startIndex]
|
||||
let firstCharacter = title[title.startIndex]
|
||||
let newSection = (index: index, length: i - index, title: "\(firstCharacter)")
|
||||
sections.append(newSection)
|
||||
index = i
|
||||
}
|
||||
}
|
||||
let name = item[index].name!.uppercased()
|
||||
let firstCharacter = name[name.startIndex]
|
||||
let title = item[index].title.uppercased()
|
||||
let firstCharacter = title[title.startIndex]
|
||||
let newSection = (index: index, length: item.count - index, title: "\(firstCharacter)")
|
||||
sections.append(newSection)
|
||||
}
|
||||
|
||||
func actOnPasswordUpdatedNotification() {
|
||||
passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData()
|
||||
reloadTableView(data: passwordEntities!)
|
||||
initPasswordsTableEntries()
|
||||
reloadTableView(data: passwordsTableEntries)
|
||||
setNavigationItemTitle()
|
||||
}
|
||||
|
||||
private func setNavigationItemTitle() {
|
||||
let numberOfUnsynced = PasswordStore.shared.getNumberOfUnsyncedPasswords()
|
||||
if numberOfUnsynced == 0 {
|
||||
|
|
@ -269,8 +323,8 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
|
||||
func actOnPasswordStoreErasedNotification() {
|
||||
passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData()
|
||||
reloadTableView(data: passwordEntities!)
|
||||
initPasswordsTableEntries()
|
||||
reloadTableView(data: passwordsTableEntries)
|
||||
setNavigationItemTitle()
|
||||
}
|
||||
|
||||
|
|
@ -297,28 +351,20 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
if segue.identifier == "showPasswordDetail" {
|
||||
if let viewController = segue.destination as? PasswordDetailTableViewController {
|
||||
let selectedIndexPath = self.tableView.indexPath(for: sender as! UITableViewCell)!
|
||||
let index = sections[selectedIndexPath.section].index + selectedIndexPath.row
|
||||
let passwordEntity: PasswordEntity
|
||||
if searchController.isActive && searchController.searchBar.text != "" {
|
||||
passwordEntity = filteredPasswordEntities[index]
|
||||
} else {
|
||||
passwordEntity = passwordEntities![index]
|
||||
}
|
||||
let passwordEntity = getPasswordEntry(by: selectedIndexPath).passwordEntity!
|
||||
viewController.passwordEntity = passwordEntity
|
||||
let passwordCategoryEntities = PasswordStore.shared.fetchPasswordCategoryEntityCoreData(password: passwordEntity)
|
||||
viewController.passwordCategoryEntities = passwordCategoryEntities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func filterContentForSearchText(searchText: String, scope: String = "All") {
|
||||
filteredPasswordEntities = passwordEntities!.filter { password in
|
||||
return password.name!.lowercased().contains(searchText.lowercased())
|
||||
filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in
|
||||
return entry.title.lowercased().contains(searchText.lowercased())
|
||||
}
|
||||
if searchController.isActive && searchController.searchBar.text != "" {
|
||||
reloadTableView(data: filteredPasswordEntities)
|
||||
reloadTableView(data: filteredPasswordsTableEntries)
|
||||
} else {
|
||||
reloadTableView(data: passwordEntities!)
|
||||
reloadTableView(data: passwordsTableEntries)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -328,7 +374,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
refreshControl.attributedTitle = NSAttributedString(string: atribbutedTitle)
|
||||
}
|
||||
|
||||
func reloadTableView (data: [PasswordEntity]) {
|
||||
func reloadTableView(data: [PasswordsTableEntry]) {
|
||||
if parentPasswordEntity != nil {
|
||||
navigationItem.leftBarButtonItem = backUIBarButtonItem
|
||||
} else {
|
||||
navigationItem.leftBarButtonItem = nil
|
||||
}
|
||||
generateSections(item: data)
|
||||
tableView.reloadData()
|
||||
updateRefreshControlTitle()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import SwiftyUserDefaults
|
|||
extension PasswordEntity {
|
||||
func decrypt(passphrase: String) throws -> Password? {
|
||||
var password: Password?
|
||||
let encryptedDataPath = URL(fileURLWithPath: "\(Globals.repositoryPath)/\(rawPath!)")
|
||||
let encryptedDataPath = URL(fileURLWithPath: "\(Globals.repositoryPath)/\(path!)")
|
||||
let encryptedData = try Data(contentsOf: encryptedDataPath)
|
||||
let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase)
|
||||
let plainText = String(data: decryptedData, encoding: .utf8) ?? ""
|
||||
|
|
|
|||
|
|
@ -237,27 +237,56 @@ class PasswordStore {
|
|||
try storeRepository?.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func updatePasswordEntityCoreData() {
|
||||
deleteCoreData(entityName: "PasswordEntity")
|
||||
deleteCoreData(entityName: "PasswordCategoryEntity")
|
||||
let fm = FileManager.default
|
||||
fm.enumerator(atPath: self.storeURL.path)?.forEach({ (e) in
|
||||
if let e = e as? String, let url = URL(string: e) {
|
||||
if url.pathExtension == "gpg" {
|
||||
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
|
||||
let endIndex = url.lastPathComponent.index(url.lastPathComponent.endIndex, offsetBy: -4)
|
||||
passwordEntity.name = url.lastPathComponent.substring(to: endIndex)
|
||||
passwordEntity.rawPath = "\(url.path)"
|
||||
let items = url.path.characters.split(separator: "/").map(String.init)
|
||||
for i in 0 ..< items.count - 1 {
|
||||
let passwordCategoryEntity = PasswordCategoryEntity(context: context)
|
||||
passwordCategoryEntity.category = items[i]
|
||||
passwordCategoryEntity.level = Int16(i)
|
||||
passwordCategoryEntity.password = passwordEntity
|
||||
do {
|
||||
var q = try fm.contentsOfDirectory(atPath: self.storeURL.path).filter{
|
||||
!$0.hasPrefix(".")
|
||||
}.map { (filename) -> PasswordEntity in
|
||||
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
|
||||
if filename.hasSuffix(".gpg") {
|
||||
passwordEntity.name = filename.substring(to: filename.index(filename.endIndex, offsetBy: -4))
|
||||
} else {
|
||||
passwordEntity.name = filename
|
||||
}
|
||||
passwordEntity.path = filename
|
||||
passwordEntity.parent = nil
|
||||
return passwordEntity
|
||||
}
|
||||
while q.count > 0 {
|
||||
let e = q.first!
|
||||
q.remove(at: 0)
|
||||
guard !e.name!.hasPrefix(".") else {
|
||||
continue
|
||||
}
|
||||
var isDirectory: ObjCBool = false
|
||||
let filePath = storeURL.appendingPathComponent(e.path!).path
|
||||
if fm.fileExists(atPath: filePath, isDirectory: &isDirectory) {
|
||||
if isDirectory.boolValue {
|
||||
e.isDir = true
|
||||
let files = try fm.contentsOfDirectory(atPath: filePath).map { (filename) -> PasswordEntity in
|
||||
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
|
||||
if filename.hasSuffix(".gpg") {
|
||||
passwordEntity.name = filename.substring(to: filename.index(filename.endIndex, offsetBy: -4))
|
||||
} else {
|
||||
passwordEntity.name = filename
|
||||
}
|
||||
passwordEntity.path = "\(e.path!)/\(filename)"
|
||||
passwordEntity.parent = e
|
||||
return passwordEntity
|
||||
}
|
||||
q += files
|
||||
} else {
|
||||
e.isDir = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
|
|
@ -281,9 +310,10 @@ class PasswordStore {
|
|||
return commits
|
||||
}
|
||||
|
||||
func fetchPasswordEntityCoreData() -> [PasswordEntity] {
|
||||
func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] {
|
||||
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
do {
|
||||
passwordEntityFetch.predicate = NSPredicate(format: "parent = %@", parent ?? 0)
|
||||
let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity]
|
||||
return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending }
|
||||
} catch {
|
||||
|
|
@ -291,18 +321,21 @@ class PasswordStore {
|
|||
}
|
||||
}
|
||||
|
||||
func fetchPasswordCategoryEntityCoreData(password: PasswordEntity) -> [PasswordCategoryEntity] {
|
||||
let passwordCategoryEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordCategoryEntity")
|
||||
passwordCategoryEntityFetchRequest.predicate = NSPredicate(format: "password = %@", password)
|
||||
passwordCategoryEntityFetchRequest.sortDescriptors = [NSSortDescriptor(key: "level", ascending: true)]
|
||||
func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] {
|
||||
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
do {
|
||||
let passwordCategoryEntities = try context.fetch(passwordCategoryEntityFetchRequest) as! [PasswordCategoryEntity]
|
||||
return passwordCategoryEntities
|
||||
if !withDir {
|
||||
passwordEntityFetch.predicate = NSPredicate(format: "isDir = false")
|
||||
|
||||
}
|
||||
let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity]
|
||||
return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending }
|
||||
} catch {
|
||||
fatalError("Failed to fetch password categories: \(error)")
|
||||
fatalError("Failed to fetch passwords: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func fetchUnsyncedPasswords() -> [PasswordEntity] {
|
||||
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
|
||||
|
|
@ -452,7 +485,9 @@ class PasswordStore {
|
|||
progressBlock(0.3)
|
||||
let saveURL = storeURL.appendingPathComponent("\(password.name).gpg")
|
||||
try encryptedData.write(to: saveURL)
|
||||
passwordEntity.rawPath = "\(password.name).gpg"
|
||||
passwordEntity.name = password.name
|
||||
passwordEntity.path = "\(password.name).gpg"
|
||||
passwordEntity.parent = nil
|
||||
passwordEntity.synced = false
|
||||
try context.save()
|
||||
print(saveURL.path)
|
||||
|
|
@ -466,7 +501,7 @@ class PasswordStore {
|
|||
func update(passwordEntity: PasswordEntity, password: Password, progressBlock: (_ progress: Float) -> Void) {
|
||||
do {
|
||||
let encryptedData = try passwordEntity.encrypt(password: password)
|
||||
let saveURL = storeURL.appendingPathComponent(passwordEntity.rawPath!)
|
||||
let saveURL = storeURL.appendingPathComponent(passwordEntity.path!)
|
||||
try encryptedData.write(to: saveURL)
|
||||
progressBlock(0.3)
|
||||
let _ = createAddCommitInRepository(message: "Update password by pass for iOS", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11759" systemVersion="16D32" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="PasswordCategoryEntity" representedClassName="PasswordCategoryEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="category" attributeType="String" syncable="YES"/>
|
||||
<attribute name="level" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" indexed="YES" syncable="YES"/>
|
||||
<relationship name="password" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PasswordEntity" inverseName="categories" inverseEntity="PasswordEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12124.1" systemVersion="16E175b" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="PasswordEntity" representedClassName="PasswordEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
|
||||
<attribute name="isDir" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="raw" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
|
||||
<attribute name="rawPath" attributeType="String" syncable="YES"/>
|
||||
<attribute name="path" attributeType="String" syncable="YES"/>
|
||||
<attribute name="synced" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<relationship name="categories" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PasswordCategoryEntity" inverseName="password" inverseEntity="PasswordCategoryEntity" syncable="YES"/>
|
||||
<relationship name="children" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PasswordEntity" inverseName="parent" inverseEntity="PasswordEntity" syncable="YES"/>
|
||||
<relationship name="parent" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PasswordEntity" inverseName="children" inverseEntity="PasswordEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="PasswordCategoryEntity" positionX="115" positionY="-9" width="128" height="90"/>
|
||||
<element name="PasswordEntity" positionX="-63" positionY="-18" width="128" height="135"/>
|
||||
<element name="PasswordEntity" positionX="36" positionY="81" width="128" height="150"/>
|
||||
</elements>
|
||||
</model>
|
||||
Loading…
Add table
Add a link
Reference in a new issue