Support folder in password view

- change core data
  - change data struct to store table view entry
  - delete unnecessary functions
This commit is contained in:
Bob Sun 2017-03-02 14:51:40 +08:00
parent 98b01d16cf
commit 050a960167
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
7 changed files with 177 additions and 89 deletions

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <device id="retina5_5" orientation="portrait">
<adaptation id="fullscreen"/> <adaptation id="fullscreen"/>
</device> </device>
@ -43,9 +43,6 @@
</label> </label>
</subviews> </subviews>
</tableViewCellContentView> </tableViewCellContentView>
<connections>
<segue destination="tW4-E9-CGv" kind="show" identifier="showPasswordDetail" id="26n-ZD-G0k"/>
</connections>
</tableViewCell> </tableViewCell>
</prototypes> </prototypes>
<sections/> <sections/>
@ -63,6 +60,7 @@
</navigationItem> </navigationItem>
<connections> <connections>
<outlet property="tableView" destination="Tn1-q5-vaJ" id="UHc-AS-gXh"/> <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> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6ju-JT-yds" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="6ju-JT-yds" userLabel="First Responder" sceneMemberID="firstResponder"/>

View file

@ -31,7 +31,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
numberFormatter.numberStyle = NumberFormatter.Style.decimal numberFormatter.numberStyle = NumberFormatter.Style.decimal
let fm = FileManager.default 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))! let numberOfPasswords = numberFormatter.string(from: NSNumber(value: passwordEntities.count))!
var size = UInt64(0) var size = UInt64(0)

View file

@ -13,7 +13,6 @@ import SVProgressHUD
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate { class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
var passwordEntity: PasswordEntity? var passwordEntity: PasswordEntity?
var passwordCategoryEntities: [PasswordCategoryEntity]?
var passwordCategoryText = "" var passwordCategoryText = ""
var password: Password? var password: Password?
var passwordImage: UIImage? var passwordImage: UIImage?
@ -62,13 +61,23 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
var tableData = Array<TableSection>() 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() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell") tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell") tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
let passwordCategoryArray = passwordCategoryEntities?.map { $0.category! } passwordCategoryText = generateCategoryText()
passwordCategoryText = (passwordCategoryArray?.joined(separator: " > "))!
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:))) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
tableView.addGestureRecognizer(tapGesture) tableView.addGestureRecognizer(tapGesture)
@ -300,7 +309,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
footerLabel.numberOfLines = 0 footerLabel.numberOfLines = 0
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote) footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
footerLabel.textColor = UIColor.gray 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")" footerLabel.text = "Last Updated: \(dateString ?? "Unknown")"
view.addSubview(footerLabel) view.addSubview(footerLabel)
return view return view

View file

@ -11,9 +11,29 @@ import SVProgressHUD
import SwiftyUserDefaults import SwiftyUserDefaults
import PasscodeLock import PasscodeLock
enum PasswordsTableEntryType {
case password, dir
}
struct PasswordsTableEntry {
var title: String
var isDir: Bool
var passwordEntity: PasswordEntity?
}
class PasswordsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { class PasswordsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private var passwordEntities: [PasswordEntity]? private var passwordsTableEntries: [PasswordsTableEntry] = []
var filteredPasswordEntities = [PasswordEntity]() 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 sections : [(index: Int, length :Int, title: String)] = Array()
var searchActive : Bool = false var searchActive : Bool = false
let searchController = UISearchController(searchResultsController: nil) let searchController = UISearchController(searchResultsController: nil)
@ -23,6 +43,10 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
return refreshControl return refreshControl
}() }()
let searchBarView = UIView(frame: CGRect(x: 0, y: 64, width: UIScreen.main.bounds.width, height: 44)) 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! @IBOutlet weak var tableView: UITableView!
@ -48,6 +72,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
func syncPasswords() { func syncPasswords() {
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
@ -69,8 +94,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
DispatchQueue.main.async { DispatchQueue.main.async {
PasswordStore.shared.updatePasswordEntityCoreData() PasswordStore.shared.updatePasswordEntityCoreData()
self.passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData() self.parentPasswordEntity = nil
self.reloadTableView(data: self.passwordEntities!) self.initPasswordsTableEntries()
self.reloadTableView(data: self.passwordsTableEntries)
PasswordStore.shared.setAllSynced() PasswordStore.shared.setAllSynced()
self.setNavigationItemTitle() self.setNavigationItemTitle()
Defaults[.lastUpdatedTime] = Date() Defaults[.lastUpdatedTime] = Date()
@ -90,12 +116,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
setNavigationItemTitle() 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.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.actOnPasswordStoreErasedNotification), name: NSNotification.Name(rawValue: "passwordStoreErased"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnSearchNotification), name: NSNotification.Name(rawValue: "search"), 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.delegate = self
tableView.dataSource = self tableView.dataSource = self
searchController.searchResultsUpdater = self searchController.searchResultsUpdater = self
@ -134,17 +160,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
var password: PasswordEntity let entry = getPasswordEntry(by: indexPath)
let index = sections[indexPath.section].index + indexPath.row if !entry.isDir {
if searchController.isActive && searchController.searchBar.text != "" { if entry.passwordEntity!.synced {
password = filteredPasswordEntities[index] cell.textLabel?.text = entry.title
} else {
cell.textLabel?.text = "\(entry.title)"
}
} else { } else {
password = passwordEntities![index] cell.textLabel?.text = "\(entry.title)/"
}
if password.synced {
cell.textLabel?.text = password.name!
} else {
cell.textLabel?.text = "\(password.name!)"
} }
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:))) let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
longPressGestureRecognizer.minimumPressDuration = 0.6 longPressGestureRecognizer.minimumPressDuration = 0.6
@ -152,6 +176,35 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
return cell 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) { func longPressAction(_ gesture: UILongPressGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.began { if gesture.state == UIGestureRecognizerState.began {
let touchPoint = gesture.location(in: tableView) let touchPoint = gesture.location(in: tableView)
@ -185,9 +238,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
let index = sections[indexPath.section].index + indexPath.row let index = sections[indexPath.section].index + indexPath.row
let password: PasswordEntity let password: PasswordEntity
if searchController.isActive && searchController.searchBar.text != "" { if searchController.isActive && searchController.searchBar.text != "" {
password = filteredPasswordEntities[index] password = passwordsTableEntries[index].passwordEntity!
} else { } else {
password = passwordEntities![index] password = filteredPasswordsTableEntries[index].passwordEntity!
} }
UIImpactFeedbackGenerator(style: .medium).impactOccurred() UIImpactFeedbackGenerator(style: .medium).impactOccurred()
var passphrase = "" var passphrase = ""
@ -232,33 +285,34 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
func generateSections(item: [PasswordEntity]) { func generateSections(item: [PasswordsTableEntry]) {
sections.removeAll() sections.removeAll()
if item.count == 0 { guard item.count != 0 else {
return return
} }
var index = 0 var index = 0
for i in 0 ..< item.count { for i in 0 ..< item.count {
let name = item[index].name!.uppercased() let title = item[index].title.uppercased()
let commonPrefix = item[i].name!.commonPrefix(with: name, options: .caseInsensitive) let commonPrefix = item[i].title.commonPrefix(with: title, options: .caseInsensitive)
if commonPrefix.characters.count == 0 { if commonPrefix.characters.count == 0 {
let firstCharacter = name[name.startIndex] let firstCharacter = title[title.startIndex]
let newSection = (index: index, length: i - index, title: "\(firstCharacter)") let newSection = (index: index, length: i - index, title: "\(firstCharacter)")
sections.append(newSection) sections.append(newSection)
index = i index = i
} }
} }
let name = item[index].name!.uppercased() let title = item[index].title.uppercased()
let firstCharacter = name[name.startIndex] let firstCharacter = title[title.startIndex]
let newSection = (index: index, length: item.count - index, title: "\(firstCharacter)") let newSection = (index: index, length: item.count - index, title: "\(firstCharacter)")
sections.append(newSection) sections.append(newSection)
} }
func actOnPasswordUpdatedNotification() { func actOnPasswordUpdatedNotification() {
passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData() initPasswordsTableEntries()
reloadTableView(data: passwordEntities!) reloadTableView(data: passwordsTableEntries)
setNavigationItemTitle() setNavigationItemTitle()
} }
private func setNavigationItemTitle() { private func setNavigationItemTitle() {
let numberOfUnsynced = PasswordStore.shared.getNumberOfUnsyncedPasswords() let numberOfUnsynced = PasswordStore.shared.getNumberOfUnsyncedPasswords()
if numberOfUnsynced == 0 { if numberOfUnsynced == 0 {
@ -269,8 +323,8 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
func actOnPasswordStoreErasedNotification() { func actOnPasswordStoreErasedNotification() {
passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData() initPasswordsTableEntries()
reloadTableView(data: passwordEntities!) reloadTableView(data: passwordsTableEntries)
setNavigationItemTitle() setNavigationItemTitle()
} }
@ -297,28 +351,20 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
if segue.identifier == "showPasswordDetail" { if segue.identifier == "showPasswordDetail" {
if let viewController = segue.destination as? PasswordDetailTableViewController { if let viewController = segue.destination as? PasswordDetailTableViewController {
let selectedIndexPath = self.tableView.indexPath(for: sender as! UITableViewCell)! let selectedIndexPath = self.tableView.indexPath(for: sender as! UITableViewCell)!
let index = sections[selectedIndexPath.section].index + selectedIndexPath.row let passwordEntity = getPasswordEntry(by: selectedIndexPath).passwordEntity!
let passwordEntity: PasswordEntity
if searchController.isActive && searchController.searchBar.text != "" {
passwordEntity = filteredPasswordEntities[index]
} else {
passwordEntity = passwordEntities![index]
}
viewController.passwordEntity = passwordEntity viewController.passwordEntity = passwordEntity
let passwordCategoryEntities = PasswordStore.shared.fetchPasswordCategoryEntityCoreData(password: passwordEntity)
viewController.passwordCategoryEntities = passwordCategoryEntities
} }
} }
} }
func filterContentForSearchText(searchText: String, scope: String = "All") { func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredPasswordEntities = passwordEntities!.filter { password in filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in
return password.name!.lowercased().contains(searchText.lowercased()) return entry.title.lowercased().contains(searchText.lowercased())
} }
if searchController.isActive && searchController.searchBar.text != "" { if searchController.isActive && searchController.searchBar.text != "" {
reloadTableView(data: filteredPasswordEntities) reloadTableView(data: filteredPasswordsTableEntries)
} else { } else {
reloadTableView(data: passwordEntities!) reloadTableView(data: passwordsTableEntries)
} }
} }
@ -328,7 +374,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
refreshControl.attributedTitle = NSAttributedString(string: atribbutedTitle) 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) generateSections(item: data)
tableView.reloadData() tableView.reloadData()
updateRefreshControlTitle() updateRefreshControlTitle()

View file

@ -12,7 +12,7 @@ import SwiftyUserDefaults
extension PasswordEntity { extension PasswordEntity {
func decrypt(passphrase: String) throws -> Password? { func decrypt(passphrase: String) throws -> Password? {
var password: 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 encryptedData = try Data(contentsOf: encryptedDataPath)
let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase) let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase)
let plainText = String(data: decryptedData, encoding: .utf8) ?? "" let plainText = String(data: decryptedData, encoding: .utf8) ?? ""

View file

@ -237,27 +237,56 @@ class PasswordStore {
try storeRepository?.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock) try storeRepository?.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock)
} }
func updatePasswordEntityCoreData() { func updatePasswordEntityCoreData() {
deleteCoreData(entityName: "PasswordEntity") deleteCoreData(entityName: "PasswordEntity")
deleteCoreData(entityName: "PasswordCategoryEntity")
let fm = FileManager.default let fm = FileManager.default
fm.enumerator(atPath: self.storeURL.path)?.forEach({ (e) in do {
if let e = e as? String, let url = URL(string: e) { var q = try fm.contentsOfDirectory(atPath: self.storeURL.path).filter{
if url.pathExtension == "gpg" { !$0.hasPrefix(".")
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity }.map { (filename) -> PasswordEntity in
let endIndex = url.lastPathComponent.index(url.lastPathComponent.endIndex, offsetBy: -4) let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
passwordEntity.name = url.lastPathComponent.substring(to: endIndex) if filename.hasSuffix(".gpg") {
passwordEntity.rawPath = "\(url.path)" passwordEntity.name = filename.substring(to: filename.index(filename.endIndex, offsetBy: -4))
let items = url.path.characters.split(separator: "/").map(String.init) } else {
for i in 0 ..< items.count - 1 { passwordEntity.name = filename
let passwordCategoryEntity = PasswordCategoryEntity(context: context) }
passwordCategoryEntity.category = items[i] passwordEntity.path = filename
passwordCategoryEntity.level = Int16(i) passwordEntity.parent = nil
passwordCategoryEntity.password = passwordEntity 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 { do {
try context.save() try context.save()
} catch { } catch {
@ -281,9 +310,10 @@ class PasswordStore {
return commits return commits
} }
func fetchPasswordEntityCoreData() -> [PasswordEntity] { func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] {
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
passwordEntityFetch.predicate = NSPredicate(format: "parent = %@", parent ?? 0)
let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity] let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity]
return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending } return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending }
} catch { } catch {
@ -291,18 +321,21 @@ class PasswordStore {
} }
} }
func fetchPasswordCategoryEntityCoreData(password: PasswordEntity) -> [PasswordCategoryEntity] { func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] {
let passwordCategoryEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordCategoryEntity") let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
passwordCategoryEntityFetchRequest.predicate = NSPredicate(format: "password = %@", password)
passwordCategoryEntityFetchRequest.sortDescriptors = [NSSortDescriptor(key: "level", ascending: true)]
do { do {
let passwordCategoryEntities = try context.fetch(passwordCategoryEntityFetchRequest) as! [PasswordCategoryEntity] if !withDir {
return passwordCategoryEntities passwordEntityFetch.predicate = NSPredicate(format: "isDir = false")
}
let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity]
return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending }
} catch { } catch {
fatalError("Failed to fetch password categories: \(error)") fatalError("Failed to fetch passwords: \(error)")
} }
} }
func fetchUnsyncedPasswords() -> [PasswordEntity] { func fetchUnsyncedPasswords() -> [PasswordEntity] {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0) passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
@ -452,7 +485,9 @@ class PasswordStore {
progressBlock(0.3) progressBlock(0.3)
let saveURL = storeURL.appendingPathComponent("\(password.name).gpg") let saveURL = storeURL.appendingPathComponent("\(password.name).gpg")
try encryptedData.write(to: saveURL) 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 passwordEntity.synced = false
try context.save() try context.save()
print(saveURL.path) print(saveURL.path)
@ -466,7 +501,7 @@ class PasswordStore {
func update(passwordEntity: PasswordEntity, password: Password, progressBlock: (_ progress: Float) -> Void) { func update(passwordEntity: PasswordEntity, password: Password, progressBlock: (_ progress: Float) -> Void) {
do { do {
let encryptedData = try passwordEntity.encrypt(password: password) let encryptedData = try passwordEntity.encrypt(password: password)
let saveURL = storeURL.appendingPathComponent(passwordEntity.rawPath!) let saveURL = storeURL.appendingPathComponent(passwordEntity.path!)
try encryptedData.write(to: saveURL) try encryptedData.write(to: saveURL)
progressBlock(0.3) progressBlock(0.3)
let _ = createAddCommitInRepository(message: "Update password by pass for iOS", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock) let _ = createAddCommitInRepository(message: "Update password by pass for iOS", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock)

View file

@ -1,20 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?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=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12124.1" systemVersion="16E175b" 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>
<entity name="PasswordEntity" representedClassName="PasswordEntity" syncable="YES" codeGenerationType="class"> <entity name="PasswordEntity" representedClassName="PasswordEntity" syncable="YES" codeGenerationType="class">
<attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/> <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="name" attributeType="String" syncable="YES"/>
<attribute name="raw" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/> <attribute name="path" attributeType="String" syncable="YES"/>
<attribute name="rawPath" attributeType="String" syncable="YES"/>
<attribute name="synced" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" 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> </entity>
<elements> <elements>
<element name="PasswordCategoryEntity" positionX="115" positionY="-9" width="128" height="90"/> <element name="PasswordEntity" positionX="36" positionY="81" width="128" height="150"/>
<element name="PasswordEntity" positionX="-63" positionY="-18" width="128" height="135"/>
</elements> </elements>
</model> </model>