Format code with SwiftFormat automatically in every build

This commit is contained in:
Danny Moesch 2020-06-28 21:25:40 +02:00 committed by Mingshen Sun
parent f167ab7549
commit 7f9f0e43b2
100 changed files with 1124 additions and 1063 deletions

1
.swift-version Normal file
View file

@ -0,0 +1 @@
5.3

96
.swiftformat Normal file
View file

@ -0,0 +1,96 @@
# Exclude folders
--exclude Carthage,go,Pods
# Enabled rules
--rules andOperator, anyObjectProtocol, blankLinesAroundMark, blankLinesAtEndOfScope, blankLinesAtStartOfScope, blankLinesBetweenScopes, braces, consecutiveBlankLines, consecutiveSpaces, duplicateImports, elseOnSameLine, emptyBraces, fileHeader, hoistPatternLet, indent, isEmpty, leadingDelimiters, linebreakAtEndOfFile, linebreaks, modifierOrder, numberFormatting, preferKeyPath, redundantBackticks, redundantBreak, redundantExtensionACL, redundantFileprivate, redundantGet, redundantInit, redundantLet, redundantLetError, redundantNilInit, redundantObjc, redundantParens, redundantPattern, redundantRawValues, redundantReturn, redundantSelf, redundantVoidReturnType, semicolons, sortedImports, spaceAroundBraces, spaceAroundBrackets, spaceAroundComments, spaceAroundGenerics, spaceAroundOperators, spaceAroundParens, spaceInsideBraces, spaceInsideBrackets, spaceInsideComments, spaceInsideGenerics, spaceInsideParens, strongOutlets, strongifiedSelf, todos, trailingClosures, trailingCommas, trailingSpace, typeSugar, unusedArguments, void, wrap, wrapArguments, wrapAttributes, yodaConditions
# Formatting options
## Use allman indentation style: "true" or "false" (default)
--allman false
## Binary grouping,threshold (default: 4,8) or "none", "ignore"
--binarygrouping 4,8
## Closing paren position: "balanced" (default) or "same-line"
--closingparen balanced
## Commas in collection literals: "always" (default) or "inline"
--commas always
## Decimal grouping,threshold (default: 3,6) or "none", "ignore"
--decimalgrouping 3,6
## Placement of else/catch: "same-line" (default) or "next-line"
--elseposition same-line
## Case of 'e' in numbers: "lowercase" or "uppercase" (default)
--exponentcase lowercase
## Group exponent digits: "enabled" or "disabled" (default)
--exponentgrouping disabled
## Group digits after '.': "enabled" or "disabled" (default)
--fractiongrouping disabled
## Function @attributes: "preserve", "prev-line", or "same-line"
--funcattributes prev-line
## Guard else: "same-line", "next-line" or "auto" (default)
--guardelse auto
## Header comments: "strip", "ignore", or the text you wish use
--header ignore
## Hex grouping,threshold (default: 4,8) or "none", "ignore"
--hexgrouping 4,8
## Casing for hex literals: "uppercase" (default) or "lowercase"
--hexliteralcase uppercase
## #if indenting: "indent" (default), "no-indent" or "outdent"
--ifdef indent
## "testable-top", "testable-bottom" or "alphabetized" (default)
--importgrouping testable-bottom
## Number of spaces to indent, or "tab" to use tabs
--indent 4
## Indent cases inside a switch: "true" or "false" (default)
--indentcase false
## Linebreak character to use: "cr", "crlf" or "lf" (default)
--linebreaks lf
## Maximum length of a line before wrapping. defaults to "none"
--maxwidth none
## Comma-delimited list of modifiers in preferred order
--modifierorder
## Comma-delimited list of operators without surrounding space
--nospaceoperators
## Comma-delimited list of operators that shouldn't be wrapped
--nowrapoperators
## Octal grouping,threshold (default: 4,8) or "none", "ignore"
--octalgrouping 4,8
## Spacing for operator funcs: "spaced" (default) or "no-space"
--operatorfunc spaced
## let/var placement in patterns: "hoist" (default) or "inline"
--patternlet hoist
## Explicit self: "insert", "remove" (default) or "init-only"
--self init-only
## Comma-delimited list of functions with @autoclosure arguments
--selfrequired
## Allow semicolons: "never" or "inline" (default)
--semicolons inline
## Use ? for Optionals "always" (default) or "except-properties"
--shortoptionals always
## Align code independently of tab width. defaults to "enabled"
--smarttabs enabled
## "closure-only", "unnamed-only" or "always" (default)
--stripunusedargs always
## The width of a tab character. Defaults to "unspecified"
--tabwidth 4
## Comma-delimited list of functions that use trailing closures
--trailingclosures
## Trim trailing space: "always" (default) or "nonblank-lines"
--trimwhitespace always
## Type @attributes: "preserve", "prev-line", or "same-line"
--typeattributes preserve
## Property @attributes: "preserve", "prev-line", or "same-line"
--varattributes same-line
## How Void types are represented: "void" (default) or "tuple"
--voidtype void
## Wrap all arguments: "before-first", "after-first", "preserve"
--wraparguments preserve
## Wrap array/dict: "before-first", "after-first", "preserve"
--wrapcollections preserve
## Wrap func params: "before-first", "after-first", "preserve"
--wrapparameters preserve
## Xcode indent guard/enum: "enabled" or "disabled" (default)
--xcodeindentation disabled
## Swap yoda values: "always" (default) or "literals-only"
--yodaswap always

View file

@ -6,6 +6,7 @@ addons:
- go
- gnupg2
- pass
- swiftformat
before_install:
- echo -e "machine github.com\n login $GITHUB_ACCESS_TOKEN" >> ~/.netrc
install:

View file

@ -1080,6 +1080,7 @@
A26700191EEC450100176B8A /* Embed App Extensions */,
A26075921EEC6F34005DB03E /* Embed Frameworks */,
9AF6A4F532EB900EE22C80EA /* [CP] Embed Pods Frameworks */,
3005F34F24A9143C000519B5 /* ShellScript */,
);
buildRules = (
);
@ -1321,6 +1322,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3005F34F24A9143C000519B5 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "SWIFTFORMAT_VERSION=\"0.45.*\"\n\nif which swiftformat > /dev/null; then\n if [[ \"$(swiftformat --version)\" == $SWIFTFORMAT_VERSION ]]; then\n swiftformat .\n else\n echo \"Failure: SwiftFormat $SWIFTFORMAT_VERSION is required. Install it or update the build script to use a newer version.\"\n exit 1\n fi\nelse\n echo \"Failure: SwiftFormat not installed. Get it via 'brew install swiftformat'.\"\n exit 2\nfi\n";
};
3EFC287772C1D2B2762FAC45 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;

View file

@ -6,15 +6,14 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import CoreData
import SVProgressHUD
import passKit
import SVProgressHUD
import SwiftyUserDefaults
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
enum ViewTag: Int {
case blur = 100, appicon
@ -25,50 +24,51 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return presenter
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
func application(_: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
SVProgressHUD.setMinimumSize(CGSize(width: 150, height: 100))
passcodeLockPresenter.present(windowLevel: UIApplication.shared.windows.last?.windowLevel.rawValue)
if let shortcutItem = launchOptions?[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem {
if shortcutItem.type == Globals.bundleIdentifier + ".search" {
self.perform(#selector(postSearchNotification), with: nil, afterDelay: 0.4)
perform(#selector(postSearchNotification), with: nil, afterDelay: 0.4)
}
}
return true
}
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
func application(_: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if let _ = window?.rootViewController as? PasscodeLockViewController {
window?.frame = UIScreen.main.bounds
}
return .all
}
@objc func postSearchNotification() {
@objc
func postSearchNotification() {
NotificationCenter.default.post(name: .passwordSearch, object: nil)
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
func application(_: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler _: @escaping (Bool) -> Void) {
if shortcutItem.type == Globals.bundleIdentifier + ".search" {
let tabBarController = self.window!.rootViewController as! UITabBarController
let tabBarController = window!.rootViewController as! UITabBarController
tabBarController.selectedIndex = 0
let navigationController = tabBarController.selectedViewController as! UINavigationController
navigationController.popToRootViewController(animated: false)
self.perform(#selector(postSearchNotification), with: nil, afterDelay: 0.4)
perform(#selector(postSearchNotification), with: nil, afterDelay: 0.4)
}
}
func applicationWillResignActive(_ application: UIApplication) {
func applicationWillResignActive(_: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
// Display a blur effect view
let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = (self.window?.frame)!
blurEffectView.frame = (window?.frame)!
blurEffectView.tag = ViewTag.blur.rawValue
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.window?.addSubview(blurEffectView)
window?.addSubview(blurEffectView)
// Display the Pass icon in the middle of the screen
let iconsDictionary = Bundle.main.infoDictionary?["CFBundleIcons"] as? NSDictionary
@ -78,33 +78,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
let appIconView = UIImageView(image: appIcon)
appIconView.layer.cornerRadius = (appIcon?.size.height)! / 5
appIconView.layer.masksToBounds = true
appIconView.center = (self.window?.center)!
appIconView.center = (window?.center)!
appIconView.tag = ViewTag.appicon.rawValue
self.window?.addSubview(appIconView)
window?.addSubview(appIconView)
}
func applicationDidEnterBackground(_ application: UIApplication) {
func applicationDidEnterBackground(_: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
func applicationWillEnterForeground(_: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
passcodeLockPresenter.present(windowLevel: UIApplication.shared.windows.last?.windowLevel.rawValue)
}
func applicationDidBecomeActive(_ application: UIApplication) {
func applicationDidBecomeActive(_: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
self.window?.viewWithTag(ViewTag.appicon.rawValue)?.removeFromSuperview()
self.window?.viewWithTag(ViewTag.blur.rawValue)?.removeFromSuperview()
window?.viewWithTag(ViewTag.appicon.rawValue)?.removeFromSuperview()
window?.viewWithTag(ViewTag.blur.rawValue)?.removeFromSuperview()
}
func applicationWillTerminate(_ application: UIApplication) {
func applicationWillTerminate(_: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
saveContext()
}
// MARK: - Core Data stack
@ -123,7 +122,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
try! FileManager.default.createDirectory(atPath: Globals.documentPath, withIntermediateDirectories: true, attributes: nil)
}
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: URL(fileURLWithPath: Globals.dbPath))]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.loadPersistentStores(completionHandler: { _, error in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
@ -144,7 +143,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - Core Data Saving support
func saveContext () {
func saveContext() {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
@ -157,6 +156,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
}
}

View file

@ -6,11 +6,10 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class AboutRepositoryTableViewController: BasicStaticTableViewController {
private static let VALUE_NOT_AVAILABLE = "ValueNotAvailable".localize()
private var needRefresh = false
@ -18,6 +17,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
let indicator = UIActivityIndicatorView(style: .gray)
return indicator
}()
private let passwordStore = PasswordStore.shared
override func viewDidLoad() {
@ -41,10 +41,9 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
}
private func setTableData() {
// clear current contents (if any)
self.tableData.removeAll(keepingCapacity: true)
self.tableView.reloadData()
tableData.removeAll(keepingCapacity: true)
tableView.reloadData()
indicator.startAnimating()
// reload the table
@ -67,8 +66,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
[.style: CellDataStyle.value1, .accessoryType: type, .title: "LocalCommits".localize(), .detailText: localCommits],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "LastSynced".localize(), .detailText: lastSynced],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Commits".localize(), .detailText: commits],
[.title: "CommitLogs".localize(), .action: "segue", .link: "showCommitLogsSegue"],
],
[.title: "CommitLogs".localize(), .action: "segue", .link: "showCommitLogsSegue"]],
]
strongSelf.indicator.stopAnimating()
strongSelf.tableView.reloadData()
@ -79,15 +77,15 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
private func numberOfPasswordsString() -> String {
let formatter = NumberFormatter()
formatter.numberStyle = NumberFormatter.Style.decimal
return formatter.string(from: NSNumber(value: self.passwordStore.numberOfPasswords)) ?? ""
return formatter.string(from: NSNumber(value: passwordStore.numberOfPasswords)) ?? ""
}
private func sizeOfRepositoryString() -> String {
return ByteCountFormatter.string(fromByteCount: Int64(self.passwordStore.sizeOfRepositoryByteCount), countStyle: ByteCountFormatter.CountStyle.file)
ByteCountFormatter.string(fromByteCount: Int64(passwordStore.sizeOfRepositoryByteCount), countStyle: ByteCountFormatter.CountStyle.file)
}
private func lastSyncedTimeString() -> String {
guard let date = self.passwordStore.lastSyncedTime else {
guard let date = passwordStore.lastSyncedTime else {
return "SyncAgain?".localize()
}
let formatter = DateFormatter()
@ -103,7 +101,8 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
return AboutRepositoryTableViewController.VALUE_NOT_AVAILABLE
}
@objc func setNeedRefresh() {
@objc
func setNeedRefresh() {
needRefresh = true
}
}

View file

@ -9,17 +9,16 @@
import UIKit
class AboutTableViewController: BasicStaticTableViewController {
override func viewDidLoad() {
tableData = [
// section 0
[[.title: "Website".localize(), .action: "link", .link: "https://github.com/mssun/pass-ios.git"],
[.title: "Help".localize(), .action: "link", .link: "https://github.com/mssun/passforios/wiki"],
[.title: "ContactDeveloper".localize(), .action: "link", .link: "mailto:developer@passforios.mssun.me?subject=Pass%20for%20iOS"],],
[.title: "ContactDeveloper".localize(), .action: "link", .link: "mailto:developer@passforios.mssun.me?subject=Pass%20for%20iOS"]],
// section 1,
[[.title: "OpenSourceComponents".localize(), .action: "segue", .link: "showOpenSourceComponentsSegue"],
[.title: "SpecialThanks".localize(), .action: "segue", .link: "showSpecialThanksSegue"],],
[.title: "SpecialThanks".localize(), .action: "segue", .link: "showSpecialThanksSegue"]],
]
super.viewDidLoad()
}
@ -39,11 +38,10 @@ class AboutTableViewController: BasicStaticTableViewController {
return nil
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 1 {
return "Acknowledgements".localize().uppercased()
}
return nil
}
}

View file

@ -6,8 +6,8 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class AddPasswordTableViewController: PasswordEditorTableViewController {
var defaultDirPrefix = ""
@ -17,7 +17,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
tableData[0][0][PasswordEditorCellKey.content] = defaultDirPrefix
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
override func shouldPerformSegue(withIdentifier identifier: String, sender _: Any?) -> Bool {
if identifier == "saveAddPasswordSegue" {
// check PGP key
guard PGPAgent.shared.isPrepared else {

View file

@ -6,16 +6,15 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SVProgressHUD
import passKit
import SVProgressHUD
import UIKit
class AdvancedSettingsTableViewController: UITableViewController {
@IBOutlet weak var encryptInASCIIArmoredTableViewCell: UITableViewCell!
@IBOutlet weak var gitSignatureTableViewCell: UITableViewCell!
@IBOutlet weak var eraseDataTableViewCell: UITableViewCell!
@IBOutlet weak var discardChangesTableViewCell: UITableViewCell!
@IBOutlet var encryptInASCIIArmoredTableViewCell: UITableViewCell!
@IBOutlet var gitSignatureTableViewCell: UITableViewCell!
@IBOutlet var eraseDataTableViewCell: UITableViewCell!
@IBOutlet var discardChangesTableViewCell: UITableViewCell!
let passwordStore = PasswordStore.shared
let encryptInASCIIArmoredSwitch: UISwitch = {
@ -37,10 +36,10 @@ class AdvancedSettingsTableViewController: UITableViewController {
private func setGitSignatureText() {
let gitSignatureName = passwordStore.gitSignatureForNow?.name ?? ""
let gitSignatureEmail = passwordStore.gitSignatureForNow?.email ?? ""
self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .footnote)
self.gitSignatureTableViewCell.detailTextLabel?.text = "\(gitSignatureName) <\(gitSignatureEmail)>"
if Defaults.gitSignatureName == nil && Defaults.gitSignatureEmail == nil {
self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .body)
gitSignatureTableViewCell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .footnote)
gitSignatureTableViewCell.detailTextLabel?.text = "\(gitSignatureName) <\(gitSignatureEmail)>"
if Defaults.gitSignatureName == nil, Defaults.gitSignatureEmail == nil {
gitSignatureTableViewCell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .body)
gitSignatureTableViewCell.detailTextLabel?.text = "NotSet".localize()
}
}
@ -49,7 +48,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
tableView.deselectRow(at: indexPath, animated: true)
if tableView.cellForRow(at: indexPath) == eraseDataTableViewCell {
let alert = UIAlertController(title: "ErasePasswordStoreData?".localize(), message: "EraseExplanation.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in
alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive, handler: { [unowned self] (_) -> Void in
SVProgressHUD.show(withStatus: "Erasing...".localize())
self.passwordStore.erase()
self.navigationController!.popViewController(animated: true)
@ -57,10 +56,10 @@ class AdvancedSettingsTableViewController: UITableViewController {
SVProgressHUD.dismiss(withDelay: 1)
}))
alert.addAction(UIAlertAction.dismiss())
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
} else if tableView.cellForRow(at: indexPath) == discardChangesTableViewCell {
let alert = UIAlertController(title: "DiscardAllLocalChanges?".localize(), message: "DiscardExplanation.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "DiscardAllLocalChanges".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in
alert.addAction(UIAlertAction(title: "DiscardAllLocalChanges".localize(), style: UIAlertAction.Style.destructive, handler: { [unowned self] (_) -> Void in
SVProgressHUD.show(withStatus: "Resetting...".localize())
do {
let numberDiscarded = try self.passwordStore.reset()
@ -73,15 +72,17 @@ class AdvancedSettingsTableViewController: UITableViewController {
}))
alert.addAction(UIAlertAction.dismiss())
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
}
}
@objc func encryptInASCIIArmoredAction(_ sender: Any?) {
@objc
func encryptInASCIIArmoredAction(_: Any?) {
Defaults.encryptInArmored = encryptInASCIIArmoredSwitch.isOn
}
@IBAction func saveGitConfigSetting(segue: UIStoryboardSegue) {
@IBAction
func saveGitConfigSetting(segue: UIStoryboardSegue) {
if let controller = segue.source as? GitConfigSettingsTableViewController {
if let gitSignatureName = controller.nameTextField.text,
let gitSignatureEmail = controller.emailTextField.text {
@ -91,5 +92,4 @@ class AdvancedSettingsTableViewController: UITableViewController {
setGitSignatureText()
}
}
}

View file

@ -6,11 +6,10 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SafariServices
import MessageUI
import passKit
import SafariServices
import UIKit
enum CellDataType {
case link, segue, empty, detail
@ -25,7 +24,7 @@ enum CellDataKey {
}
class BasicStaticTableViewController: UITableViewController, MFMailComposeViewControllerDelegate {
var tableData = [[Dictionary<CellDataKey, Any>]]()
var tableData = [[[CellDataKey: Any]]]()
var navigationItemTitle: String?
override func viewDidLoad() {
@ -35,12 +34,12 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return tableData.count
override func numberOfSections(in _: UITableView) -> Int {
tableData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData[section].count
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
tableData[section].count
}
override func didReceiveMemoryWarning() {
@ -48,8 +47,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
override func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellData = tableData[indexPath.section][indexPath.row]
let cellDataStyle = cellData[CellDataKey.style] as? CellDataStyle
var cell: UITableViewCell?
@ -76,7 +74,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
return cell ?? UITableViewCell()
}
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
override func tableView(_: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let cellData = tableData[indexPath.section][indexPath.row]
let selector = cellData[CellDataKey.detailDisclosureAction] as? Selector
perform(selector, with: cellData[CellDataKey.detailDisclosureData])
@ -108,7 +106,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
}
case "http", "https":
let svc = SFSafariViewController(url: URL(string: link)!, entersReaderIfAvailable: false)
self.present(svc, animated: true, completion: nil)
present(svc, animated: true, completion: nil)
default:
break
}
@ -123,10 +121,10 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
mailVC.setToRecipients(recipients)
mailVC.setSubject(subject)
mailVC.setMessageBody("", isHTML: false)
self.present(mailVC, animated: true, completion: nil)
present(mailVC, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith _: MFMailComposeResult, error _: Error?) {
controller.dismiss(animated: true)
}
}

View file

@ -6,9 +6,9 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import ObjectiveGit
import passKit
import UIKit
class CommitLogsTableViewController: UITableViewController {
var commits: [GTCommit] = []
@ -18,12 +18,12 @@ class CommitLogsTableViewController: UITableViewController {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(updateCommitLogs), name: .passwordStoreUpdated, object: nil)
commits = getCommitLogs()
self.tableView.estimatedRowHeight = 50
self.tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 50
tableView.rowHeight = UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return commits.count
override func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
commits.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@ -42,7 +42,8 @@ class CommitLogsTableViewController: UITableViewController {
return cell
}
@objc func updateCommitLogs() {
@objc
func updateCommitLogs() {
commits = getCommitLogs()
tableView.reloadData()
}

View file

@ -6,11 +6,11 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class EditPasswordTableViewController: PasswordEditorTableViewController {
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
override func shouldPerformSegue(withIdentifier identifier: String, sender _: Any?) -> Bool {
if identifier == "saveEditPasswordSegue" {
// check name
guard checkName() else {

View file

@ -6,8 +6,8 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class GeneralSettingsTableViewController: BasicStaticTableViewController {
let passwordStore = PasswordStore.shared
@ -67,37 +67,35 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
override func viewDidLoad() {
tableData = [
// section 0
[[.title: "AboutRepository".localize(), .action: "segue", .link: "showAboutRepositorySegue"],],
[[.title: "AboutRepository".localize(), .action: "segue", .link: "showAboutRepositorySegue"]],
// section 1
[
[.title: "RememberPgpKeyPassphrase".localize(), .action: "none",],
[.title: "RememberGitCredentialPassphrase".localize(), .action: "none",],
[.title: "RememberPgpKeyPassphrase".localize(), .action: "none"],
[.title: "RememberGitCredentialPassphrase".localize(), .action: "none"],
],
// section 2
[
[.title: "ShowFolders".localize(), .action: "none",],
[.title: "HidePasswordImages".localize(), .action: "none",],
[.title: "HideUnknownFields".localize(), .action: "none",],
[.title: "HideOtpFields".localize(), .action: "none",],
[.title: "ShowFolders".localize(), .action: "none"],
[.title: "HidePasswordImages".localize(), .action: "none"],
[.title: "HideUnknownFields".localize(), .action: "none"],
[.title: "HideOtpFields".localize(), .action: "none"],
],
]
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath)
let cell = super.tableView(tableView, cellForRowAt: indexPath)
switch cell.textLabel!.text! {
case "HideUnknownFields".localize():
cell.accessoryType = .none
let detailButton = UIButton(type: .detailDisclosure)
hideUnknownSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hideUnknownSwitch.bounds.width, height: hideUnknownSwitch.bounds.height)
hideUnknownSwitch.frame = CGRect(x: detailButton.bounds.width + 10, y: 0, width: hideUnknownSwitch.bounds.width, height: hideUnknownSwitch.bounds.height)
detailButton.frame = CGRect(x: 0, y: 5, width: detailButton.bounds.width, height: detailButton.bounds.height)
detailButton.addTarget(self, action: #selector(GeneralSettingsTableViewController.tapHideUnknownSwitchDetailButton(_:)), for: UIControl.Event.touchDown)
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hideUnknownSwitch.bounds.width+10, height: hideUnknownSwitch.bounds.height))
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hideUnknownSwitch.bounds.width + 10, height: hideUnknownSwitch.bounds.height))
accessoryView.addSubview(detailButton)
accessoryView.addSubview(hideUnknownSwitch)
cell.accessoryView = accessoryView
@ -106,10 +104,10 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
case "HideOtpFields".localize():
cell.accessoryType = .none
let detailButton = UIButton(type: .detailDisclosure)
hideOTPSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hideOTPSwitch.bounds.width, height: hideOTPSwitch.bounds.height)
hideOTPSwitch.frame = CGRect(x: detailButton.bounds.width + 10, y: 0, width: hideOTPSwitch.bounds.width, height: hideOTPSwitch.bounds.height)
detailButton.frame = CGRect(x: 0, y: 5, width: detailButton.bounds.width, height: detailButton.bounds.height)
detailButton.addTarget(self, action: #selector(GeneralSettingsTableViewController.tapHideOTPSwitchDetailButton(_:)), for: UIControl.Event.touchDown)
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hideOTPSwitch.bounds.width+10, height: hideOTPSwitch.bounds.height))
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hideOTPSwitch.bounds.width + 10, height: hideOTPSwitch.bounds.height))
accessoryView.addSubview(detailButton)
accessoryView.addSubview(hideOTPSwitch)
cell.accessoryView = accessoryView
@ -130,10 +128,10 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
case "HidePasswordImages".localize():
cell.accessoryType = .none
let detailButton = UIButton(type: .detailDisclosure)
hidePasswordImagesSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hidePasswordImagesSwitch.bounds.width, height: hidePasswordImagesSwitch.bounds.height)
hidePasswordImagesSwitch.frame = CGRect(x: detailButton.bounds.width + 10, y: 0, width: hidePasswordImagesSwitch.bounds.width, height: hidePasswordImagesSwitch.bounds.height)
detailButton.frame = CGRect(x: 0, y: 5, width: detailButton.bounds.width, height: detailButton.bounds.height)
detailButton.addTarget(self, action: #selector(GeneralSettingsTableViewController.tapHidePasswordImagesSwitchDetailButton(_:)), for: UIControl.Event.touchDown)
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hidePasswordImagesSwitch.bounds.width+10, height: hidePasswordImagesSwitch.bounds.height))
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hidePasswordImagesSwitch.bounds.width + 10, height: hidePasswordImagesSwitch.bounds.height))
accessoryView.addSubview(detailButton)
accessoryView.addSubview(hidePasswordImagesSwitch)
cell.accessoryView = accessoryView
@ -144,43 +142,50 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
return cell
}
@objc func tapHideUnknownSwitchDetailButton(_ sender: Any?) {
@objc
func tapHideUnknownSwitchDetailButton(_: Any?) {
let alertMessage = "HideUnknownFieldsExplanation.".localize()
let alertTitle = "HideUnknownFields".localize()
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
}
@objc func tapHideOTPSwitchDetailButton(_ sender: Any?) {
@objc
func tapHideOTPSwitchDetailButton(_: Any?) {
let keywordsString = Constants.OTP_KEYWORDS.joined(separator: ", ")
let alertMessage = "HideOtpFieldsExplanation.".localize(keywordsString)
let alertTitle = "HideOtpFields".localize()
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
}
@objc func tapHidePasswordImagesSwitchDetailButton(_ sender: Any?) {
@objc
func tapHidePasswordImagesSwitchDetailButton(_: Any?) {
let alertMessage = "HidePasswordImagesExplanation.".localize()
let alertTitle = "HidePasswordImages".localize()
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
}
@objc func hideUnknownSwitchAction(_ sender: Any?) {
@objc
func hideUnknownSwitchAction(_: Any?) {
Defaults.isHideUnknownOn = hideUnknownSwitch.isOn
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
}
@objc func hideOTPSwitchAction(_ sender: Any?) {
@objc
func hideOTPSwitchAction(_: Any?) {
Defaults.isHideOTPOn = hideOTPSwitch.isOn
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
}
@objc func rememberPGPPassphraseSwitchAction(_ sender: Any?) {
@objc
func rememberPGPPassphraseSwitchAction(_: Any?) {
Defaults.isRememberPGPPassphraseOn = rememberPGPPassphraseSwitch.isOn
if rememberPGPPassphraseSwitch.isOn == false {
AppKeychain.shared.removeAllContent(withPrefix: Globals.pgpKeyPassphrase)
}
}
@objc func rememberGitCredentialPassphraseSwitchAction(_ sender: Any?) {
@objc
func rememberGitCredentialPassphraseSwitchAction(_: Any?) {
Defaults.isRememberGitCredentialPassphraseOn = rememberGitCredentialPassphraseSwitch.isOn
if rememberGitCredentialPassphraseSwitch.isOn == false {
passwordStore.gitSSHPrivateKeyPassphrase = nil
@ -188,14 +193,15 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
}
}
@objc func showFolderSwitchAction(_ sender: Any?) {
@objc
func showFolderSwitchAction(_: Any?) {
Defaults.isShowFolderOn = showFolderSwitch.isOn
NotificationCenter.default.post(name: .passwordDisplaySettingChanged, object: nil)
}
@objc func hidePasswordImagesSwitchAction(_ sender: Any?) {
@objc
func hidePasswordImagesSwitchAction(_: Any?) {
Defaults.isHidePasswordImagesOn = hidePasswordImagesSwitch.isOn
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
}
}

View file

@ -6,14 +6,14 @@
// Copyright © 2017 Yishi Lin. All rights reserved.
//
import UIKit
import passKit
import UIKit
class GitConfigSettingsTableViewController: UITableViewController {
let passwordStore = PasswordStore.shared
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet var nameTextField: UITextField!
@IBOutlet var emailTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
@ -26,7 +26,7 @@ class GitConfigSettingsTableViewController: UITableViewController {
emailTextField.text = Defaults.gitSignatureEmail
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
override func shouldPerformSegue(withIdentifier identifier: String, sender _: Any?) -> Bool {
if identifier == "saveGitConfigSettingSegue" {
let name = nameTextField.text!.isEmpty ? Globals.gitSignatureDefaultName : nameTextField.text!
let email = emailTextField.text!.isEmpty ? Globals.gitSignatureDefaultEmail : nameTextField.text!
@ -38,4 +38,3 @@ class GitConfigSettingsTableViewController: UITableViewController {
return true
}
}

View file

@ -6,21 +6,20 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SVProgressHUD
import passKit
import SVProgressHUD
import UIKit
class GitRepositorySettingsTableViewController: UITableViewController {
// MARK: - View Outlet
@IBOutlet weak var gitURLTextField: UITextField!
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var branchNameTextField: UITextField!
@IBOutlet weak var authSSHKeyCell: UITableViewCell!
@IBOutlet weak var authPasswordCell: UITableViewCell!
@IBOutlet weak var gitURLCell: UITableViewCell!
@IBOutlet weak var gitRepositoryURLTabelViewCell: UITableViewCell!
@IBOutlet var gitURLTextField: UITextField!
@IBOutlet var usernameTextField: UITextField!
@IBOutlet var branchNameTextField: UITextField!
@IBOutlet var authSSHKeyCell: UITableViewCell!
@IBOutlet var authPasswordCell: UITableViewCell!
@IBOutlet var gitURLCell: UITableViewCell!
@IBOutlet var gitRepositoryURLTabelViewCell: UITableViewCell!
// MARK: - Properties
@ -33,27 +32,29 @@ class GitRepositorySettingsTableViewController: UITableViewController {
updateAuthenticationMethodCheckView(for: newValue)
}
}
private var gitUrl: URL {
get { Defaults.gitURL }
set { Defaults.gitURL = newValue }
}
private var gitBranchName: String {
get { Defaults.gitBranchName }
set { Defaults.gitBranchName = newValue }
}
private var gitUsername: String {
get { Defaults.gitUsername }
set { Defaults.gitUsername = newValue }
}
private var gitCredential: GitCredential {
get {
switch Defaults.gitAuthenticationMethod {
case .password:
return GitCredential(credential: .http(userName: Defaults.gitUsername))
case .key:
let privateKey: String = AppKeychain.shared.get(for: SshKey.PRIVATE.getKeychainKey()) ?? ""
return GitCredential(credential: .ssh(userName: Defaults.gitUsername, privateKey: privateKey))
}
switch Defaults.gitAuthenticationMethod {
case .password:
return GitCredential(credential: .http(userName: Defaults.gitUsername))
case .key:
let privateKey: String = AppKeychain.shared.get(for: SshKey.PRIVATE.getKeychainKey()) ?? ""
return GitCredential(credential: .ssh(userName: Defaults.gitUsername, privateKey: privateKey))
}
}
@ -61,9 +62,9 @@ class GitRepositorySettingsTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
gitURLTextField.text = self.gitUrl.absoluteString
usernameTextField.text = self.gitUsername
branchNameTextField.text = self.gitBranchName
gitURLTextField.text = gitUrl.absoluteString
usernameTextField.text = gitUsername
branchNameTextField.text = gitBranchName
sshLabel = authSSHKeyCell.subviews[0].subviews[0] as? UILabel
authSSHKeyCell.accessoryType = .detailButton
}
@ -94,7 +95,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if cell == authPasswordCell {
self.gitAuthenticationMethod = .password
gitAuthenticationMethod = .password
} else if cell == authSSHKeyCell {
if !AppKeychain.shared.contains(key: SshKey.PRIVATE.getKeychainKey()) {
Utils.alert(title: "CannotSelectSshKey".localize(), message: "PleaseSetupSshKeyFirst.".localize(), controller: self)
@ -108,7 +109,8 @@ class GitRepositorySettingsTableViewController: UITableViewController {
// MARK: - Segue Handlers
@IBAction func save(_ sender: Any) {
@IBAction
func save(_: Any) {
guard let gitURLTextFieldText = gitURLTextField.text, let gitURL = URL(string: gitURLTextFieldText.trimmed) else {
Utils.alert(title: "CannotSave".localize(), message: "SetGitRepositoryUrl".localize(), controller: self)
return
@ -137,9 +139,9 @@ class GitRepositorySettingsTableViewController: UITableViewController {
}
}
self.gitUrl = gitURL
self.gitBranchName = branchName.trimmed
self.gitUsername = (gitURL.user ?? usernameTextField.text ?? "git").trimmed
gitUrl = gitURL
gitBranchName = branchName.trimmed
gitUsername = (gitURL.user ?? usernameTextField.text ?? "git").trimmed
if passwordStore.repositoryExists() {
let overwriteAlert: UIAlertController = {
@ -150,7 +152,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
alert.addAction(UIAlertAction.cancel())
return alert
}()
self.present(overwriteAlert, animated: true)
present(overwriteAlert, animated: true)
} else {
cloneAndSegueIfSuccess()
}
@ -159,15 +161,15 @@ class GitRepositorySettingsTableViewController: UITableViewController {
private func cloneAndSegueIfSuccess() {
// Remember git credential password/passphrase temporarily, ask whether users want this after a successful clone.
Defaults.isRememberGitCredentialPassphraseOn = true
DispatchQueue.global(qos: .userInitiated).async() {
DispatchQueue.global(qos: .userInitiated).async {
do {
let transferProgressBlock: (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void = { (git_transfer_progress, _) in
let transferProgressBlock: (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void = { git_transfer_progress, _ in
let gitTransferProgress = git_transfer_progress.pointee
let progress = Float(gitTransferProgress.received_objects) / Float(gitTransferProgress.total_objects)
SVProgressHUD.showProgress(progress, status: "Cloning Remote Repository")
}
let checkoutProgressBlock: (String, UInt, UInt) -> Void = { (_, completedSteps, totalSteps) in
let checkoutProgressBlock: (String, UInt, UInt) -> Void = { _, completedSteps, totalSteps in
let progress = Float(completedSteps) / Float(totalSteps)
SVProgressHUD.showProgress(progress, status: "CheckingOutBranch".localize(self.gitBranchName))
}
@ -179,7 +181,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
transferProgressBlock: transferProgressBlock,
checkoutProgressBlock: checkoutProgressBlock)
SVProgressHUD.dismiss() {
SVProgressHUD.dismiss {
let savePassphraseAlert: UIAlertController = {
let alert = UIAlertController(title: "Done".localize(), message: "WantToSaveGitCredential?".localize(), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No".localize(), style: .default) { _ in
@ -188,7 +190,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
self.passwordStore.gitSSHPrivateKeyPassphrase = nil
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
})
alert.addAction(UIAlertAction(title: "Yes".localize(), style: .destructive) {_ in
alert.addAction(UIAlertAction(title: "Yes".localize(), style: .destructive) { _ in
Defaults.isRememberGitCredentialPassphraseOn = true
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
})
@ -199,7 +201,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
}
}
} catch {
SVProgressHUD.dismiss() {
SVProgressHUD.dismiss {
let error = error as NSError
var message = error.localizedDescription
if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError {
@ -213,7 +215,8 @@ class GitRepositorySettingsTableViewController: UITableViewController {
}
}
@IBAction func importSSHKey(segue: UIStoryboardSegue) {
@IBAction
func importSSHKey(segue: UIStoryboardSegue) {
guard let sourceController = segue.source as? KeyImporter, sourceController.isReadyToUse() else {
return
}
@ -278,7 +281,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
}
private func requestCredentialPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
return requestGitCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self)
requestGitCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self)
}
private func updateAuthenticationMethodCheckView(for method: GitAuthenticationMethod) {
@ -297,21 +300,20 @@ class GitRepositorySettingsTableViewController: UITableViewController {
private func showGitURLFormatHelp() {
let message = """
https://example.com[:port]/project.git
ssh://[user@]server[:port]/project.git
[user@]server:project.git (no scheme)
"""
https://example.com[:port]/project.git
ssh://[user@]server[:port]/project.git
[user@]server:project.git (no scheme)
"""
Utils.alert(title: "Git URL Format", message: message, controller: self)
}
}
extension GitRepositorySettingsTableViewController: KeyImporter {
static let keySource = KeySource.itunes
static let label = "ITunesFileSharing".localize()
func isReadyToUse() -> Bool {
return KeyFileManager.PrivateSsh.doesKeyFileExist()
KeyFileManager.PrivateSsh.doesKeyFileExist()
}
func importKeys() throws {

View file

@ -9,7 +9,6 @@
import passKit
protocol KeyImporter {
static var keySource: KeySource { get }
static var label: String { get }
@ -24,9 +23,8 @@ protocol KeyImporter {
}
extension KeyImporter {
static var isCurrentKeySource: Bool {
return Defaults.gitSSHKeySource == Self.keySource
Defaults.gitSSHKeySource == Self.keySource
}
static var menuLabel: String {

View file

@ -6,11 +6,10 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SafariServices
import UIKit
class OpenSourceComponentsTableViewController: BasicStaticTableViewController {
private static let openSourceComponents = [
["FavIcon",
"https://github.com/bitserf/FavIcon",
@ -48,12 +47,13 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController {
.link: item[1],
.accessoryType: UITableViewCell.AccessoryType.detailDisclosureButton,
.detailDisclosureAction: #selector(actOnDetailDisclosureButton(_:)),
.detailDisclosureData: item[2]
.detailDisclosureData: item[2],
])
}
}
@objc func actOnDetailDisclosureButton(_ sender: Any?) {
@objc
func actOnDetailDisclosureButton(_ sender: Any?) {
if let link = sender as? String, let url = URL(string: link) {
let svc = SFSafariViewController(url: url, entersReaderIfAvailable: false)
present(svc, animated: true)

View file

@ -6,15 +6,14 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
@IBOutlet weak var armorPublicKeyTextView: UITextView!
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
@IBOutlet weak var scanPublicKeyCell: UITableViewCell!
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
@IBOutlet var armorPublicKeyTextView: UITextView!
@IBOutlet var armorPrivateKeyTextView: UITextView!
@IBOutlet var scanPublicKeyCell: UITableViewCell!
@IBOutlet var scanPrivateKeyCell: UITableViewCell!
var armorPublicKey: String?
var armorPrivateKey: String?
@ -23,45 +22,47 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
enum KeyType {
case publicKey, privateKey
}
var keyType = KeyType.publicKey
var segments = [String]()
var message = ""
func reset(keytype: KeyType) {
self.keyType = keytype
self.segments.removeAll()
keyType = keytype
segments.removeAll()
message = "LookingForStartingFrame.".localize()
}
func addSegment(segment: String) -> (accept: Bool, message: String) {
let keyTypeStr = self.keyType == .publicKey ? "Public" : "Private"
let theOtherKeyTypeStr = self.keyType == .publicKey ? "Private" : "Public"
let keyTypeStr = keyType == .publicKey ? "Public" : "Private"
let theOtherKeyTypeStr = keyType == .publicKey ? "Private" : "Public"
// Skip duplicated segments.
guard segment != self.segments.last else {
return (accept: false, message: self.message)
guard segment != segments.last else {
return (accept: false, message: message)
}
// Check whether we have found the first block.
guard !self.segments.isEmpty || segment.contains("-----BEGIN PGP \(keyTypeStr.uppercased()) KEY BLOCK-----") else {
guard !segments.isEmpty || segment.contains("-----BEGIN PGP \(keyTypeStr.uppercased()) KEY BLOCK-----") else {
// Check whether we are scanning the wrong key type.
if segment.contains("-----BEGIN PGP \(theOtherKeyTypeStr.uppercased()) KEY BLOCK-----") {
self.message = "Scan\(keyTypeStr)Key.".localize()
message = "Scan\(keyTypeStr)Key.".localize()
}
return (accept: false, message: self.message)
return (accept: false, message: message)
}
// Update the list of scanned segment and return.
self.segments.append(segment)
segments.append(segment)
if segment.contains("-----END PGP \(keyTypeStr.uppercased()) KEY BLOCK-----") {
self.message = "Done".localize()
return (accept: true, message: self.message)
message = "Done".localize()
return (accept: true, message: message)
} else {
self.message = "ScannedQrCodes(%d)".localize(self.segments.count)
return (accept: false, message: self.message)
message = "ScannedQrCodes(%d)".localize(segments.count)
return (accept: false, message: message)
}
}
}
var scanned = ScannedPGPKey()
override func viewDidLoad() {
@ -76,13 +77,14 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
scanPrivateKeyCell?.accessoryType = .disclosureIndicator
}
@IBAction func save(_ sender: Any) {
@IBAction
func save(_: Any) {
armorPublicKey = armorPublicKeyTextView.text
armorPrivateKey = armorPrivateKeyTextView.text
self.saveImportedKeys()
saveImportedKeys()
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
func textView(_: UITextView, shouldChangeTextIn _: NSRange, replacementText text: String) -> Bool {
if text == UIPasteboard.general.string {
// user pastes something, do the copy here again and clear the pasteboard in 45s
SecurePasteboard.shared.copy(textToCopy: text)
@ -94,21 +96,23 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
let selectedCell = tableView.cellForRow(at: indexPath)
if selectedCell == scanPublicKeyCell {
scanned.reset(keytype: ScannedPGPKey.KeyType.publicKey)
self.performSegue(withIdentifier: "showPGPScannerSegue", sender: self)
performSegue(withIdentifier: "showPGPScannerSegue", sender: self)
} else if selectedCell == scanPrivateKeyCell {
scanned.reset(keytype: ScannedPGPKey.KeyType.privateKey)
self.performSegue(withIdentifier: "showPGPScannerSegue", sender: self)
performSegue(withIdentifier: "showPGPScannerSegue", sender: self)
}
tableView.deselectRow(at: indexPath, animated: true)
}
// MARK: - QRScannerControllerDelegate Methods
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
return scanned.addSegment(segment: line)
}
// MARK: - QRScannerControllerDelegate Methods
func handleScannedOutput(line: String) {
func handleScannedOutput(line _: String) {
let key = scanned.segments.joined(separator: "")
switch scanned.keyType {
case .publicKey:
@ -118,7 +122,7 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
override func prepare(for segue: UIStoryboardSegue, sender _: Any?) {
if segue.identifier == "showPGPScannerSegue" {
if let navController = segue.destination as? UINavigationController {
if let viewController = navController.topViewController as? QRScannerController {
@ -132,7 +136,6 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
}
extension PGPKeyArmorImportTableViewController: PGPKeyImporter {
static let keySource = KeySource.armor
static let label = "AsciiArmorEncryptedKey".localize()

View file

@ -9,18 +9,18 @@
import passKit
class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController {
@IBOutlet var pgpPublicKeyFile: UITableViewCell!
@IBOutlet var pgpPrivateKeyFile: UITableViewCell!
@IBOutlet weak var pgpPublicKeyFile: UITableViewCell!
@IBOutlet weak var pgpPrivateKeyFile: UITableViewCell!
private var publicKey: String? = nil
private var privateKey: String? = nil
private var publicKey: String?
private var privateKey: String?
private enum KeyType { case none, `private`, `public` }
private var currentlyPicking = KeyType.none
@IBAction func save(_ sender: Any) {
self.saveImportedKeys()
@IBAction
func save(_: Any) {
saveImportedKeys()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -43,7 +43,6 @@ class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController {
}
extension PGPKeyFileImportTableViewController: UIDocumentPickerDelegate {
func documentPicker(_: UIDocumentPickerViewController, didPickDocumentsAt url: [URL]) {
guard let url = url.first else {
return
@ -78,12 +77,11 @@ extension PGPKeyFileImportTableViewController: UIDocumentPickerDelegate {
}
extension PGPKeyFileImportTableViewController: PGPKeyImporter {
static let keySource = KeySource.file
static let label = "LoadFromFiles".localize()
func isReadyToUse() -> Bool {
return validate(key: publicKey) && validate(key: privateKey)
validate(key: publicKey) && validate(key: privateKey)
}
func importKeys() throws {

View file

@ -9,19 +9,15 @@
import passKit
protocol PGPKeyImporter: KeyImporter {
func doAfterImport()
func saveImportedKeys()
}
extension PGPKeyImporter {
static var isCurrentKeySource: Bool {
return Defaults.pgpKeySource == Self.keySource
Defaults.pgpKeySource == Self.keySource
}
func doAfterImport() {
}
func doAfterImport() {}
}

View file

@ -6,13 +6,12 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class PGPKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
@IBOutlet weak var pgpPublicKeyURLTextField: UITextField!
@IBOutlet weak var pgpPrivateKeyURLTextField: UITextField!
@IBOutlet var pgpPublicKeyURLTextField: UITextField!
@IBOutlet var pgpPrivateKeyURLTextField: UITextField!
var pgpPrivateKeyURL: URL?
var pgpPublicKeyURL: URL?
@ -23,30 +22,30 @@ class PGPKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
pgpPrivateKeyURLTextField.text = Defaults.pgpPrivateKeyURL?.absoluteString
}
@IBAction func save(_ sender: Any) {
@IBAction
func save(_: Any) {
guard let publicKeyURLText = pgpPublicKeyURLTextField.text,
let publicKeyURL = URL(string: publicKeyURLText),
let privateKeyURLText = pgpPrivateKeyURLTextField.text,
let privateKeyURL = URL(string: privateKeyURLText) else {
Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self)
return
Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self)
return
}
if privateKeyURL.scheme?.lowercased() == "http" || publicKeyURL.scheme?.lowercased() == "http" {
Utils.alert(title: "HttpNotSecure".localize(), message: "ReallyUseHttp.".localize(), controller: self)
}
pgpPrivateKeyURL = privateKeyURL
pgpPublicKeyURL = publicKeyURL
self.saveImportedKeys()
saveImportedKeys()
}
}
extension PGPKeyUrlImportTableViewController: PGPKeyImporter {
static let keySource = KeySource.url
static let label = "DownloadFromUrl".localize()
func isReadyToUse() -> Bool {
return validate(pgpKeyUrl: pgpPublicKeyURLTextField.text ?? "")
validate(pgpKeyUrl: pgpPublicKeyURLTextField.text ?? "")
&& validate(pgpKeyUrl: pgpPrivateKeyURLTextField.text ?? "")
}

View file

@ -6,16 +6,16 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import FavIcon
import SVProgressHUD
import passKit
import SVProgressHUD
import UIKit
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
var passwordEntity: PasswordEntity?
private var password: Password?
private var passwordImage: UIImage?
private var oneTimePasswordIndexPath : IndexPath?
private var oneTimePasswordIndexPath: IndexPath?
private var shouldPopCurrentView = false
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
@ -52,7 +52,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
tableView.addGestureRecognizer(tapGesture)
tapGesture.delegate = self
tableView.contentInset = UIEdgeInsets.init(top: -36, left: 0, bottom: 44, right: 0);
tableView.contentInset = UIEdgeInsets(top: -36, left: 0, bottom: 44, right: 0)
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 52
@ -66,8 +66,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
let image = UIImage(data: imageData as Data)
passwordImage = image
}
self.decryptThenShowPassword()
self.setupOneTimePasswordAutoRefresh()
decryptThenShowPassword()
setupOneTimePasswordAutoRefresh()
// pop the current view because this password might be "discarded"
NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: .passwordStoreChangeDiscarded, object: nil)
@ -78,16 +78,17 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.shouldPopCurrentView {
if shouldPopCurrentView {
let alert = UIAlertController(title: "Notice".localize(), message: "PreviousChangesDiscarded.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction.okAndPopView(controller: self))
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
}
}
@objc private func decryptThenShowPassword(keyID: String? = nil) {
@objc
private func decryptThenShowPassword(keyID: String? = nil) {
guard let passwordEntity = passwordEntity else {
Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, handler: {(UIAlertAction) -> Void in
Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, handler: { (_) -> Void in
self.navigationController!.popViewController(animated: true)
})
return
@ -98,7 +99,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: self)
self.password = try self.passwordStore.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
self.showPassword()
} catch AppError.PgpPrivateKeyNotFound(let key) {
} catch let AppError.PgpPrivateKeyNotFound(key) {
DispatchQueue.main.async {
// alert: cancel or try again
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
@ -115,7 +116,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// alert: cancel or try again
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
alert.addAction(UIAlertAction(title: "TryAgain".localize(), style: .default) {_ in
alert.addAction(UIAlertAction(title: "TryAgain".localize(), style: .default) { _ in
self.decryptThenShowPassword()
})
self.present(alert, animated: true, completion: nil)
@ -141,14 +142,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
private func setupOneTimePasswordAutoRefresh() {
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
[weak self] timer in
[weak self] _ in
// bail out of the timer code if the object has been freed
guard let strongSelf = self,
let otpType = strongSelf.password?.otpType,
otpType != .none,
let indexPath = strongSelf.oneTimePasswordIndexPath,
let cell = strongSelf.tableView.cellForRow(at: indexPath) as? LabelTableViewCell else {
return
return
}
switch otpType {
case .totp:
@ -163,64 +164,67 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
@objc private func pressEdit(_ sender: Any?) {
@objc
private func pressEdit(_: Any?) {
performSegue(withIdentifier: "editPasswordSegue", sender: self)
}
@objc private func setShouldPopCurrentView() {
self.shouldPopCurrentView = true
@objc
private func setShouldPopCurrentView() {
shouldPopCurrentView = true
}
@IBAction private func cancelEditPassword(segue: UIStoryboardSegue) {
@IBAction
private func cancelEditPassword(segue _: UIStoryboardSegue) {}
}
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
if self.password!.changed != 0 {
self.saveEditPassword(password: self.password!)
@IBAction
private func saveEditPassword(segue _: UIStoryboardSegue) {
if password!.changed != 0 {
saveEditPassword(password: password!)
}
}
private func saveEditPassword(password: Password, keyID: String? = nil) {
SVProgressHUD.show(withStatus: "Saving".localize())
do {
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: password, keyID: keyID)
self.setTableData()
self.tableView.reloadData()
passwordEntity = try passwordStore.edit(passwordEntity: passwordEntity!, password: password, keyID: keyID)
setTableData()
tableView.reloadData()
SVProgressHUD.showSuccess(withStatus: "Success".localize())
SVProgressHUD.dismiss(withDelay: 1)
} catch AppError.PgpPublicKeyNotFound(let key) {
DispatchQueue.main.async {
// alert: cancel or select keys
SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Cannot Edit Password", message: AppError.PgpPublicKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
let selectKey = UIAlertAction.selectKey(controller: self) { action in
self.saveEditPassword(password: password, keyID: action.title)
}
alert.addAction(selectKey)
} catch let AppError.PgpPublicKeyNotFound(key) {
DispatchQueue.main.async {
// alert: cancel or select keys
SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Cannot Edit Password", message: AppError.PgpPublicKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
let selectKey = UIAlertAction.selectKey(controller: self) { action in
self.saveEditPassword(password: password, keyID: action.title)
}
alert.addAction(selectKey)
self.present(alert, animated: true, completion: nil)
}
return
} catch {
DispatchQueue.main.async {
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
}
}
self.present(alert, animated: true, completion: nil)
}
return
} catch {
DispatchQueue.main.async {
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
}
}
}
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
@IBAction
private func deletePassword(segue _: UIStoryboardSegue) {
do {
try passwordStore.delete(passwordEntity: passwordEntity!)
} catch {
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
}
let _ = navigationController?.popViewController(animated: true)
_ = navigationController?.popViewController(animated: true)
}
private func setTableData() {
self.tableData = Array<TableSection>()
tableData = [TableSection]()
// name section
var section = TableSection(type: .name)
@ -239,7 +243,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
section.item.append(Constants.PASSWORD_KEYWORD => password.password)
tableData.append(section)
// addition section
// show one time password
@ -254,7 +257,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// show additional information
let filteredAdditionKeys = password.getFilteredAdditions()
if filteredAdditionKeys.count > 0 {
if !filteredAdditionKeys.isEmpty {
section = TableSection(type: .addition, header: "Additions".localize())
section.item.append(contentsOf: filteredAdditionKeys)
tableData.append(section)
@ -264,10 +267,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
section = TableSection(type: .misc)
section.item.append(AdditionField(title: "ShowRaw".localize()))
tableData.append(section)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
override func prepare(for segue: UIStoryboardSegue, sender _: Any?) {
if segue.identifier == "editPasswordSegue" {
if let controller = segue.destination as? UINavigationController {
if let editController = controller.viewControllers.first as? EditPasswordTableViewController {
@ -288,9 +290,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
if urlString.lowercased().hasPrefix("http://") {
// try to replace http url to https url
newUrlString = urlString.replacingOccurrences(of: "http://",
with: "https://",
options: .caseInsensitive,
range: urlString.range(of: "http://"))
with: "https://",
options: .caseInsensitive,
range: urlString.range(of: "http://"))
} else if urlString.lowercased().hasPrefix("https://") {
// do nothing here
} else {
@ -311,11 +313,12 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
@objc private func tapMenu(recognizer: UITapGestureRecognizer) {
@objc
private func tapMenu(recognizer: UITapGestureRecognizer) {
if recognizer.state == UIGestureRecognizer.State.ended {
let tapLocation = recognizer.location(in: self.tableView)
if let tapIndexPath = self.tableView.indexPathForRow(at: tapLocation) {
if let tappedCell = self.tableView.cellForRow(at: tapIndexPath) as? LabelTableViewCell {
let tapLocation = recognizer.location(in: tableView)
if let tapIndexPath = tableView.indexPathForRow(at: tapLocation) {
if let tappedCell = tableView.cellForRow(at: tapIndexPath) as? LabelTableViewCell {
tappedCell.becomeFirstResponder()
let menuController = UIMenuController.shared
let revealItem = UIMenuItem(title: "Reveal".localize(), action: #selector(LabelTableViewCell.revealPassword(_:)))
@ -330,22 +333,22 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
func gestureRecognizer(_: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view!.isKind(of: UIButton.classForCoder()) {
return false
}
return true
}
@IBAction func back(segue:UIStoryboardSegue) {
}
@IBAction
func back(segue _: UIStoryboardSegue) {}
func getNextHOTP() {
guard password != nil, passwordEntity != nil, password?.otpType == .hotp else {
DispatchQueue.main.async {
Utils.alert(title: "Error".localize(), message: "GetNextPasswordOfNonHotp.".localize(), controller: self, completion: nil)
}
return;
return
}
// copy HOTP to pasteboard (will update counter)
@ -356,7 +359,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// commit the change of HOTP counter
if password!.changed != 0 {
do {
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!)
passwordEntity = try passwordStore.edit(passwordEntity: passwordEntity!, password: password!)
SVProgressHUD.showSuccess(withStatus: "PasswordCopied".localize() | "CounterUpdated".localize())
SVProgressHUD.dismiss(withDelay: 1)
} catch {
@ -384,19 +387,19 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
return from
}
override func numberOfSections(in tableView: UITableView) -> Int {
return tableData.count
override func numberOfSections(in _: UITableView) -> Int {
tableData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData[section].item.count
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
tableData[section].item.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sectionIndex = indexPath.section
let rowIndex = indexPath.row
let tableDataItem = tableData[sectionIndex].item[rowIndex]
switch(tableData[sectionIndex].type) {
switch tableData[sectionIndex].type {
case .name:
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordDetailTitleTableViewCell", for: indexPath) as! PasswordDetailTitleTableViewCell
if !Defaults.isHidePasswordImagesOn {
@ -453,8 +456,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
detailTextLabel.text = "HiddenFields(%d)".localize(numberOfHiddenFields)
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return tableData[section].header
override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
tableData[section].header
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
@ -464,7 +467,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
footerLabel.numberOfLines = 0
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
footerLabel.textColor = UIColor.gray
let dateString = self.passwordStore.getLatestUpdateInfo(filename: password!.url.path)
let dateString = passwordStore.getLatestUpdateInfo(filename: password!.url.path)
footerLabel.text = "LastUpdated".localize(dateString)
view.addSubview(footerLabel)
return view
@ -472,15 +475,15 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
return nil
}
override func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
override func tableView(_: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender _: Any?) {
if action == #selector(copy(_:)) {
SecurePasteboard.shared.copy(textToCopy: tableData[indexPath.section].item[indexPath.row].content)
}
}
override func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
override func tableView(_: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender _: Any?) -> Bool {
let section = tableData[indexPath.section]
switch(section.type) {
switch section.type {
case .main, .addition:
return action == #selector(UIResponderStandardEditActions.copy(_:))
default:
@ -488,8 +491,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
return true
override func tableView(_: UITableView, shouldShowMenuForRowAt _: IndexPath) -> Bool {
true
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

View file

@ -6,10 +6,10 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SafariServices
import OneTimePassword
import passKit
import SafariServices
import UIKit
enum PasswordEditorCellType: Equatable {
case nameCell
@ -30,18 +30,16 @@ enum PasswordEditorCellKey {
}
protocol PasswordSettingSliderTableViewCellDelegate {
func generateAndCopyPassword()
}
class PasswordEditorTableViewController: UITableViewController {
var tableData = [[Dictionary<PasswordEditorCellKey, Any>]]()
var tableData = [[[PasswordEditorCellKey: Any]]]()
var password: Password?
private var navigationItemTitle: String?
private var sectionHeaderTitles = ["Name".localize(), "Password".localize(), "Additions".localize(),""].map {$0.uppercased()}
private var sectionHeaderTitles = ["Name".localize(), "Password".localize(), "Additions".localize(), ""].map { $0.uppercased() }
private var sectionFooterTitles = ["", "", "UseKeyValueFormat.".localize(), ""]
private let nameSection = 0
private let passwordSection = 1
@ -86,7 +84,7 @@ class PasswordEditorTableViewController: UITableViewController {
passwordFlavorCell?.textLabel?.text = "PasswordGeneratorFlavor".localize()
passwordFlavorCell?.selectionStyle = .none
let passwordFlavorSelector = UISegmentedControl(items: PasswordGeneratorFlavor.allCases.map { $0.localized })
let passwordFlavorSelector = UISegmentedControl(items: PasswordGeneratorFlavor.allCases.map(\.localized))
passwordFlavorSelector.selectedSegmentIndex = PasswordGeneratorFlavor.allCases.firstIndex(of: passwordGenerator.flavor)!
passwordFlavorSelector.addTarget(self, action: #selector(flavorChanged), for: .valueChanged)
passwordFlavorCell?.accessoryView = passwordFlavorSelector
@ -126,11 +124,11 @@ class PasswordEditorTableViewController: UITableViewController {
],
[
[.type: PasswordEditorCellType.scanQRCodeCell],
]
],
]
if self.password != nil {
tableData[additionsSection+1].append([.type: PasswordEditorCellType.deletePasswordCell])
if password != nil {
tableData[additionsSection + 1].append([.type: PasswordEditorCellType.deletePasswordCell])
}
updateTableData(withRespectTo: passwordGenerator.flavor)
}
@ -206,11 +204,11 @@ class PasswordEditorTableViewController: UITableViewController {
}
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
override func tableView(_: UITableView, heightForHeaderInSection _: Int) -> CGFloat {
44
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
override func tableView(_: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch tableData[indexPath.section][indexPath.row][PasswordEditorCellKey.type] as! PasswordEditorCellType {
case .passwordLengthCell, .passwordGroupsCell:
return 42
@ -221,11 +219,11 @@ class PasswordEditorTableViewController: UITableViewController {
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return tableData.count
override func numberOfSections(in _: UITableView) -> Int {
tableData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == passwordSection, hidePasswordSettings {
// hide the password section, only the password should be shown
return 1
@ -234,12 +232,12 @@ class PasswordEditorTableViewController: UITableViewController {
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionHeaderTitles[section]
override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
sectionHeaderTitles[section]
}
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sectionFooterTitles[section]
override func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? {
sectionFooterTitles[section]
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -248,13 +246,13 @@ class PasswordEditorTableViewController: UITableViewController {
if selectedCell == deletePasswordCell {
let alert = UIAlertController(title: "DeletePassword?".localize(), message: nil, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Delete".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in
alert.addAction(UIAlertAction(title: "Delete".localize(), style: UIAlertAction.Style.destructive, handler: { [unowned self] (_) -> Void in
self.performSegue(withIdentifier: "deletePasswordSegue", sender: self)
}))
alert.addAction(UIAlertAction.cancel())
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
} else if selectedCell == scanQRCodeCell {
self.performSegue(withIdentifier: "showQRScannerSegue", sender: self)
performSegue(withIdentifier: "showQRScannerSegue", sender: self)
}
}
@ -276,11 +274,12 @@ class PasswordEditorTableViewController: UITableViewController {
}
}
private func isPasswordDelimiterCellData(data: Dictionary<PasswordEditorCellKey, Any>) -> Bool {
return (data[.type] as? PasswordEditorCellType) == .some(.passwordGroupsCell)
private func isPasswordDelimiterCellData(data: [PasswordEditorCellKey: Any]) -> Bool {
(data[.type] as? PasswordEditorCellType) == .some(.passwordGroupsCell)
}
@objc func flavorChanged(_ sender: UISegmentedControl) {
@objc
func flavorChanged(_ sender: UISegmentedControl) {
let flavor = PasswordGeneratorFlavor.allCases[sender.selectedSegmentIndex]
guard passwordGenerator.flavor != flavor else {
return
@ -327,7 +326,7 @@ class PasswordEditorTableViewController: UITableViewController {
additionsCell?.setContent(content: additionsString)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
override func prepare(for segue: UIStoryboardSegue, sender _: Any?) {
if segue.identifier == "showQRScannerSegue" {
if let navController = segue.destination as? UINavigationController {
if let viewController = navController.topViewController as? QRScannerController {
@ -362,8 +361,8 @@ class PasswordEditorTableViewController: UITableViewController {
// the name field should be a valid url
guard let path = name.stringByAddingPercentEncodingForRFC3986(),
var passwordURL = URL(string: path) else {
Utils.alert(title: "CannotSave".localize(), message: "PasswordNameInvalid.".localize(), controller: self, completion: nil)
return false
Utils.alert(title: "CannotSave".localize(), message: "PasswordNameInvalid.".localize(), controller: self, completion: nil)
return false
}
// check whether we can parse the filename (be consistent with PasswordStore::addPasswordEntities)
@ -382,20 +381,20 @@ class PasswordEditorTableViewController: UITableViewController {
}
// MARK: - FillPasswordTableViewCellDelegate
extension PasswordEditorTableViewController: FillPasswordTableViewCellDelegate {
extension PasswordEditorTableViewController: FillPasswordTableViewCellDelegate {
// generate password, copy to pasteboard, and set the cell
// check whether the current password looks like an OTP field
func generateAndCopyPassword() {
if let currentPassword = fillPasswordCell?.getContent(), Constants.isOtpRelated(line: currentPassword) {
let alert = UIAlertController(title: "Overwrite?".localize(), message: "OverwriteOtpConfiguration?".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive, handler: {_ in
alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive, handler: { _ in
self.generateAndCopyPasswordNoOtpCheck()
}))
alert.addAction(UIAlertAction.cancel())
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
} else {
self.generateAndCopyPasswordNoOtpCheck()
generateAndCopyPasswordNoOtpCheck()
}
}
@ -407,11 +406,12 @@ extension PasswordEditorTableViewController: FillPasswordTableViewCellDelegate {
}
// MARK: - PasswordSettingSliderTableViewCellDelegate
extension PasswordEditorTableViewController: PasswordSettingSliderTableViewCellDelegate {}
// MARK: - QRScannerControllerDelegate
extension PasswordEditorTableViewController: QRScannerControllerDelegate {
extension PasswordEditorTableViewController: QRScannerControllerDelegate {
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
if let url = URL(string: line), let _ = Token(url: url) {
return (accept: true, message: "ValidTokenUrl".localize())
@ -426,31 +426,31 @@ extension PasswordEditorTableViewController: QRScannerControllerDelegate {
}
// MARK: - SFSafariViewControllerDelegate
extension PasswordEditorTableViewController: SFSafariViewControllerDelegate {
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty })
extension PasswordEditorTableViewController: SFSafariViewControllerDelegate {
func safariViewControllerDidFinish(_: SFSafariViewController) {
let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter { !$0.isEmpty }
if copiedLinesSplit?.count ?? 0 > 0 {
let generatedPassword = copiedLinesSplit![0]
let alert = UIAlertController(title: "WannaUseIt?".localize(), message: "", preferredStyle: UIAlertController.Style.alert)
let message = NSMutableAttributedString(string: "\("SeemsLikeYouHaveCopiedSomething.".localize()) \("FirstStringIs:".localize())\n")
message.append(Utils.attributedPassword(plainPassword: generatedPassword))
alert.setValue(message, forKey: "attributedMessage")
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertAction.Style.default, handler: {[unowned self] (action) -> Void in
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertAction.Style.default, handler: { [unowned self] (_) -> Void in
// update tableData so to make sure reloadData() works correctly
self.tableData[self.passwordSection][0][PasswordEditorCellKey.content] = generatedPassword
// update cell manually, no need to call reloadData()
self.fillPasswordCell?.setContent(content: generatedPassword)
}))
alert.addAction(UIAlertAction.cancel())
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
}
}
}
// MARK: - UITextFieldDelegate
extension PasswordEditorTableViewController: UITextFieldDelegate {
extension PasswordEditorTableViewController: UITextFieldDelegate {
// update tableData so to make sure reloadData() works correctly
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == nameCell?.contentTextField {
@ -471,8 +471,8 @@ extension PasswordEditorTableViewController: UITextFieldDelegate {
}
// MARK: - UITextViewDelegate
extension PasswordEditorTableViewController: UITextViewDelegate {
extension PasswordEditorTableViewController: UITextViewDelegate {
// update tableData so to make sure reloadData() works correctly
func textViewDidEndEditing(_ textView: UITextView) {
if textView == additionsCell?.contentTextView {

View file

@ -6,16 +6,16 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SVProgressHUD
import passKit
import SVProgressHUD
import UIKit
fileprivate let hideSectionHeaderThreshold = 6 // hide section header if passwords count is less than the threshold
private let hideSectionHeaderThreshold = 6 // hide section header if passwords count is less than the threshold
class PasswordsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITabBarControllerDelegate, UISearchBarDelegate {
private var passwordsTableEntries: [PasswordTableEntry] = []
private var passwordsTableAllEntries: [PasswordTableEntry] = []
private var parentPasswordEntity: PasswordEntity? = nil
private var parentPasswordEntity: PasswordEntity?
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
@ -30,14 +30,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
private var gitCredential: GitCredential {
get {
switch Defaults.gitAuthenticationMethod {
case .password:
return GitCredential(credential: .http(userName: Defaults.gitUsername))
case .key:
let privateKey: String = AppKeychain.shared.get(for: SshKey.PRIVATE.getKeychainKey()) ?? ""
return GitCredential(credential: .ssh(userName: Defaults.gitUsername, privateKey: privateKey))
}
switch Defaults.gitAuthenticationMethod {
case .password:
return GitCredential(credential: .http(userName: Defaults.gitUsername))
case .key:
let privateKey: String = AppKeychain.shared.get(for: SshKey.PRIVATE.getKeychainKey()) ?? ""
return GitCredential(credential: .ssh(userName: Defaults.gitUsername, privateKey: privateKey))
}
}
@ -49,11 +47,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
uiSearchController.searchBar.sizeToFit()
return uiSearchController
}()
private lazy var syncControl: UIRefreshControl = {
let syncControl = UIRefreshControl()
syncControl.addTarget(self, action: #selector(handleRefresh(_:)), for: UIControl.Event.valueChanged)
return syncControl
}()
private lazy var searchBarView: UIView? = {
guard #available(iOS 11, *) else {
let uiView = UIView(frame: CGRect(x: 0, y: 64, width: self.view.bounds.width, height: 44))
@ -62,6 +62,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
return nil
}()
private lazy var backUIBarButtonItem: UIBarButtonItem = {
let backUIButton = UIButton(type: .system)
if #available(iOS 13.0, *) {
@ -116,16 +117,16 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
return transition
}()
@IBOutlet weak var tableView: UITableView!
@IBOutlet var tableView: UITableView!
private func initPasswordsTableEntries(parent: PasswordEntity?) {
let passwordAllEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false)
let passwordAllEntities = passwordStore.fetchPasswordEntityCoreData(withDir: false)
passwordsTableAllEntries = passwordAllEntities.compactMap {
PasswordTableEntry($0)
}
let passwordEntities = Defaults.isShowFolderOn ?
self.passwordStore.fetchPasswordEntityCoreData(parent: parent) :
passwordStore.fetchPasswordEntityCoreData(parent: parent) :
passwordAllEntities
passwordsTableEntries = passwordEntities.compactMap {
PasswordTableEntry($0)
@ -134,10 +135,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
parentPasswordEntity = parent
}
@IBAction func cancelAddPassword(segue: UIStoryboardSegue) {
@IBAction
func cancelAddPassword(segue _: UIStoryboardSegue) {}
}
@IBAction func saveAddPassword(segue: UIStoryboardSegue) {
@IBAction
func saveAddPassword(segue: UIStoryboardSegue) {
if let controller = segue.source as? AddPasswordTableViewController {
addPassword(password: controller.password!)
}
@ -149,13 +151,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
SVProgressHUD.show(withStatus: "Saving".localize())
DispatchQueue.global(qos: .userInitiated).async {
do {
let _ = try self.passwordStore.add(password: password, keyID: keyID)
_ = try self.passwordStore.add(password: password, keyID: keyID)
DispatchQueue.main.async {
// will trigger reloadTableView() by a notification
SVProgressHUD.showSuccess(withStatus: "Done".localize())
SVProgressHUD.dismiss(withDelay: 1)
}
} catch AppError.PgpPublicKeyNotFound(let key) {
} catch let AppError.PgpPublicKeyNotFound(key) {
DispatchQueue.main.async {
// alert: cancel or select keys
SVProgressHUD.dismiss()
@ -190,15 +192,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do {
try self.passwordStore.pullRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword, progressBlock: {(git_transfer_progress, stop) in
try self.passwordStore.pullRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword, progressBlock: { git_transfer_progress, _ in
DispatchQueue.main.async {
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects)/Float(git_transfer_progress.pointee.total_objects), status: "PullingFromRemoteRepository".localize())
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects) / Float(git_transfer_progress.pointee.total_objects), status: "PullingFromRemoteRepository".localize())
}
})
if self.passwordStore.numberOfLocalCommits > 0 {
try self.passwordStore.pushRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword, transferProgressBlock: {(current, total, bytes, stop) in
try self.passwordStore.pushRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword, transferProgressBlock: { current, total, _, _ in
DispatchQueue.main.async {
SVProgressHUD.showProgress(Float(current)/Float(total), status: "PushingToRemoteRepository".localize())
SVProgressHUD.showProgress(Float(current) / Float(total), status: "PushingToRemoteRepository".localize())
}
})
}
@ -232,7 +234,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
super.viewDidAppear(animated)
if Defaults.isShowFolderOn {
searchController.searchBar.scopeButtonTitles = SearchBarScope.allCases.map { $0.localizedName }
searchController.searchBar.scopeButtonTitles = SearchBarScope.allCases.map(\.localizedName)
} else {
searchController.searchBar.scopeButtonTitles = nil
}
@ -251,7 +253,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
navigationItem.hidesSearchBarWhenScrolling = false
} else {
// Fallback on earlier versions
tableView.contentInset = UIEdgeInsets.init(top: 44, left: 0, bottom: 0, right: 0)
tableView.contentInset = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0)
view.addSubview(searchBarView!)
}
navigationItem.title = "PasswordStore".localize()
@ -272,16 +274,17 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: UIApplication.willEnterForegroundNotification, object: nil)
// listen to the swipe back guesture
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture))
swipeRight.direction = UISwipeGestureRecognizer.Direction.right
self.view.addGestureRecognizer(swipeRight)
view.addGestureRecognizer(swipeRight)
}
@objc func didTapNavigationBar(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: self.navigationController?.navigationBar)
let hitView = self.navigationController?.navigationBar.hitTest(location, with: nil)
@objc
func didTapNavigationBar(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: navigationController?.navigationBar)
let hitView = navigationController?.navigationBar.hitTest(location, with: nil)
guard !(hitView is UIControl) else { return }
guard (passwordStore.numberOfLocalCommits != 0) else { return }
guard passwordStore.numberOfLocalCommits != 0 else { return }
let ac = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let allAction = UIAlertAction(title: "All Passwords", style: .default) { _ in
@ -289,7 +292,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
let unsyncedAction = UIAlertAction(title: "Unsynced Passwords", style: .default) { _ in
let filteredPasswordsTableEntries = self.passwordsTableEntries.filter { entry in
return !entry.synced
!entry.synced
}
self.reloadTableView(data: filteredPasswordsTableEntries, label: .unsynced)
}
@ -299,7 +302,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
ac.addAction(unsyncedAction)
ac.addAction(cancelAction)
self.present(ac, animated: true, completion: nil)
present(ac, animated: true, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
@ -310,7 +313,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
// Add gesture recognizer to the navigation bar when the view is about to appear
self.navigationController?.navigationBar.addGestureRecognizer(tapNavigationBarGestureRecognizer)
navigationController?.navigationBar.addGestureRecognizer(tapNavigationBarGestureRecognizer)
// This allows controlls in the navigation bar to continue receiving touches
tapNavigationBarGestureRecognizer.cancelsTouchesInView = false
@ -318,9 +321,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
tableView.refreshControl = passwordStore.repositoryExists() ? syncControl : nil
}
override func viewWillDisappear(_ animated: Bool) {
override func viewWillDisappear(_: Bool) {
// Remove gesture recognizer from navigation bar when view is about to disappear
self.navigationController?.navigationBar.removeGestureRecognizer(tapNavigationBarGestureRecognizer)
navigationController?.navigationBar.removeGestureRecognizer(tapNavigationBarGestureRecognizer)
}
override func viewWillLayoutSubviews() {
@ -332,12 +335,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
func numberOfSections(in _: UITableView) -> Int {
sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].entries.count
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
sections[section].entries.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@ -372,7 +375,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordTableEntry {
return sections[indexPath.section].entries[indexPath.row]
sections[indexPath.section].entries[indexPath.row]
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -390,16 +393,18 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
}
@objc func respondToSwipeGesture(gesture: UIGestureRecognizer) {
@objc
func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
// swipe right -> swipe back
if swipeGesture.direction == .right && parentPasswordEntity != nil {
self.backAction(nil)
if swipeGesture.direction == .right, parentPasswordEntity != nil {
backAction(nil)
}
}
}
@objc func backAction(_ sender: Any?) {
@objc
func backAction(_: Any?) {
guard Defaults.isShowFolderOn else { return }
var anim: CATransition? = transitionFromLeft
if parentPasswordEntity == nil {
@ -408,13 +413,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
reloadTableView(parent: parentPasswordEntity?.parent, anim: anim)
}
@objc func addPasswordAction(_ sender: Any?) {
@objc
func addPasswordAction(_: Any?) {
if shouldPerformSegue(withIdentifier: "addPasswordSegue", sender: self) {
performSegue(withIdentifier: "addPasswordSegue", sender: self)
}
}
@objc func longPressAction(_ gesture: UILongPressGestureRecognizer) {
@objc
func longPressAction(_ gesture: UILongPressGestureRecognizer) {
if gesture.state == UIGestureRecognizer.State.began {
let touchPoint = gesture.location(in: tableView)
if let indexPath = tableView.indexPathForRow(at: touchPoint) {
@ -424,31 +431,31 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
private func hideSectionHeader() -> Bool {
if passwordsTableEntries.count < hideSectionHeaderThreshold || self.searchController.isActive {
if passwordsTableEntries.count < hideSectionHeaderThreshold || searchController.isActive {
return true
}
return false
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
if hideSectionHeader() {
return nil
}
return sections[section].title
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
func sectionIndexTitles(for _: UITableView) -> [String]? {
if hideSectionHeader() {
return nil
}
return sections.map { $0.title }
return sections.map(\.title)
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return index
func tableView(_: UITableView, sectionForSectionIndexTitle _: String, at index: Int) -> Int {
index
}
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
func tableView(_: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
decryptThenCopyPassword(from: indexPath)
}
@ -460,7 +467,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
let passwordEntity = getPasswordEntry(by: indexPath).passwordEntity
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
SVProgressHUD.dismiss()
self.decryptPassword(passwordEntity: passwordEntity)
decryptPassword(passwordEntity: passwordEntity)
}
private func decryptPassword(passwordEntity: PasswordEntity, keyID: String? = nil) {
@ -476,7 +483,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
SVProgressHUD.showSuccess(withStatus: "PasswordCopiedToPasteboard.".localize())
SVProgressHUD.dismiss(withDelay: 0.6)
}
} catch AppError.PgpPrivateKeyNotFound(let key) {
} catch let AppError.PgpPrivateKeyNotFound(key) {
DispatchQueue.main.async {
// alert: cancel or try again
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
@ -488,7 +495,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
self.present(alert, animated: true)
}
} catch {
} catch {
DispatchQueue.main.async {
Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self)
}
@ -496,14 +503,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
}
private func generateSections(item: [PasswordTableEntry]) {
let collation = UILocalizedIndexedCollation.current()
let sectionTitles = collation.sectionIndexTitles
var newSections = [(title: String, entries: [PasswordTableEntry])]()
// initialize all sections
for i in 0..<sectionTitles.count {
for i in 0 ..< sectionTitles.count {
newSections.append((title: sectionTitles[i], entries: [PasswordTableEntry]()))
}
@ -514,21 +520,21 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
// sort each list and set sectionTitles
for i in 0..<sectionTitles.count {
for i in 0 ..< sectionTitles.count {
let entriesToSort = newSections[i].entries
let sortedEntries = collation.sortedArray(from: entriesToSort, collationStringSelector: #selector(getter: PasswordTableEntry.title))
newSections[i].entries = sortedEntries as! [PasswordTableEntry]
}
// only keep non-empty sections
sections = newSections.filter {$0.entries.count > 0}
sections = newSections.filter { !$0.entries.isEmpty }
}
@objc func actOnSearchNotification() {
@objc
func actOnSearchNotification() {
searchController.searchBar.becomeFirstResponder()
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "showPasswordDetail" {
guard PGPAgent.shared.isPrepared else {
@ -540,7 +546,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
return false
}
} else if identifier == "addPasswordSegue" {
guard PGPAgent.shared.isPrepared && self.passwordStore.storeRepository != nil else {
guard PGPAgent.shared.isPrepared && passwordStore.storeRepository != nil else {
Utils.alert(title: "CannotAddPassword".localize(), message: "MakeSurePgpAndGitProperlySet.".localize(), controller: self, completion: nil)
return false
}
@ -551,7 +557,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPasswordDetail" {
if let viewController = segue.destination as? PasswordDetailTableViewController {
let selectedIndexPath = self.tableView.indexPath(for: sender as! UITableViewCell)!
let selectedIndexPath = tableView.indexPath(for: sender as! UITableViewCell)!
let passwordEntity = getPasswordEntry(by: selectedIndexPath).passwordEntity
viewController.passwordEntity = passwordEntity
}
@ -568,8 +574,8 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
func filterContentForSearchText(searchText: String, scope: SearchBarScope = .all) {
var entries: [PasswordTableEntry] = scope == .all ? passwordsTableAllEntries : passwordsTableEntries
if searchController.isActive && searchController.searchBar.text != "" {
entries = entries.filter {$0.match(searchText)}
if searchController.isActive, searchController.searchBar.text != "" {
entries = entries.filter { $0.match(searchText) }
}
reloadTableView(data: entries)
}
@ -587,7 +593,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
if #available(iOS 11, *) {
navigationController?.navigationBar.prefersLargeTitles = false
}
self.navigationController?.navigationBar.removeGestureRecognizer(tapNavigationBarGestureRecognizer)
navigationController?.navigationBar.removeGestureRecognizer(tapNavigationBarGestureRecognizer)
} else {
navigationItem.leftBarButtonItem = nil
switch label {
@ -599,17 +605,17 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
if #available(iOS 11, *) {
navigationController?.navigationBar.prefersLargeTitles = true
}
self.navigationController?.navigationBar.addGestureRecognizer(tapNavigationBarGestureRecognizer)
navigationController?.navigationBar.addGestureRecognizer(tapNavigationBarGestureRecognizer)
}
navigationItem.rightBarButtonItem = addPasswordUIBarButtonItem
// set the password table
generateSections(item: data)
if anim != nil {
self.tableView.layer.add(anim!, forKey: "UITableViewReloadDataAnimationKey")
tableView.layer.add(anim!, forKey: "UITableViewReloadDataAnimationKey")
}
tableView.reloadData()
self.tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey")
tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey")
// set the sync control title
let atribbutedTitle = "LastSynced".localize() + ": \(lastSyncedTimeString())"
@ -617,7 +623,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
private func lastSyncedTimeString() -> String {
guard let date = self.passwordStore.lastSyncedTime else {
guard let date = passwordStore.lastSyncedTime else {
return "SyncAgain?".localize()
}
let formatter = DateFormatter()
@ -631,7 +637,8 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
reloadTableView(data: passwordsTableEntries, label: label, anim: anim)
}
@objc func actOnReloadTableViewRelatedNotification() {
@objc
func actOnReloadTableViewRelatedNotification() {
DispatchQueue.main.async { [weak weakSelf = self] in
guard let strongSelf = weakSelf else { return }
// Reset selectedScopeButtonIndex to make sure the correct reloadTableView
@ -641,36 +648,37 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
}
@objc func handleRefresh(_ syncControl: UIRefreshControl) {
@objc
func handleRefresh(_: UIRefreshControl) {
syncPasswords()
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if viewController == self.navigationController {
func tabBarController(_: UITabBarController, didSelect viewController: UIViewController) {
if viewController == navigationController {
let currentTime = Date().timeIntervalSince1970
let duration = currentTime - self.tapTabBarTime
self.tapTabBarTime = currentTime
let duration = currentTime - tapTabBarTime
tapTabBarTime = currentTime
if duration < 0.35 {
let topIndexPath = IndexPath(row: 0, section: 0)
if tableView.numberOfSections > 0 {
tableView.scrollToRow(at: topIndexPath, at: .bottom, animated: true)
}
self.tapTabBarTime = 0
if tableView.numberOfSections > 0 {
tableView.scrollToRow(at: topIndexPath, at: .bottom, animated: true)
}
tapTabBarTime = 0
return
}
backAction(self)
}
}
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
func searchBar(_: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
// update the default search scope
Defaults.searchDefault = SearchBarScope(rawValue: selectedScope)
updateSearchResults(for: searchController)
}
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
func searchBarShouldBeginEditing(_: UISearchBar) -> Bool {
// set the default search scope to "all"
if Defaults.isShowFolderOn && Defaults.searchDefault == .all {
if Defaults.isShowFolderOn, Defaults.searchDefault == .all {
searchController.searchBar.selectedScopeButtonIndex = SearchBarScope.all.rawValue
} else {
searchController.searchBar.selectedScopeButtonIndex = SearchBarScope.current.rawValue
@ -678,7 +686,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
return true
}
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
func searchBarShouldEndEditing(_: UISearchBar) -> Bool {
// set the default search scope to "current"
searchController.searchBar.selectedScopeButtonIndex = SearchBarScope.current.rawValue
updateSearchResults(for: searchController)
@ -686,13 +694,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
private func requestCredentialPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
return requestGitCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self)
requestGitCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self)
}
}
extension PasswordsViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let scope = SearchBarScope(rawValue: searchController.searchBar.selectedScopeButtonIndex) ?? .all
filterContentForSearchText(searchText: searchController.searchBar.text!, scope: scope)
@ -700,8 +706,7 @@ extension PasswordsViewController: UISearchResultsUpdating {
}
extension PasswordsViewController: CAAnimationDelegate {
func animationDidStart(_ anim: CAAnimation) {
func animationDidStart(_: CAAnimation) {
view.window?.backgroundColor = Colors.systemBackground
view.layer.backgroundColor = Colors.systemBackground.cgColor
}

View file

@ -6,11 +6,11 @@
// Copyright © 2017 Yishi Lin. All rights reserved.
//
import UIKit
import AVFoundation
import OneTimePassword
import SVProgressHUD
import passKit
import SVProgressHUD
import UIKit
protocol QRScannerControllerDelegate {
func checkScannedOutput(line: String) -> (accept: Bool, message: String)
@ -18,8 +18,7 @@ protocol QRScannerControllerDelegate {
}
class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
@IBOutlet weak var scannerOutput: UILabel!
@IBOutlet var scannerOutput: UILabel!
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
@ -93,15 +92,12 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg
// Dispose of any resources that can be recreated.
}
// MARK: - AVCaptureMetadataOutputObjectsDelegate Methods
func metadataOutput(_ captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
func metadataOutput(_: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from _: AVCaptureConnection) {
if let metadataObj = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
supportedCodeTypes.contains(metadataObj.type),
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj) {
// draw a bounds on the found QR code
qrCodeFrameView?.frame = barCodeObject.bounds

View file

@ -6,12 +6,11 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class RawPasswordViewController: UIViewController {
@IBOutlet weak var rawPasswordTextView: UITextView!
@IBOutlet var rawPasswordTextView: UITextView!
var password: Password?
override func viewDidLoad() {

View file

@ -6,13 +6,12 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
@IBOutlet var armorPrivateKeyTextView: UITextView!
@IBOutlet var scanPrivateKeyCell: UITableViewCell!
var gitSSHPrivateKeyPassphrase: String?
var armorPrivateKey: String?
@ -22,32 +21,33 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
var message = ""
func reset() {
self.segments.removeAll()
segments.removeAll()
message = "LookingForStartingFrame.".localize()
}
func addSegment(segment: String) -> (accept: Bool, message: String) {
// Skip duplicated segments.
guard segment != self.segments.last else {
return (accept: false, message: self.message)
guard segment != segments.last else {
return (accept: false, message: message)
}
// Check whether we have found the first block.
guard !self.segments.isEmpty || segment.contains("-----BEGIN") else {
return (accept: false, message: self.message)
guard !segments.isEmpty || segment.contains("-----BEGIN") else {
return (accept: false, message: message)
}
// Update the list of scanned segment and return.
self.segments.append(segment)
segments.append(segment)
if segment.range(of: "-----END.*KEY-----", options: .regularExpression, range: nil, locale: nil) != nil {
self.message = "Done".localize()
return (accept: true, message: self.message)
message = "Done".localize()
return (accept: true, message: message)
} else {
self.message = "ScannedQrCodes(%d)".localize(self.segments.count)
return (accept: false, message: self.message)
message = "ScannedQrCodes(%d)".localize(segments.count)
return (accept: false, message: message)
}
}
}
var scanned = ScannedSSHKey()
override func viewDidLoad() {
@ -59,12 +59,13 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
scanPrivateKeyCell?.accessoryType = .disclosureIndicator
}
@IBAction func doneButtonTapped(_ sender: Any) {
@IBAction
func doneButtonTapped(_: Any) {
armorPrivateKey = armorPrivateKeyTextView.text
performSegue(withIdentifier: "importSSHKeySegue", sender: self)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
func textView(_: UITextView, shouldChangeTextIn _: NSRange, replacementText text: String) -> Bool {
if text == UIPasteboard.general.string {
// user pastes something, do the copy here again and clear the pasteboard in 45s
SecurePasteboard.shared.copy(textToCopy: text)
@ -76,23 +77,24 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
let selectedCell = tableView.cellForRow(at: indexPath)
if selectedCell == scanPrivateKeyCell {
scanned.reset()
self.performSegue(withIdentifier: "showSSHScannerSegue", sender: self)
performSegue(withIdentifier: "showSSHScannerSegue", sender: self)
}
tableView.deselectRow(at: indexPath, animated: true)
}
// MARK: - QRScannerControllerDelegate Methods
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
return scanned.addSegment(segment: line)
}
// MARK: - QRScannerControllerDelegate Methods
func handleScannedOutput(line: String) {
func handleScannedOutput(line _: String) {
armorPrivateKeyTextView.text = scanned.segments.joined(separator: "")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
override func prepare(for segue: UIStoryboardSegue, sender _: Any?) {
if segue.identifier == "showSSHScannerSegue" {
if let navController = segue.destination as? UINavigationController {
if let viewController = navController.topViewController as? QRScannerController {
@ -104,13 +106,11 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
}
}
@IBAction private func cancelSSHScanner(segue: UIStoryboardSegue) {
}
@IBAction
private func cancelSSHScanner(segue _: UIStoryboardSegue) {}
}
extension SSHKeyArmorImportTableViewController: KeyImporter {
static let keySource = KeySource.armor
static let label = "AsciiArmorEncryptedKey".localize()

View file

@ -10,12 +10,12 @@ import passKit
import SVProgressHUD
class SSHKeyFileImportTableViewController: AutoCellHeightUITableViewController {
@IBOutlet var sshPrivateKeyFile: UITableViewCell!
@IBOutlet weak var sshPrivateKeyFile: UITableViewCell!
private var privateKey: String?
private var privateKey: String? = nil
@IBAction func doneButtonTapped(_ sender: Any) {
@IBAction
func doneButtonTapped(_: Any) {
performSegue(withIdentifier: "importSSHKeySegue", sender: self)
}
@ -35,7 +35,6 @@ class SSHKeyFileImportTableViewController: AutoCellHeightUITableViewController {
}
extension SSHKeyFileImportTableViewController: UIDocumentPickerDelegate {
func documentPicker(_: UIDocumentPickerViewController, didPickDocumentsAt url: [URL]) {
guard let url = url.first else {
return
@ -61,7 +60,6 @@ extension SSHKeyFileImportTableViewController: UIDocumentPickerDelegate {
}
extension SSHKeyFileImportTableViewController: KeyImporter {
static let keySource = KeySource.file
static let label = "LoadFromFiles".localize()

View file

@ -6,12 +6,11 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import SVProgressHUD
import passKit
import SVProgressHUD
class SSHKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
@IBOutlet weak var privateKeyURLTextField: UITextField!
@IBOutlet var privateKeyURLTextField: UITextField!
var sshPrivateKeyURL: URL?
@ -20,11 +19,12 @@ class SSHKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
privateKeyURLTextField.text = Defaults.gitSSHPrivateKeyURL?.absoluteString
}
@IBAction func doneButtonTapped(_ sender: UIButton) {
@IBAction
func doneButtonTapped(_: UIButton) {
guard let text = privateKeyURLTextField.text,
let privateKeyURL = URL(string: text) else {
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKeyUrl.".localize(), controller: self)
return
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKeyUrl.".localize(), controller: self)
return
}
if privateKeyURL.scheme?.lowercased() == "http" {
@ -41,7 +41,6 @@ class SSHKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
}
extension SSHKeyUrlImportTableViewController: KeyImporter {
static let keySource = KeySource.url
static let label = "DownloadFromUrl".localize()

View file

@ -10,15 +10,16 @@ import UIKit
class SettingsSplitViewController: UISplitViewController, UISplitViewControllerDelegate {
override func viewDidLoad() {
self.delegate = self
self.preferredDisplayMode = .allVisible
delegate = self
preferredDisplayMode = .allVisible
}
func splitViewController(
_ splitViewController: UISplitViewController,
collapseSecondary secondaryViewController: UIViewController,
onto primaryViewController: UIViewController) -> Bool {
_: UISplitViewController,
collapseSecondary _: UIViewController,
onto _: UIViewController
) -> Bool {
// Return true to prevent UIKit from applying its default behavior
return true
true
}
}

View file

@ -6,26 +6,27 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SVProgressHUD
import CoreData
import passKit
import SVProgressHUD
import UIKit
class SettingsTableViewController: UITableViewController, UITabBarControllerDelegate {
@IBOutlet weak var pgpKeyTableViewCell: UITableViewCell!
@IBOutlet weak var passcodeTableViewCell: UITableViewCell!
@IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell!
@IBOutlet var pgpKeyTableViewCell: UITableViewCell!
@IBOutlet var passcodeTableViewCell: UITableViewCell!
@IBOutlet var passwordRepositoryTableViewCell: UITableViewCell!
var setPasscodeLockAlert: UIAlertController?
let passwordStore = PasswordStore.shared
let keychain = AppKeychain.shared
var passcodeLock = PasscodeLock.shared
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
func tabBarController(_: UITabBarController, didSelect _: UIViewController) {
navigationController?.popViewController(animated: true)
}
@IBAction func savePGPKey(segue: UIStoryboardSegue) {
@IBAction
func savePGPKey(segue: UIStoryboardSegue) {
guard let sourceController = segue.source as? PGPKeyImporter, sourceController.isReadyToUse() else {
return
}
@ -58,19 +59,20 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
}
}
@IBAction func saveGitServerSetting(segue: UIStoryboardSegue) {
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults.gitURL.host
@IBAction
func saveGitServerSetting(segue _: UIStoryboardSegue) {
passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults.gitURL.host
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil)
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults.gitURL.host
passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults.gitURL.host
setPGPKeyTableViewCellDetailText()
setPasscodeLockCell()
}
override func viewWillAppear(_ animated: Bool) {
override func viewWillAppear(_: Bool) {
super.viewWillAppear(true)
tabBarController!.delegate = self
setPasswordRepositoryTableViewCellDetailText()
@ -78,9 +80,9 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
private func setPasscodeLockCell() {
if passcodeLock.hasPasscode {
self.passcodeTableViewCell.detailTextLabel?.text = "On".localize()
passcodeTableViewCell.detailTextLabel?.text = "On".localize()
} else {
self.passcodeTableViewCell.detailTextLabel?.text = "Off".localize()
passcodeTableViewCell.detailTextLabel?.text = "Off".localize()
}
}
@ -107,7 +109,8 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
passwordRepositoryTableViewCell.detailTextLabel?.text = host
}
@objc func actOnPasswordStoreErasedNotification() {
@objc
func actOnPasswordStoreErasedNotification() {
setPGPKeyTableViewCellDetailText()
setPasswordRepositoryTableViewCellDetailText()
setPasscodeLockCell()
@ -180,9 +183,8 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let passcodeRemoveViewController = PasscodeLockViewController()
let removePasscodeAction = UIAlertAction(title: "RemovePasscode".localize(), style: .destructive) { [weak self] _ in
passcodeRemoveViewController.successCallback = {
passcodeRemoveViewController.successCallback = {
self?.passcodeLock.delete()
self?.setPasscodeLockCell()
}
@ -198,10 +200,11 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
optionMenu.addAction(UIAlertAction.cancel())
optionMenu.popoverPresentationController?.sourceView = passcodeTableViewCell
optionMenu.popoverPresentationController?.sourceRect = passcodeTableViewCell.bounds
self.present(optionMenu, animated: true, completion: nil)
present(optionMenu, animated: true, completion: nil)
}
@objc func alertTextFieldDidChange(_ sender: UITextField) {
@objc
func alertTextFieldDidChange(_ sender: UITextField) {
// check whether we should enable the Save button in setPasscodeLockAlert
if let setPasscodeLockAlert = self.setPasscodeLockAlert,
let setPasscodeLockAlertTextFields0 = setPasscodeLockAlert.textFields?[0],
@ -218,25 +221,25 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
func setPasscodeLock() {
// prepare the alert for setting the passcode
setPasscodeLockAlert = UIAlertController(title: "SetPasscode".localize(), message: "FillInAppPasscode.".localize(), preferredStyle: .alert)
setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in
setPasscodeLockAlert?.addTextField(configurationHandler: { (_ textField: UITextField) -> Void in
textField.placeholder = "Passcode".localize()
textField.isSecureTextEntry = true
textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
})
setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in
setPasscodeLockAlert?.addTextField(configurationHandler: { (_ textField: UITextField) -> Void in
textField.placeholder = "PasswordConfirmation".localize()
textField.isSecureTextEntry = true
textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
})
// save action
let saveAction = UIAlertAction(title: "Save".localize(), style: .default) { (action:UIAlertAction) -> Void in
let saveAction = UIAlertAction(title: "Save".localize(), style: .default) { (_: UIAlertAction) -> Void in
let passcode: String = self.setPasscodeLockAlert!.textFields![0].text!
self.passcodeLock.save(passcode: passcode)
// refresh the passcode lock cell ("On")
self.setPasscodeLockCell()
}
saveAction.isEnabled = false // disable the Save button by default
saveAction.isEnabled = false // disable the Save button by default
// cancel action
let cancelAction = UIAlertAction.cancel()
@ -244,17 +247,16 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
// present
setPasscodeLockAlert?.addAction(saveAction)
setPasscodeLockAlert?.addAction(cancelAction)
self.present(setPasscodeLockAlert!, animated: true, completion: nil)
present(setPasscodeLockAlert!, animated: true, completion: nil)
}
}
extension SettingsTableViewController: PGPKeyImporter {
static let keySource = KeySource.itunes
static let label = "ITunesFileSharing".localize()
func isReadyToUse() -> Bool {
return KeyFileManager.PublicPgp.doesKeyFileExist() && KeyFileManager.PrivatePgp.doesKeyFileExist()
KeyFileManager.PublicPgp.doesKeyFileExist() && KeyFileManager.PrivatePgp.doesKeyFileExist()
}
func importKeys() throws {

View file

@ -18,7 +18,7 @@ class SpecialThanksTableViewController: BasicStaticTableViewController {
"https://icons8.com"],
["FlatIcon",
"https://www.flaticon.com"],
]
]
override func viewDidLoad() {
tableData.append([])

View file

@ -7,12 +7,12 @@
//
import Foundation
import SVProgressHUD
import passKit
import SVProgressHUD
public func requestGitCredentialPassword(credential: GitCredential.Credential,
lastPassword: String?,
controller: UIViewController) -> String? {
lastPassword: String?,
controller: UIViewController) -> String? {
let sem = DispatchSemaphore(value: 0)
var password: String?
let message: String = {
@ -27,21 +27,21 @@ public func requestGitCredentialPassword(credential: GitCredential.Credential,
DispatchQueue.main.async {
SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: .alert)
alert.addTextField() {
alert.addTextField {
$0.text = lastPassword ?? ""
$0.isSecureTextEntry = true
}
alert.addAction(UIAlertAction.ok() { _ in
alert.addAction(UIAlertAction.ok { _ in
password = alert.textFields?.first?.text
sem.signal()
})
alert.addAction(UIAlertAction.cancel() { _ in
alert.addAction(UIAlertAction.cancel { _ in
password = nil
sem.signal()
})
controller.present(alert, animated: true)
}
let _ = sem.wait(timeout: .distantFuture)
_ = sem.wait(timeout: .distantFuture)
return password
}

View file

@ -25,7 +25,7 @@ class SecurePasteboard {
// exit the existing background task, if any
if backgroundTaskID != UIBackgroundTaskIdentifier.invalid {
UIApplication.shared.endBackgroundTask(UIBackgroundTaskIdentifier.invalid)
self.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
backgroundTaskID = UIBackgroundTaskIdentifier.invalid
}
backgroundTaskID = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in
@ -40,5 +40,4 @@ class SecurePasteboard {
self?.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
}
}
}

View file

@ -7,8 +7,8 @@
//
import Foundation
import SVProgressHUD
import passKit
import SVProgressHUD
extension Utils {
static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) {

View file

@ -6,8 +6,8 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
import UIKit
protocol FillPasswordTableViewCellDelegate {
func generateAndCopyPassword()
@ -15,12 +15,11 @@ protocol FillPasswordTableViewCellDelegate {
}
class FillPasswordTableViewCell: UITableViewCell, ContentProvider {
@IBOutlet weak var contentTextField: UITextField!
@IBOutlet var contentTextField: UITextField!
var delegate: FillPasswordTableViewCellDelegate?
@IBOutlet weak var settingButton: UIButton!
@IBOutlet weak var generateButton: UIButton!
@IBOutlet var settingButton: UIButton!
@IBOutlet var generateButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
@ -32,21 +31,24 @@ class FillPasswordTableViewCell: UITableViewCell, ContentProvider {
generateButton.imageView?.contentMode = .scaleAspectFit
}
@IBAction func generatePassword(_ sender: UIButton) {
self.delegate?.generateAndCopyPassword()
@IBAction
func generatePassword(_: UIButton) {
delegate?.generateAndCopyPassword()
}
@IBAction func showHidePasswordSettings() {
self.delegate?.showHidePasswordSettings()
@IBAction
func showHidePasswordSettings() {
delegate?.showHidePasswordSettings()
}
// re-color
@IBAction func textFieldDidChange(_ sender: UITextField) {
@IBAction
func textFieldDidChange(_ sender: UITextField) {
contentTextField.attributedText = Utils.attributedPassword(plainPassword: sender.text ?? "")
}
func getContent() -> String? {
return contentTextField.attributedText?.string
contentTextField.attributedText?.string
}
func setContent(content: String?) {

View file

@ -6,9 +6,9 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SVProgressHUD
import passKit
import SVProgressHUD
import UIKit
struct LabelTableViewCellData {
var title: String
@ -16,9 +16,8 @@ struct LabelTableViewCellData {
}
class LabelTableViewCell: UITableViewCell {
@IBOutlet weak var contentLabel: UILabel!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet var contentLabel: UILabel!
@IBOutlet var titleLabel: UILabel!
private enum CellType {
case password, URL, HOTP, other
@ -27,7 +26,7 @@ class LabelTableViewCell: UITableViewCell {
private var type = CellType.other
private var isReveal = false
weak var delegatePasswordTableView : PasswordDetailTableViewController?
weak var delegatePasswordTableView: PasswordDetailTableViewController?
private var passwordDisplayButton: UIButton?
private var buttons: UIView?
@ -74,12 +73,10 @@ class LabelTableViewCell: UITableViewCell {
}
override var canBecomeFirstResponder: Bool {
get {
return true
}
true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
override func canPerformAction(_ action: Selector, withSender _: Any?) -> Bool {
switch type {
case .password:
if isReveal {
@ -100,11 +97,12 @@ class LabelTableViewCell: UITableViewCell {
}
}
override func copy(_ sender: Any?) {
override func copy(_: Any?) {
SecurePasteboard.shared.copy(textToCopy: cellData?.content)
}
@objc func revealPassword(_ sender: Any?) {
@objc
func revealPassword(_: Any?) {
let plainPassword = cellData?.content ?? ""
if type == .password {
contentLabel.attributedText = Utils.attributedPassword(plainPassword: plainPassword)
@ -115,7 +113,8 @@ class LabelTableViewCell: UITableViewCell {
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Invisible"), for: .normal)
}
@objc func concealPassword(_ sender: Any?) {
@objc
func concealPassword(_: Any?) {
if type == .password {
if cellData?.content.isEmpty == false {
contentLabel.text = Globals.passwordDots
@ -130,7 +129,8 @@ class LabelTableViewCell: UITableViewCell {
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
}
@objc func reversePasswordDisplay(_ sender: Any?) {
@objc
func reversePasswordDisplay(_ sender: Any?) {
if isReveal {
// conceal
concealPassword(sender)
@ -140,19 +140,21 @@ class LabelTableViewCell: UITableViewCell {
}
}
@objc func openLink(_ sender: Any?) {
@objc
func openLink(_: Any?) {
// if isURLCell, passwordTableView should not be nil
delegatePasswordTableView!.openLink(to: cellData?.content)
}
@objc func getNextHOTP(_ sender: Any?) {
@objc
func getNextHOTP(_: Any?) {
// if isHOTPCell, passwordTableView should not be nil
delegatePasswordTableView!.getNextHOTP()
}
private func updateButtons() {
// total width and height of a button
let height = min(self.bounds.height, 36.0)
let height = min(bounds.height, 36.0)
let width = max(height * 0.8, Globals.tableCellButtonSize)
// margins (between button boundary and icon)
@ -167,7 +169,7 @@ class LabelTableViewCell: UITableViewCell {
passwordDisplayButton!.frame = CGRect(x: 0, y: 0, width: width, height: height)
passwordDisplayButton!.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
passwordDisplayButton!.imageView?.contentMode = .scaleAspectFit
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsets.init(top: marginY, left: marginX, bottom: marginY, right: marginX)
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsets(top: marginY, left: marginX, bottom: marginY, right: marginX)
passwordDisplayButton!.addTarget(self, action: #selector(reversePasswordDisplay), for: UIControl.Event.touchUpInside)
buttons = passwordDisplayButton
}
@ -177,7 +179,7 @@ class LabelTableViewCell: UITableViewCell {
nextButton.frame = CGRect(x: 0, y: 0, width: width, height: height)
nextButton.setImage(#imageLiteral(resourceName: "Refresh"), for: .normal)
nextButton.imageView?.contentMode = .scaleAspectFit
nextButton.contentEdgeInsets = UIEdgeInsets.init(top: marginY, left: marginX, bottom: marginY, right: marginX)
nextButton.contentEdgeInsets = UIEdgeInsets(top: marginY, left: marginX, bottom: marginY, right: marginX)
nextButton.addTarget(self, action: #selector(getNextHOTP), for: UIControl.Event.touchUpInside)
// password button
@ -186,7 +188,7 @@ class LabelTableViewCell: UITableViewCell {
passwordDisplayButton!.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
passwordDisplayButton!.imageView?.contentMode = .scaleAspectFit
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsets.init(top: marginY, left: marginX, bottom: marginY, right: marginX)
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsets(top: marginY, left: marginX, bottom: marginY, right: marginX)
passwordDisplayButton!.addTarget(self, action: #selector(reversePasswordDisplay), for: UIControl.Event.touchUpInside)
buttons = UIView()
@ -197,6 +199,6 @@ class LabelTableViewCell: UITableViewCell {
passwordDisplayButton = nil
buttons = nil
}
self.accessoryView = buttons
accessoryView = buttons
}
}

View file

@ -9,13 +9,12 @@
import UIKit
class PasswordDetailTitleTableViewCell: UITableViewCell {
@IBOutlet weak var categoryLabel: UILabel!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var passwordImageImageView: UIImageView!
@IBOutlet var categoryLabel: UILabel!
@IBOutlet var nameLabel: UILabel!
@IBOutlet var passwordImageImageView: UIImageView!
@IBOutlet var labelImageConstraint: NSLayoutConstraint!
@IBOutlet var labelCellConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
}
@ -25,5 +24,4 @@ class PasswordDetailTitleTableViewCell: UITableViewCell {
// Configure the view for the selected state
}
}

View file

@ -10,7 +10,6 @@ import passKit
import UIKit
class SliderTableViewCell: UITableViewCell {
@IBOutlet var titleLabel: UILabel!
@IBOutlet var valueLabel: UILabel!
@IBOutlet var slider: UISlider!
@ -20,7 +19,8 @@ class SliderTableViewCell: UITableViewCell {
private var delegate: PasswordSettingSliderTableViewCellDelegate!
@IBAction func handleSliderValueChange(_ sender: UISlider) {
@IBAction
func handleSliderValueChange(_ sender: UISlider) {
let newRoundedValue = Int(sender.value)
// Proceed only if the rounded value gets updated.
guard checker(newRoundedValue) else {
@ -67,9 +67,8 @@ class SliderTableViewCell: UITableViewCell {
}
extension SliderTableViewCell: ContentProvider {
func getContent() -> String? {
return nil
nil
}
func setContent(content _: String?) {}

View file

@ -10,7 +10,6 @@ import passKit
import UIKit
class SwitchTableViewCell: UITableViewCell {
@IBOutlet var titleLabel: UILabel!
@IBOutlet var controlSwitch: UISwitch!
@ -18,7 +17,8 @@ class SwitchTableViewCell: UITableViewCell {
private var delegate: PasswordSettingSliderTableViewCellDelegate!
@IBAction func switchValueChanged(_: Any) {
@IBAction
func switchValueChanged(_: Any) {
updater(controlSwitch.isOn)
delegate.generateAndCopyPassword()
}

View file

@ -9,11 +9,10 @@
import UIKit
class TextFieldTableViewCell: UITableViewCell, ContentProvider {
@IBOutlet weak var contentTextField: UITextField!
@IBOutlet var contentTextField: UITextField!
func getContent() -> String? {
return contentTextField.text
contentTextField.text
}
func setContent(content: String?) {

View file

@ -9,17 +9,16 @@
import UIKit
class TextViewTableViewCell: UITableViewCell, ContentProvider {
@IBOutlet weak var contentTextView: UITextView!
@IBOutlet var contentTextView: UITextView!
override func awakeFromNib() {
super.awakeFromNib()
self.contentTextView.textContainer.lineFragmentPadding = 0
self.contentTextView.textContainerInset = .zero
contentTextView.textContainer.lineFragmentPadding = 0
contentTextView.textContainerInset = .zero
}
func getContent() -> String? {
return contentTextView.text
contentTextView.text
}
func setContent(content: String?) {

View file

@ -6,11 +6,10 @@
Copyright © 2019 Bob Sun. All rights reserved.
*/
import UIKit
import passKit
import UIKit
class UICodeHighlightingLabel: UILocalizedLabel {
private static let CODE_ATTRIBUTES: [NSAttributedString.Key: Any] = [.font: UIFont(name: "Menlo-Regular", size: 12)!]
private static let ATTRIBUTED_NEWLINE = NSAttributedString(string: "\n")
@ -43,4 +42,3 @@ class UICodeHighlightingLabel: UILocalizedLabel {
return formattedText
}
}

View file

@ -6,11 +6,10 @@
Copyright © 2019 Bob Sun. All rights reserved.
*/
import UIKit
import passKit
import UIKit
class UILocalizedLabel: UILabel {
override func awakeFromNib() {
super.awakeFromNib()
text = text?.localize()

View file

@ -10,8 +10,8 @@ import AuthenticationServices
import passKit
class CredentialProviderViewController: ASCredentialProviderViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate {
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
@IBOutlet var searchBar: UISearchBar!
@IBOutlet var tableView: UITableView!
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
@ -26,13 +26,13 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
}()
/*
Prepare your UI to list available credentials for the user to choose from. The items in
'serviceIdentifiers' describe the service the user is logging in to, so your extension can
prioritize the most relevant credentials in the list.
*/
Prepare your UI to list available credentials for the user to choose from. The items in
'serviceIdentifiers' describe the service the user is logging in to, so your extension can
prioritize the most relevant credentials in the list.
*/
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
// clean up the search bar
guard serviceIdentifiers.count > 0 else {
guard !serviceIdentifiers.isEmpty else {
searchBar.text = ""
searchBar.becomeFirstResponder()
searchBarSearchButtonClicked(searchBar)
@ -41,7 +41,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
// get the domain
var identifier = serviceIdentifiers[0].identifier
if !identifier.hasPrefix("http://") && !identifier.hasPrefix("https://") {
if !identifier.hasPrefix("http://"), !identifier.hasPrefix("https://") {
identifier = "http://" + identifier
}
let url = URL(string: identifier)?.host ?? ""
@ -53,37 +53,38 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
}
/*
Implement this method if your extension support
s showing credentials in the QuickType bar.
When the user selects a credential from your app, this method will be called with the
ASPasswordCredentialIdentity your app has previously saved to the ASCredentialIdentityStore.
Provide the password by completing the extension request with the associated ASPasswordCredential.
If using the credential would require showing custom UI for authenticating the user, cancel
the request with error code ASExtensionError.userInteractionRequired.
Implement this method if your extension support
s showing credentials in the QuickType bar.
When the user selects a credential from your app, this method will be called with the
ASPasswordCredentialIdentity your app has previously saved to the ASCredentialIdentityStore.
Provide the password by completing the extension request with the associated ASPasswordCredential.
If using the credential would require showing custom UI for authenticating the user, cancel
the request with error code ASExtensionError.userInteractionRequired.
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
let databaseIsUnlocked = true
if (databaseIsUnlocked) {
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
} else {
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
}
}
*/
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
let databaseIsUnlocked = true
if (databaseIsUnlocked) {
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
} else {
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
}
}
*/
/*
Implement this method if provideCredentialWithoutUserInteraction(for:) can fail with
ASExtensionError.userInteractionRequired. In this case, the system may present your extension's
UI and call this method. Show appropriate UI for authenticating the user then provide the password
by completing the extension request with the associated ASPasswordCredential.
Implement this method if provideCredentialWithoutUserInteraction(for:) can fail with
ASExtensionError.userInteractionRequired. In this case, the system may present your extension's
UI and call this method. Show appropriate UI for authenticating the user then provide the password
by completing the extension request with the associated ASPasswordCredential.
override func prepareInterfaceToProvideCredential(for credentialIdentity: ASPasswordCredentialIdentity) {
}
*/
override func prepareInterfaceToProvideCredential(for credentialIdentity: ASPasswordCredentialIdentity) {
}
*/
@IBAction func cancel(_ sender: AnyObject?) {
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
@IBAction
func cancel(_: AnyObject?) {
extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
}
override func viewWillAppear(_ animated: Bool) {
@ -107,7 +108,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
private func initPasswordsTableEntries() {
filteredPasswordsTableEntries.removeAll()
let passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false)
let passwordEntities = passwordStore.fetchPasswordEntityCoreData(withDir: false)
passwordsTableEntries = passwordEntities.compactMap {
PasswordTableEntry($0)
}
@ -129,7 +130,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
}
// select row -> extension returns (with username and password)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
let entry = getPasswordEntry(by: indexPath)
guard PGPAgent.shared.isPrepared else {
@ -139,7 +140,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
let passwordEntity = entry.passwordEntity
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
self.decryptPassword(passwordEntity: passwordEntity)
decryptPassword(passwordEntity: passwordEntity)
}
private func decryptPassword(passwordEntity: PasswordEntity, keyID: String? = nil) {
@ -154,7 +155,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
let passwordCredential = ASPasswordCredential(user: username, password: password)
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential)
}
} catch AppError.PgpPrivateKeyNotFound(let key) {
} catch let AppError.PgpPrivateKeyNotFound(key) {
DispatchQueue.main.async {
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
@ -173,34 +174,34 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
func numberOfSectionsInTableView(tableView _: UITableView) -> Int {
1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
if searchActive {
return filteredPasswordsTableEntries.count
}
return passwordsTableEntries.count;
return passwordsTableEntries.count
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = ""
searchActive = false
self.tableView.reloadData()
tableView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if let searchText = searchBar.text, searchText.isEmpty == false {
filteredPasswordsTableEntries = passwordsTableEntries.filter {$0.match(searchText)}
filteredPasswordsTableEntries = passwordsTableEntries.filter { $0.match(searchText) }
searchActive = true
} else {
searchActive = false
}
self.tableView.reloadData()
tableView.reloadData()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
func searchBar(_ searchBar: UISearchBar, textDidChange _: String) {
searchBarSearchButtonClicked(searchBar)
}

View file

@ -6,23 +6,26 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import AuthenticationServices
import Foundation
import passKit
import AuthenticationServices
// cancel means cancel the extension
class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
var originalExtensionContest: ASCredentialProviderExtensionContext?
public convenience init(extensionContext: ASCredentialProviderExtensionContext?) {
self.init()
originalExtensionContest = extensionContext
self.originalExtensionContest = extensionContext
}
override func viewDidLoad() {
super.viewDidLoad()
cancelButton?.removeTarget(nil, action: nil, for: .allEvents)
cancelButton?.addTarget(self, action: #selector(cancelExtension), for: .touchUpInside)
}
@objc func cancelExtension() {
@objc
func cancelExtension() {
originalExtensionContest?.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
}
}
@ -34,7 +37,7 @@ class PasscodeExtensionDisplay {
public init(extensionContext: ASCredentialProviderExtensionContext?) {
self.extensionContext = extensionContext
passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
self.passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
passcodeLockVC.dismissCompletionCallback = { [weak self] in
self?.dismiss()
}
@ -43,14 +46,14 @@ class PasscodeExtensionDisplay {
// present the passcode lock view if passcode is set and the view controller is not presented
public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) {
guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else {
guard PasscodeLock.shared.hasPasscode, !isPasscodePresented == true else {
return
}
isPasscodePresented = true
extensionVC.present(passcodeLockVC, animated: true, completion: nil)
}
public func dismiss(animated: Bool = true) {
public func dismiss(animated _: Bool = true) {
isPasscodePresented = false
}
}

View file

@ -11,8 +11,8 @@ import MobileCoreServices
import passKit
class ExtensionViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UINavigationBarDelegate {
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
@IBOutlet var searchBar: UISearchBar!
@IBOutlet var tableView: UITableView!
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
@ -35,7 +35,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
private func initPasswordsTableEntries() {
filteredPasswordsTableEntries.removeAll()
let passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false)
let passwordEntities = passwordStore.fetchPasswordEntityCoreData(withDir: false)
passwordsTableEntries = passwordEntities.map {
PasswordTableEntry($0)
}
@ -67,11 +67,11 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
for provider in itemProviders {
// search using the extensionContext inputs
if provider.hasItemConformingToTypeIdentifier(OnePasswordExtensionActions.findLogin) {
provider.loadItem(forTypeIdentifier: OnePasswordExtensionActions.findLogin, options: nil, completionHandler: { (item, error) -> Void in
provider.loadItem(forTypeIdentifier: OnePasswordExtensionActions.findLogin, options: nil, completionHandler: { (item, _) -> Void in
let dictionary = item as! NSDictionary
var url: String?
if var urlString = dictionary[OnePasswordExtensionKey.URLStringKey] as? String {
if !urlString.hasPrefix("http://") && !urlString.hasPrefix("https://") {
if !urlString.hasPrefix("http://"), !urlString.hasPrefix("https://") {
urlString = "http://" + urlString
}
url = URL(string: urlString)?.host
@ -84,14 +84,13 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
self?.searchBarSearchButtonClicked((self?.searchBar)!)
}
})
}
else if provider.hasItemConformingToTypeIdentifier(kUTTypePropertyList as String) {
provider.loadItem(forTypeIdentifier: kUTTypePropertyList as String, options: nil, completionHandler: { (item, error) -> Void in
} else if provider.hasItemConformingToTypeIdentifier(kUTTypePropertyList as String) {
provider.loadItem(forTypeIdentifier: kUTTypePropertyList as String, options: nil, completionHandler: { (item, _) -> Void in
var url: String?
if let dictionary = item as? NSDictionary,
let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as? NSDictionary,
var urlString = results[OnePasswordExtensionKey.URLStringKey] as? String {
if !urlString.hasPrefix("http://") && !urlString.hasPrefix("https://") {
if !urlString.hasPrefix("http://"), !urlString.hasPrefix("https://") {
urlString = "http://" + urlString
}
url = URL(string: urlString)?.host
@ -105,7 +104,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
}
})
} else if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil, completionHandler: { (item, error) -> Void in
provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil, completionHandler: { (item, _) -> Void in
let url = (item as? NSURL)!.host
DispatchQueue.main.async { [weak self] in
self?.extensionAction = .fillBrowser
@ -137,7 +136,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
}
// select row -> extension returns (with username and password)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
let entry = getPasswordEntry(by: indexPath)
guard PGPAgent.shared.isPrepared else {
@ -147,7 +146,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
let passwordEntity = entry.passwordEntity
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
self.decryptPassword(passwordEntity: passwordEntity)
decryptPassword(passwordEntity: passwordEntity)
}
private func decryptPassword(passwordEntity: PasswordEntity, keyID: String? = nil) {
@ -158,7 +157,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
let username = decryptedPassword.getUsernameForCompletion()
let password = decryptedPassword.password
DispatchQueue.main.async {// prepare a dictionary to return
DispatchQueue.main.async { // prepare a dictionary to return
switch self.extensionAction {
case .findLogin:
let extensionItem = NSExtensionItem()
@ -173,14 +172,14 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
Utils.copyToPasteboard(textToCopy: decryptedPassword.password)
// return a dictionary for JavaScript for best-effor fill in
let extensionItem = NSExtensionItem()
let returnDictionary = [NSExtensionJavaScriptFinalizeArgumentKey : ["username": username, "password": password]]
let returnDictionary = [NSExtensionJavaScriptFinalizeArgumentKey: ["username": username, "password": password]]
extensionItem.attachments = [NSItemProvider(item: returnDictionary as NSSecureCoding, typeIdentifier: String(kUTTypePropertyList))]
self.extensionContext?.completeRequest(returningItems: [extensionItem], completionHandler: nil)
default:
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
}
}
} catch AppError.PgpPrivateKeyNotFound(let key) {
} catch let AppError.PgpPrivateKeyNotFound(key) {
DispatchQueue.main.async {
// alert: cancel or try again
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.PgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
@ -192,7 +191,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
self.present(alert, animated: true, completion: nil)
}
} catch {
} catch {
DispatchQueue.main.async {
Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self, completion: nil)
}
@ -200,39 +199,39 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
func numberOfSectionsInTableView(tableView _: UITableView) -> Int {
1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchActive{
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
if searchActive {
return filteredPasswordsTableEntries.count
}
return passwordsTableEntries.count;
return passwordsTableEntries.count
}
@IBAction func cancelExtension(_ sender: Any) {
@IBAction
func cancelExtension(_: Any) {
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = ""
searchActive = false
self.tableView.reloadData()
tableView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if let searchText = searchBar.text, searchText.isEmpty == false {
filteredPasswordsTableEntries = passwordsTableEntries.filter {$0.match(searchText)}
filteredPasswordsTableEntries = passwordsTableEntries.filter { $0.match(searchText) }
searchActive = true
} else {
searchActive = false
}
self.tableView.reloadData()
tableView.reloadData()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
func searchBar(_ searchBar: UISearchBar, textDidChange _: String) {
searchBarSearchButtonClicked(searchBar)
}

View file

@ -14,14 +14,17 @@ class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
var originalExtensionContest: NSExtensionContext?
public convenience init(extensionContext: NSExtensionContext?) {
self.init()
originalExtensionContest = extensionContext
self.originalExtensionContest = extensionContext
}
override func viewDidLoad() {
super.viewDidLoad()
cancelButton?.removeTarget(nil, action: nil, for: .allEvents)
cancelButton?.addTarget(self, action: #selector(cancelExtension), for: .touchUpInside)
}
@objc func cancelExtension() {
@objc
func cancelExtension() {
originalExtensionContest?.completeRequest(returningItems: [], completionHandler: nil)
}
}
@ -33,7 +36,7 @@ class PasscodeExtensionDisplay {
public init(extensionContext: NSExtensionContext?) {
self.extensionContext = extensionContext
passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
self.passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
passcodeLockVC.dismissCompletionCallback = { [weak self] in
self?.dismiss()
}
@ -42,14 +45,14 @@ class PasscodeExtensionDisplay {
// present the passcode lock view if passcode is set and the view controller is not presented
public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) {
guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else {
guard PasscodeLock.shared.hasPasscode, !isPasscodePresented == true else {
return
}
isPasscodePresented = true
extensionVC.present(passcodeLockVC, animated: true, completion: nil)
}
public func dismiss(animated: Bool = true) {
public func dismiss(animated _: Bool = true) {
isPasscodePresented = false
}
}

View file

@ -9,10 +9,9 @@
import Crypto
struct GopenPGPInterface: PGPInterface {
private static let errorMapping: [String: Error] = [
"gopenpgp: error in unlocking key: openpgp: invalid data: private key checksum failure": AppError.WrongPassphrase,
"openpgp: incorrect key": AppError.KeyExpiredOrIncompatible,
"gopenpgp: error in unlocking key: openpgp: invalid data: private key checksum failure": AppError.WrongPassphrase,
"openpgp: incorrect key": AppError.KeyExpiredOrIncompatible,
]
private var publicKeys: [String: CryptoKey] = [:]
@ -43,26 +42,25 @@ struct GopenPGPInterface: PGPInterface {
}
privateKeys[k.getFingerprint().lowercased()] = k
}
}
func extractKeysFromArmored(str: String) -> [String] {
var keys: [String] = []
var key: String = ""
for line in str.splitByNewline() {
if line.trimmed.uppercased().hasPrefix("-----BEGIN PGP") {
key = ""
key += line
} else if line.trimmed.uppercased().hasPrefix("-----END PGP") {
key += line
keys.append(key)
} else {
key += line
}
var keys: [String] = []
var key: String = ""
for line in str.splitByNewline() {
if line.trimmed.uppercased().hasPrefix("-----BEGIN PGP") {
key = ""
key += line
} else if line.trimmed.uppercased().hasPrefix("-----END PGP") {
key += line
keys.append(key)
} else {
key += line
}
key += "\n"
}
return keys
}
}
return keys
}
func containsPublicKey(with keyID: String) -> Bool {
publicKeys.keys.contains(where: { key in key.hasSuffix(keyID.lowercased()) })
@ -73,7 +71,7 @@ struct GopenPGPInterface: PGPInterface {
}
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data? {
guard let e = privateKeys.first(where: { (key, _) in key.hasSuffix(keyID.lowercased()) }),
guard let e = privateKeys.first(where: { key, _ in key.hasSuffix(keyID.lowercased()) }),
let privateKey = privateKeys[e.key] else {
throw AppError.Decryption
}
@ -97,7 +95,7 @@ struct GopenPGPInterface: PGPInterface {
}
func encrypt(plainData: Data, keyID: String) throws -> Data {
guard let e = publicKeys.first(where: { (key, _) in key.hasSuffix(keyID.lowercased()) }),
guard let e = publicKeys.first(where: { key, _ in key.hasSuffix(keyID.lowercased()) }),
let publicKey = publicKeys[e.key] else {
throw AppError.Encryption
}
@ -124,11 +122,11 @@ struct GopenPGPInterface: PGPInterface {
}
var keyID: [String] {
return publicKeys.keys.map({ $0.uppercased() })
publicKeys.keys.map { $0.uppercased() }
}
var shortKeyID: [String] {
return publicKeys.keys.map({ $0.suffix(8).uppercased()})
publicKeys.keys.map { $0.suffix(8).uppercased() }
}
private func createPgpMessage(from encryptedData: Data) -> CryptoPGPMessage? {

View file

@ -9,7 +9,6 @@
import ObjectivePGP
struct ObjectivePGPInterface: PGPInterface {
private let publicKey: Key
private let privateKey: Key
@ -30,11 +29,11 @@ struct ObjectivePGPInterface: PGPInterface {
self.privateKey = privateKey
}
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data? {
return try ObjectivePGP.decrypt(encryptedData, andVerifySignature: false, using: keyring.keys) { _ in passphrase }
func decrypt(encryptedData: Data, keyID _: String, passphrase: String) throws -> Data? {
try ObjectivePGP.decrypt(encryptedData, andVerifySignature: false, using: keyring.keys) { _ in passphrase }
}
func encrypt(plainData: Data, keyID: String) throws -> Data {
func encrypt(plainData: Data, keyID _: String) throws -> Data {
let encryptedData = try ObjectivePGP.encrypt(plainData, addSignature: false, using: keyring.keys, passphraseForKey: nil)
if Defaults.encryptInArmored {
return Armor.armored(encryptedData, as: .message).data(using: .ascii)!
@ -51,10 +50,10 @@ struct ObjectivePGPInterface: PGPInterface {
}
var keyID: [String] {
return keyring.keys.map({ $0.keyID.longIdentifier })
keyring.keys.map(\.keyID.longIdentifier)
}
var shortKeyID: [String] {
return keyring.keys.map({ $0.keyID.shortIdentifier })
keyring.keys.map(\.keyID.shortIdentifier)
}
}

View file

@ -7,7 +7,6 @@
//
public class PGPAgent {
public static let shared: PGPAgent = PGPAgent()
private let keyStore: KeyStore
@ -20,7 +19,7 @@ public class PGPAgent {
public func initKeys() throws {
guard let publicKey: String = keyStore.get(for: PgpKey.PUBLIC.getKeychainKey()),
let privateKey: String = keyStore.get(for: PgpKey.PRIVATE.getKeychainKey()) else {
let privateKey: String = keyStore.get(for: PgpKey.PRIVATE.getKeychainKey()) else {
pgpInterface = nil
throw AppError.KeyImport
}
@ -52,7 +51,7 @@ public class PGPAgent {
throw AppError.Decryption
}
var keyID = keyID;
var keyID = keyID
if !pgpInterface.containsPrivateKey(with: keyID) {
if pgpInterface.keyID.count == 1 {
keyID = pgpInterface.keyID.first!
@ -62,8 +61,8 @@ public class PGPAgent {
}
// Remember the previous status and set the current status
let previousDecryptStatus = self.latestDecryptStatus
self.latestDecryptStatus = false
let previousDecryptStatus = latestDecryptStatus
latestDecryptStatus = false
// Get the PGP key passphrase.
var passphrase = ""
@ -77,7 +76,7 @@ public class PGPAgent {
return nil
}
// The decryption step has succeed.
self.latestDecryptStatus = true
latestDecryptStatus = true
return result
}
@ -98,7 +97,7 @@ public class PGPAgent {
}
public var isPrepared: Bool {
return keyStore.contains(key: PgpKey.PUBLIC.getKeychainKey())
keyStore.contains(key: PgpKey.PUBLIC.getKeychainKey())
&& keyStore.contains(key: PgpKey.PRIVATE.getKeychainKey())
}

View file

@ -7,7 +7,6 @@
//
protocol PGPInterface {
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data?
func encrypt(plainData: Data, keyID: String) throws -> Data

View file

@ -11,9 +11,8 @@
import UIKit
open class PasscodeLockPresenter {
fileprivate var mainWindow: UIWindow?
fileprivate var passcodeLockWindow: UIWindow?
private var mainWindow: UIWindow?
private var passcodeLockWindow: UIWindow?
public init(mainWindow window: UIWindow?) {
self.mainWindow = window
@ -27,7 +26,7 @@ open class PasscodeLockPresenter {
// new window
mainWindow?.endEditing(true)
passcodeLockWindow = UIWindow(frame: self.mainWindow!.frame)
passcodeLockWindow = UIWindow(frame: mainWindow!.frame)
moveWindowsToFront(windowLevel: windowLevel)
passcodeLockWindow?.isHidden = false
@ -46,9 +45,9 @@ open class PasscodeLockPresenter {
passcodeLockWindow?.rootViewController = nil
}
fileprivate func moveWindowsToFront(windowLevel: CGFloat?) {
private func moveWindowsToFront(windowLevel: CGFloat?) {
let windowLevel = windowLevel ?? UIWindow.Level.normal.rawValue
let maxWinLevel = max(windowLevel, UIWindow.Level.normal.rawValue)
passcodeLockWindow?.windowLevel = UIWindow.Level(rawValue: maxWinLevel + 1)
passcodeLockWindow?.windowLevel = UIWindow.Level(rawValue: maxWinLevel + 1)
}
}

View file

@ -8,14 +8,13 @@
// Inspired by SwiftPasscodeLock created by Yanko Dimitrov.
//
import UIKit
import LocalAuthentication
import UIKit
open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
open var dismissCompletionCallback: (()->Void)?
open var successCallback: (()->Void)?
open var cancelCallback: (()->Void)?
open var dismissCompletionCallback: (() -> Void)?
open var successCallback: (() -> Void)?
open var cancelCallback: (() -> Void)?
weak var passcodeTextField: UITextField?
weak var biometryAuthButton: UIButton?
@ -26,18 +25,18 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
private let passwordStore = PasswordStore.shared
open override func loadView() {
override open func loadView() {
super.loadView()
let passcodeTextField = UITextField()
let passcodeTextField = UITextField()
passcodeTextField.borderStyle = UITextField.BorderStyle.roundedRect
passcodeTextField.placeholder = "EnterPasscode".localize()
passcodeTextField.isSecureTextEntry = true
passcodeTextField.clearButtonMode = UITextField.ViewMode.whileEditing
passcodeTextField.delegate = self
passcodeTextField.addTarget(self, action: #selector(self.passcodeTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
passcodeTextField.addTarget(self, action: #selector(passcodeTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
passcodeTextField.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(passcodeTextField)
view.addSubview(passcodeTextField)
self.passcodeTextField = passcodeTextField
view.backgroundColor = Colors.systemBackground
@ -50,7 +49,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
biometryAuthButton.addTarget(self, action: #selector(bioButtonPressedAction(_:)), for: .touchUpInside)
biometryAuthButton.isHidden = true
biometryAuthButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(biometryAuthButton)
view.addSubview(biometryAuthButton)
self.biometryAuthButton = biometryAuthButton
let myContext = LAContext()
@ -71,19 +70,19 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
forgotPasscodeButton.setTitleColor(Colors.systemBlue, for: .normal)
forgotPasscodeButton.addTarget(self, action: #selector(forgotPasscodeButtonPressedAction(_:)), for: .touchUpInside)
// hide the forgotPasscodeButton if the native app is running
forgotPasscodeButton.isHidden = self.isCancellable
forgotPasscodeButton.isHidden = isCancellable
forgotPasscodeButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(forgotPasscodeButton)
view.addSubview(forgotPasscodeButton)
self.forgotPasscodeButton = forgotPasscodeButton
let cancelButton = UIButton(type: .custom)
cancelButton.setTitle("Cancel".localize(), for: .normal)
cancelButton.setTitleColor(Colors.systemBlue, for: .normal)
cancelButton.addTarget(self, action: #selector(passcodeLockDidCancel), for: .touchUpInside)
cancelButton.isHidden = !self.isCancellable
cancelButton.isHidden = !isCancellable
cancelButton.translatesAutoresizingMaskIntoConstraints = false
cancelButton.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
self.view.addSubview(cancelButton)
view.addSubview(cancelButton)
self.cancelButton = cancelButton
// Display the Pass icon in the middle of the screen
@ -94,49 +93,48 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
appIconView.translatesAutoresizingMaskIntoConstraints = false
appIconView.layer.cornerRadius = appIconSize / 5
appIconView.layer.masksToBounds = true
self.view?.addSubview(appIconView)
view?.addSubview(appIconView)
NSLayoutConstraint.activate([
passcodeTextField.widthAnchor.constraint(equalToConstant: 250),
passcodeTextField.heightAnchor.constraint(equalToConstant: 40),
passcodeTextField.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
passcodeTextField.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -20),
passcodeTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
passcodeTextField.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -20),
// above passocde
appIconView.widthAnchor.constraint(equalToConstant: appIconSize),
appIconView.heightAnchor.constraint(equalToConstant: appIconSize),
appIconView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
appIconView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
appIconView.bottomAnchor.constraint(equalTo: passcodeTextField.topAnchor, constant: -appIconSize),
// below passcode
biometryAuthButton.widthAnchor.constraint(equalToConstant: 250),
biometryAuthButton.heightAnchor.constraint(equalToConstant: 40),
biometryAuthButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
biometryAuthButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
biometryAuthButton.topAnchor.constraint(equalTo: passcodeTextField.bottomAnchor),
// cancel (top-left of the screen)
cancelButton.widthAnchor.constraint(equalToConstant: 150),
cancelButton.heightAnchor.constraint(equalToConstant: 40),
cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor),
cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20),
cancelButton.topAnchor.constraint(equalTo: view.safeTopAnchor),
cancelButton.leftAnchor.constraint(equalTo: view.safeLeftAnchor, constant: 20),
// bottom of the screen
forgotPasscodeButton.widthAnchor.constraint(equalToConstant: 250),
forgotPasscodeButton.heightAnchor.constraint(equalToConstant: 40),
forgotPasscodeButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
forgotPasscodeButton.bottomAnchor.constraint(equalTo: self.view.safeBottomAnchor, constant: -40)
forgotPasscodeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
forgotPasscodeButton.bottomAnchor.constraint(equalTo: view.safeBottomAnchor, constant: -40),
])
// dismiss keyboard when tapping anywhere
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing))
let tap = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing))
view.addGestureRecognizer(tap)
}
open override func viewDidLoad() {
override open func viewDidLoad() {
super.viewDidLoad()
}
open override func viewDidAppear(_ animated: Bool) {
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let biometryAuthButton = biometryAuthButton {
self.bioButtonPressedAction(biometryAuthButton)
bioButtonPressedAction(biometryAuthButton)
}
}
@ -167,16 +165,18 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
dismissPasscodeLock(completionHandler: successCallback)
}
@objc func passcodeLockDidCancel() {
@objc
func passcodeLockDidCancel() {
dismissPasscodeLock(completionHandler: cancelCallback)
}
@objc func bioButtonPressedAction(_ uiButton: UIButton) {
@objc
func bioButtonPressedAction(_: UIButton) {
let myContext = LAContext()
let myLocalizedReasonString = "AuthenticationNeeded.".localize()
var authError: NSError?
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, _ in
if success {
DispatchQueue.main.async {
// user authenticated successfully, take appropriate action
@ -187,9 +187,10 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
}
}
@objc func forgotPasscodeButtonPressedAction(_ uiButton: UIButton) {
@objc
func forgotPasscodeButtonPressedAction(_: UIButton) {
let alert = UIAlertController(title: "ResetPass".localize(), message: "ResetPassExplanation.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in
alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive, handler: { [unowned self] (_) -> Void in
let myContext = LAContext()
var error: NSError?
// If the device passcode is not set, reset the app.
@ -199,7 +200,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
return
}
// If the device passcode is set, authentication is required.
myContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "ErasePasswordStoreData".localize()) { (success, error) in
myContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "ErasePasswordStoreData".localize()) { success, error in
if success {
DispatchQueue.main.async {
// User authenticated successfully, take appropriate action
@ -214,25 +215,26 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
}
}))
alert.addAction(UIAlertAction.dismiss())
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
}
public override func textFieldShouldReturn(_ textField: UITextField) -> Bool {
override public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == passcodeTextField {
if !PasscodeLock.shared.check(passcode: textField.text ?? "") {
self.passcodeTextField?.placeholder =
passcodeTextField?.placeholder =
"TryAgain".localize()
self.passcodeTextField?.text = ""
self.passcodeTextField?.shake()
passcodeTextField?.text = ""
passcodeTextField?.shake()
}
}
textField.resignFirstResponder()
return true
}
@objc func passcodeTextFieldDidChange(_ textField: UITextField) {
@objc
func passcodeTextFieldDidChange(_ textField: UITextField) {
if PasscodeLock.shared.check(passcode: textField.text ?? "") {
self.passcodeLockDidSucceed()
passcodeLockDidSucceed()
}
}

View file

@ -7,7 +7,6 @@
//
extension Array {
func slices(count: UInt) -> [ArraySlice<Element>] {
guard count != 0 else {
return []

View file

@ -8,18 +8,18 @@
extension String {
public func localize() -> String {
return NSLocalizedString(self, value: "#\(self)#", comment: "")
NSLocalizedString(self, value: "#\(self)#", comment: "")
}
public func localize(_ firstValue: CVarArg) -> String {
return String(format: localize(), firstValue)
String(format: localize(), firstValue)
}
public func localize(_ firstValue: CVarArg, _ secondValue: CVarArg) -> String {
return String(format: localize(), firstValue, secondValue)
String(format: localize(), firstValue, secondValue)
}
public func localize(_ error: Error) -> String {
return localize(error.localizedDescription)
localize(error.localizedDescription)
}
}

View file

@ -8,7 +8,7 @@
extension String {
public var trimmed: String {
return trimmingCharacters(in: .whitespacesAndNewlines)
trimmingCharacters(in: .whitespacesAndNewlines)
}
public func stringByAddingPercentEncodingForRFC3986() -> String? {
@ -19,12 +19,12 @@ extension String {
}
public func splitByNewline() -> [String] {
return split(omittingEmptySubsequences: false) { $0 == "\n" || $0 == "\r\n" }.map(String.init)
split(omittingEmptySubsequences: false) { $0 == "\n" || $0 == "\r\n" }.map(String.init)
}
}
extension String {
public static func | (left: String, right: String) -> String {
return right.isEmpty ? left : left + "\n" + right
right.isEmpty ? left : left + "\n" + right
}
}

View file

@ -6,12 +6,12 @@
// Copyright © 2020 Bob Sun. All rights reserved.
//
import UIKit
import Foundation
import UIKit
extension UIAlertAction {
public static func cancelAndPopView(controller: UIViewController) -> UIAlertAction {
return cancel() { _ in
cancel { _ in
controller.navigationController?.popViewController(animated: true)
}
}
@ -24,7 +24,7 @@ extension UIAlertAction {
cancel(with: "Dismiss", handler: handler)
}
public static func cancel(with title: String, handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction {
public static func cancel(with _: String, handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction {
UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: handler)
}
@ -33,7 +33,7 @@ extension UIAlertAction {
}
public static func okAndPopView(controller: UIViewController) -> UIAlertAction {
return ok() { _ in
ok { _ in
controller.navigationController?.popViewController(animated: true)
}
}
@ -41,13 +41,12 @@ extension UIAlertAction {
public static func selectKey(controller: UIViewController, handler: ((UIAlertAction) -> Void)?) -> UIAlertAction {
UIAlertAction(title: "Select Key", style: .default) { _ in
let selectKeyAlert = UIAlertController(title: "Select from imported keys", message: nil, preferredStyle: .actionSheet)
try? PGPAgent.shared.getShortKeyID().forEach({ k in
try? PGPAgent.shared.getShortKeyID().forEach { k in
let action = UIAlertAction(title: k, style: .default, handler: handler)
selectKeyAlert.addAction(action)
})
}
selectKeyAlert.addAction(UIAlertAction.cancelAndPopView(controller: controller))
controller.present(selectKeyAlert, animated: true, completion: nil)
}
}
}

View file

@ -14,7 +14,7 @@ private var kAssociationKeyNextField: UInt8 = 0
extension UITextField {
@IBOutlet var nextField: UITextField? {
get {
return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
}
set(newField) {
objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN)
@ -25,7 +25,7 @@ extension UITextField {
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
animation.repeatCount = 3
animation.duration = 0.2/TimeInterval(animation.repeatCount)
animation.duration = 0.2 / TimeInterval(animation.repeatCount)
animation.autoreverses = true
animation.values = [3, -3]
layer.add(animation, forKey: "shake")

View file

@ -7,7 +7,8 @@
//
extension UIViewController {
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@objc
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.nextField != nil {
textField.nextField?.becomeFirstResponder()
} else {

View file

@ -9,29 +9,28 @@
import Foundation
extension UIView {
// Save anchors: https://stackoverflow.com/questions/46317061/use-safe-area-layout-programmatically
var safeTopAnchor: NSLayoutYAxisAnchor {
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.topAnchor
} else {
return self.topAnchor
return topAnchor
}
}
var safeLeftAnchor: NSLayoutXAxisAnchor {
if #available(iOS 11.0, *){
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.leftAnchor
} else {
return self.leftAnchor
return leftAnchor
}
}
var safeRightAnchor: NSLayoutXAxisAnchor {
if #available(iOS 11.0, *){
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.rightAnchor
} else {
return self.rightAnchor
return rightAnchor
}
}
@ -39,7 +38,7 @@ extension UIView {
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.bottomAnchor
} else {
return self.bottomAnchor
return bottomAnchor
}
}
}

View file

@ -9,7 +9,6 @@
import KeychainAccess
public class AppKeychain: KeyStore {
public static let shared = AppKeychain()
private let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
@ -25,15 +24,15 @@ public class AppKeychain: KeyStore {
}
public func contains(key: String) -> Bool {
return (try? keychain.contains(key)) ?? false
(try? keychain.contains(key)) ?? false
}
public func get(for key: String) -> Data? {
return try? keychain.getData(key)
try? keychain.getData(key)
}
public func get(for key: String) -> String? {
return try? keychain.getString(key)
try? keychain.getString(key)
}
public func removeContent(for key: String) {

View file

@ -10,7 +10,6 @@ import Foundation
// https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516
public extension FileManager {
/// This method calculates the accumulated size of a directory on the volume in bytes.
///
/// As there's no simple way to get this information from the file system it has to crawl the entire hierarchy,
@ -19,8 +18,7 @@ public extension FileManager {
///
/// - note: There are a couple of oddities that are not taken into account (like symbolic links, meta data of
/// directories, hard links, ...).
func allocatedSizeOfDirectoryAtURL(directoryURL : URL) throws -> UInt64 {
func allocatedSizeOfDirectoryAtURL(directoryURL: URL) throws -> UInt64 {
// We'll sum up content size here:
var accumulatedSize = UInt64(0)
@ -29,7 +27,7 @@ public extension FileManager {
URLResourceKey.isRegularFileKey,
URLResourceKey.fileAllocatedSizeKey,
URLResourceKey.totalFileAllocatedSizeKey,
]
]
// The error handler simply signals errors to outside code.
var errorDidOccur: Error?
@ -38,7 +36,6 @@ public extension FileManager {
return false
}
// We have to enumerate all directory contents, including subdirectories.
let enumerator = self.enumerator(at: directoryURL,
includingPropertiesForKeys: prefetchedProperties,
@ -91,4 +88,3 @@ public extension FileManager {
return accumulatedSize
}
}

View file

@ -43,14 +43,15 @@ public final class Globals {
// UI related
public static let tableCellButtonSize = CGFloat(20.0)
private init() { }
private init() {}
}
public extension Bundle {
var releaseVersionNumber: String? {
return infoDictionary?["CFBundleShortVersionString"] as? String
infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildVersionNumber: String? {
return infoDictionary?["CFBundleVersion"] as? String
infoDictionary?["CFBundleVersion"] as? String
}
}

View file

@ -45,6 +45,6 @@ public class KeyFileManager {
}
public func doesKeyFileExist() -> Bool {
return FileManager.default.fileExists(atPath: keyPath)
FileManager.default.fileExists(atPath: keyPath)
}
}

View file

@ -7,7 +7,6 @@
//
public class Utils {
public static func copyToPasteboard(textToCopy: String?) {
guard textToCopy != nil else {
return
@ -15,8 +14,8 @@ public class Utils {
UIPasteboard.general.string = textToCopy
}
public static func attributedPassword(plainPassword: String) -> NSAttributedString{
let attributedPassword = NSMutableAttributedString.init(string: plainPassword)
public static func attributedPassword(plainPassword: String) -> NSAttributedString {
let attributedPassword = NSMutableAttributedString(string: plainPassword)
// draw all digits in the password into red
// draw all punctuation characters in the password into blue
for (index, element) in plainPassword.unicodeScalars.enumerated() {
@ -40,24 +39,24 @@ public class Utils {
}
public static func createRequestPGPKeyPassphraseHandler(controller: UIViewController) -> (String) -> String {
return { keyID in
{ keyID in
let sem = DispatchSemaphore(value: 0)
var passphrase = ""
DispatchQueue.main.async {
let title = "Passphrase".localize() + " (\(keyID.suffix(8)))"
let message = "FillInPgpPassphrase.".localize()
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction.ok() { _ in
alert.addAction(UIAlertAction.ok { _ in
passphrase = alert.textFields?.first?.text ?? ""
sem.signal()
})
alert.addTextField() { textField in
alert.addTextField { textField in
textField.text = AppKeychain.shared.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? ""
textField.isSecureTextEntry = true
}
controller.present(alert, animated: true)
}
let _ = sem.wait(timeout: DispatchTime.distantFuture)
_ = sem.wait(timeout: DispatchTime.distantFuture)
if Defaults.isRememberPGPPassphraseOn {
AppKeychain.shared.add(string: passphrase, for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID))
}
@ -65,4 +64,3 @@ public class Utils {
}
}
}

View file

@ -25,7 +25,7 @@ public struct GitCredential {
public func credentialProvider(requestCredentialPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider {
var attempts = 0
return GTCredentialProvider { (_, _, _) -> (GTCredential?) in
var credential: GTCredential? = nil
var credential: GTCredential?
switch self.credential {
case let .http(userName):
@ -52,7 +52,7 @@ public struct GitCredential {
return nil
}
var lastPassword = self.passwordStore.gitSSHPrivateKeyPassphrase
if lastPassword == nil || attempts != 0 {
if lastPassword == nil || attempts != 0 {
if let requestedPassword = requestCredentialPassword(self.credential, lastPassword) {
if Defaults.isRememberGitCredentialPassphraseOn {
self.passwordStore.gitSSHPrivateKeyPassphrase = requestedPassword
@ -72,10 +72,9 @@ public struct GitCredential {
public func delete() {
switch credential {
case .http:
self.passwordStore.gitPassword = nil
passwordStore.gitPassword = nil
case .ssh:
self.passwordStore.gitSSHPrivateKeyPassphrase = nil
passwordStore.gitSSHPrivateKeyPassphrase = nil
}
}
}

View file

@ -23,7 +23,7 @@ public class PasscodeLock {
}
public var hasPasscode: Bool {
return passcode != nil
passcode != nil
}
public func save(passcode: String) {
@ -32,7 +32,7 @@ public class PasscodeLock {
}
public func check(passcode: String) -> Bool {
return self.passcode == passcode
self.passcode == passcode
}
public func delete() {

View file

@ -6,11 +6,10 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import OneTimePassword
import Base32
import OneTimePassword
public class Password {
public var name: String
public var url: URL
public var plainText: String
@ -29,47 +28,47 @@ public class Password {
}
public var namePath: String {
return url.deletingPathExtension().path
url.deletingPathExtension().path
}
public var nameFromPath: String? {
return url.deletingPathExtension().path.split(separator: "/").last.map { String($0) }
url.deletingPathExtension().path.split(separator: "/").last.map { String($0) }
}
public var password: String {
return parser.firstLine
parser.firstLine
}
public var plainData: Data {
return plainText.data(using: .utf8)!
plainText.data(using: .utf8)!
}
public var additionsPlainText: String {
return parser.additionsSection
parser.additionsSection
}
public var username: String? {
return getAdditionValue(withKey: Constants.USERNAME_KEYWORD)
getAdditionValue(withKey: Constants.USERNAME_KEYWORD)
}
public var login: String? {
return getAdditionValue(withKey: Constants.LOGIN_KEYWORD)
getAdditionValue(withKey: Constants.LOGIN_KEYWORD)
}
public var urlString: String? {
return getAdditionValue(withKey: Constants.URL_KEYWORD)
getAdditionValue(withKey: Constants.URL_KEYWORD)
}
public var currentOtp: String? {
return otpToken?.currentPassword
otpToken?.currentPassword
}
public var numberOfUnknowns: Int {
return additions.map { $0.title }.filter(Constants.isUnknown).count
additions.map(\.title).filter(Constants.isUnknown).count
}
public var numberOfOtpRelated: Int {
return additions.map { $0.title }.filter(Constants.isOtpKeyword).count - (firstLineIsOTPField ? 1 : 0)
additions.map(\.title).filter(Constants.isOtpKeyword).count - (firstLineIsOTPField ? 1 : 0)
}
public init(name: String, url: URL, plainText: String) {
@ -119,7 +118,7 @@ public class Password {
}
public func getFilteredAdditions() -> [AdditionField] {
return additions.filter { field in
additions.filter { field in
let title = field.title.lowercased()
return title != Constants.USERNAME_KEYWORD
&& title != Constants.LOGIN_KEYWORD
@ -194,12 +193,12 @@ public class Password {
newOtpauth?.append("&secret=")
newOtpauth?.append(MF_Base32Codec.base32String(from: otpToken?.generator.secret))
var lines : [String] = []
self.plainText.enumerateLines() { line, _ in
var lines: [String] = []
plainText.enumerateLines { line, _ in
let (key, _) = Parser.getKeyValuePair(from: line)
if !Constants.OTP_KEYWORDS.contains(key ?? "") {
lines.append(line)
} else if key == Constants.OTPAUTH && newOtpauth != nil {
} else if key == Constants.OTPAUTH, newOtpauth != nil {
lines.append(newOtpauth!)
// set to nil to prevent duplication
newOtpauth = nil
@ -208,10 +207,10 @@ public class Password {
if newOtpauth != nil {
lines.append(newOtpauth!)
}
self.updatePassword(name: self.name, url: self.url, plainText: lines.joined(separator: "\n"))
updatePassword(name: name, url: url, plainText: lines.joined(separator: "\n"))
// get and return the password
return self.otpToken?.currentPassword
return otpToken?.currentPassword
}
public func getUsernameForCompletion() -> String {

View file

@ -10,7 +10,6 @@ import Foundation
import SwiftyUserDefaults
extension PasswordEntity {
public var nameWithCategory: String {
if let p = path, p.hasSuffix(".gpg") {
return String(p.prefix(upTo: p.index(p.endIndex, offsetBy: -4)))
@ -19,7 +18,7 @@ extension PasswordEntity {
}
public func getCategoryText() -> String {
return getCategoryArray().joined(separator: " > ")
getCategoryArray().joined(separator: " > ")
}
public func getCategoryArray() -> [String] {
@ -44,17 +43,16 @@ extension PasswordEntity {
// manually write models instead auto generation.
public func getImage() -> Data? {
return image
image
}
public func getName() -> String {
// unwrap non-optional core data
return name ?? ""
name ?? ""
}
public func getPath() -> String {
// unwrap non-optional core data
return path ?? ""
path ?? ""
}
}

View file

@ -6,12 +6,12 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import Foundation
import CoreData
import UIKit
import SwiftyUserDefaults
import ObjectiveGit
import Foundation
import KeychainAccess
import ObjectiveGit
import SwiftyUserDefaults
import UIKit
public class PasswordStore {
public static let shared = PasswordStore()
@ -21,21 +21,18 @@ public class PasswordStore {
dateFormatter.timeStyle = .short
return dateFormatter
}()
public var storeURL: URL
public var tempStoreURL: URL {
get {
URL(fileURLWithPath: "\(storeURL.path)-temp")
}
URL(fileURLWithPath: "\(storeURL.path)-temp")
}
public var storeRepository: GTRepository?
public var gitSignatureForNow: GTSignature? {
get {
let gitSignatureName = Defaults.gitSignatureName ?? Globals.gitSignatureDefaultName
let gitSignatureEmail = Defaults.gitSignatureEmail ?? Globals.gitSignatureDefaultEmail
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())
}
let gitSignatureName = Defaults.gitSignatureName ?? Globals.gitSignatureDefaultName
let gitSignatureEmail = Defaults.gitSignatureEmail ?? Globals.gitSignatureDefaultEmail
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())
}
public var gitPassword: String? {
@ -43,7 +40,7 @@ public class PasswordStore {
AppKeychain.shared.add(string: newValue, for: Globals.gitPassword)
}
get {
return AppKeychain.shared.get(for: Globals.gitPassword)
AppKeychain.shared.get(for: Globals.gitPassword)
}
}
@ -52,12 +49,12 @@ public class PasswordStore {
AppKeychain.shared.add(string: newValue, for: Globals.gitSSHPrivateKeyPassphrase)
}
get {
return AppKeychain.shared.get(for: Globals.gitSSHPrivateKeyPassphrase)
AppKeychain.shared.get(for: Globals.gitSSHPrivateKeyPassphrase)
}
}
private let fm = FileManager.default
lazy private var context: NSManagedObjectContext = {
private lazy var context: NSManagedObjectContext = {
let modelURL = Bundle(identifier: Globals.passKitBundleIdentifier)!.url(forResource: "pass", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
let container = NSPersistentContainer(name: "pass", managedObjectModel: managedObjectModel!)
@ -65,7 +62,7 @@ public class PasswordStore {
try! FileManager.default.createDirectory(atPath: Globals.documentPath, withIntermediateDirectories: true, attributes: nil)
}
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: URL(fileURLWithPath: Globals.dbPath))]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.loadPersistentStores(completionHandler: { _, error in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
@ -84,35 +81,35 @@ public class PasswordStore {
return container.viewContext
}()
public var numberOfPasswords : Int {
return self.fetchPasswordEntityCoreData(withDir: false).count
public var numberOfPasswords: Int {
fetchPasswordEntityCoreData(withDir: false).count
}
public var sizeOfRepositoryByteCount : UInt64 {
return (try? fm.allocatedSizeOfDirectoryAtURL(directoryURL: self.storeURL)) ?? 0
public var sizeOfRepositoryByteCount: UInt64 {
(try? fm.allocatedSizeOfDirectoryAtURL(directoryURL: storeURL)) ?? 0
}
public var numberOfLocalCommits: Int {
return (try? getLocalCommits())?.flatMap { $0.count } ?? 0
(try? getLocalCommits()).map(\.count) ?? 0
}
public var lastSyncedTime: Date? {
return Defaults.lastSyncedTime
Defaults.lastSyncedTime
}
public var numberOfCommits: UInt? {
return storeRepository?.numberOfCommits(inCurrentBranch: nil)
storeRepository?.numberOfCommits(inCurrentBranch: nil)
}
init(url: URL = URL(fileURLWithPath: "\(Globals.repositoryPath)")) {
storeURL = url
self.storeURL = url
// Migration
importExistingKeysIntoKeychain()
do {
if fm.fileExists(atPath: storeURL.path) {
try storeRepository = GTRepository.init(url: storeURL)
try self.storeRepository = GTRepository(url: storeURL)
}
} catch {
print(error)
@ -183,23 +180,23 @@ public class PasswordStore {
do {
let credentialProvider = try credential.credentialProvider(requestCredentialPassword: requestCredentialPassword)
let options = [GTRepositoryCloneOptionsCredentialProvider: credentialProvider]
try self.cloneRepository(remoteRepoURL: remoteRepoURL, options: options, branchName: branchName, transferProgressBlock: transferProgressBlock, checkoutProgressBlock: checkoutProgressBlock)
try cloneRepository(remoteRepoURL: remoteRepoURL, options: options, branchName: branchName, transferProgressBlock: transferProgressBlock, checkoutProgressBlock: checkoutProgressBlock)
} catch {
credential.delete()
throw(error)
throw (error)
}
}
public func cloneRepository(remoteRepoURL: URL,
options: [AnyHashable : Any]? = nil,
options: [AnyHashable: Any]? = nil,
branchName: String,
transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void,
checkoutProgressBlock: @escaping (String, UInt, UInt) -> Void,
completion: @escaping () -> Void = {}) throws {
try? fm.removeItem(at: storeURL)
try? fm.removeItem(at: tempStoreURL)
self.gitPassword = nil
self.gitSSHPrivateKeyPassphrase = nil
gitPassword = nil
gitSSHPrivateKeyPassphrase = nil
do {
storeRepository = try GTRepository.clone(from: remoteRepoURL,
toWorkingDirectory: tempStoreURL,
@ -216,7 +213,7 @@ public class PasswordStore {
self.deleteCoreData(entityName: "PasswordEntity")
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
throw(error)
throw (error)
}
Defaults.lastSyncedTime = Date()
DispatchQueue.main.async {
@ -253,7 +250,7 @@ public class PasswordStore {
let remote = try GTRemote(name: "origin", in: storeRepository)
try storeRepository.pull(storeRepository.currentBranch(), from: remote, withOptions: options, progress: progressBlock)
Defaults.lastSyncedTime = Date()
self.setAllSynced()
setAllSynced()
DispatchQueue.main.async {
self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
@ -263,20 +260,20 @@ public class PasswordStore {
private func updatePasswordEntityCoreData() {
deleteCoreData(entityName: "PasswordEntity")
do {
var q = try fm.contentsOfDirectory(atPath: self.storeURL.path).filter {
var q = try fm.contentsOfDirectory(atPath: 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 = String(filename.prefix(upTo: filename.index(filename.endIndex, offsetBy: -4)))
} else {
passwordEntity.name = filename
}
passwordEntity.path = filename
passwordEntity.parent = nil
return passwordEntity
}.map { (filename) -> PasswordEntity in
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
if filename.hasSuffix(".gpg") {
passwordEntity.name = String(filename.prefix(upTo: filename.index(filename.endIndex, offsetBy: -4)))
} else {
passwordEntity.name = filename
}
passwordEntity.path = filename
passwordEntity.parent = nil
return passwordEntity
}
while q.count > 0 {
while !q.isEmpty {
let e = q.first!
q.remove(at: 0)
guard !e.name!.hasPrefix(".") else {
@ -309,7 +306,7 @@ public class PasswordStore {
} catch {
print(error)
}
self.saveUpdatedContext()
saveUpdatedContext()
}
public func getRecentCommits(count: Int) throws -> [GTCommit] {
@ -353,7 +350,6 @@ public class PasswordStore {
}
}
public func fetchUnsyncedPasswords() -> [PasswordEntity] {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
@ -367,11 +363,11 @@ public class PasswordStore {
public func setAllSynced() {
let passwordEntities = fetchUnsyncedPasswords()
if passwordEntities.count > 0 {
if !passwordEntities.isEmpty {
for passwordEntity in passwordEntities {
passwordEntity.synced = true
}
self.saveUpdatedContext()
saveUpdatedContext()
}
}
@ -383,7 +379,7 @@ public class PasswordStore {
let latestCommitTime = blameHunks.map({
$0.finalSignature?.time?.timeIntervalSince1970 ?? 0
}).max() else {
return "Unknown".localize()
return "Unknown".localize()
}
let lastCommitDate = Date(timeIntervalSince1970: latestCommitTime)
if Date().timeIntervalSince(lastCommitDate) <= 60 {
@ -392,8 +388,7 @@ public class PasswordStore {
return PasswordStore.dateFormatter.string(from: lastCommitDate)
}
public func updateRemoteRepo() {
}
public func updateRemoteRepo() {}
private func gitAdd(path: String) throws {
guard let storeRepository = storeRepository else {
@ -490,20 +485,20 @@ public class PasswordStore {
paths.append(passwordURL.path)
passwordURL = passwordURL.deletingLastPathComponent()
// better identify errors before saving a new password
if passwordURL.path != "." && passwordURL.path.count >= previousPathLength {
if passwordURL.path != ".", passwordURL.path.count >= previousPathLength {
throw AppError.WrongPasswordFilename
}
previousPathLength = passwordURL.path.count
}
paths.reverse()
var parentPasswordEntity: PasswordEntity? = nil
var parentPasswordEntity: PasswordEntity?
for path in paths {
let isDir = !path.hasSuffix(".gpg")
if let passwordEntity = getPasswordEntity(by: path, isDir: isDir) {
passwordEntity.synced = false
parentPasswordEntity = passwordEntity
} else {
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as! PasswordEntity
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
let pathURL = URL(string: path.stringByAddingPercentEncodingForRFC3986()!)!
if isDir {
passwordEntity.name = pathURL.lastPathComponent
@ -518,16 +513,16 @@ public class PasswordStore {
}
}
self.saveUpdatedContext()
saveUpdatedContext()
return parentPasswordEntity
}
public func add(password: Password, keyID: String? = nil) throws -> PasswordEntity? {
try createDirectoryTree(at: password.url)
let saveURL = storeURL.appendingPathComponent(password.url.path)
try self.encrypt(password: password, keyID: keyID).write(to: saveURL)
try encrypt(password: password, keyID: keyID).write(to: saveURL)
try gitAdd(path: password.url.path)
let _ = try gitCommit(message: "AddPassword.".localize(password.url.deletingPathExtension().path))
_ = try gitCommit(message: "AddPassword.".localize(password.url.deletingPathExtension().path))
let newPasswordEntity = try addPasswordEntities(password: password)
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity
@ -538,7 +533,7 @@ public class PasswordStore {
try gitRm(path: deletedFileURL.path)
try deletePasswordEntities(passwordEntity: passwordEntity)
try deleteDirectoryTree(at: deletedFileURL)
let _ = try gitCommit(message: "RemovePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!))
_ = try gitCommit(message: "RemovePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!))
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
@ -546,17 +541,17 @@ public class PasswordStore {
var newPasswordEntity: PasswordEntity? = passwordEntity
let url = try passwordEntity.getURL()
if password.changed&PasswordChange.content.rawValue != 0 {
if password.changed & PasswordChange.content.rawValue != 0 {
let saveURL = storeURL.appendingPathComponent(url.path)
try self.encrypt(password: password, keyID: keyID).write(to: saveURL)
try encrypt(password: password, keyID: keyID).write(to: saveURL)
try gitAdd(path: url.path)
let _ = try gitCommit(message: "EditPassword.".localize(url.deletingPathExtension().path.removingPercentEncoding!))
_ = try gitCommit(message: "EditPassword.".localize(url.deletingPathExtension().path.removingPercentEncoding!))
newPasswordEntity = passwordEntity
newPasswordEntity?.synced = false
self.saveUpdatedContext()
saveUpdatedContext()
}
if password.changed&PasswordChange.path.rawValue != 0 {
if password.changed & PasswordChange.path.rawValue != 0 {
let deletedFileURL = url
// add
try createDirectoryTree(at: password.url)
@ -568,7 +563,7 @@ public class PasswordStore {
// delete
try deleteDirectoryTree(at: deletedFileURL)
try deletePasswordEntities(passwordEntity: passwordEntity)
let _ = try gitCommit(message: "RenamePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!, password.url.deletingPathExtension().path.removingPercentEncoding!))
_ = try gitCommit(message: "RenamePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!, password.url.deletingPathExtension().path.removingPercentEncoding!))
}
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity
@ -576,12 +571,13 @@ public class PasswordStore {
private func deletePasswordEntities(passwordEntity: PasswordEntity) throws {
var current: PasswordEntity? = passwordEntity
while current != nil && (current!.children!.count == 0 || !current!.isDir) {
// swiftformat:disable:next isEmpty
while current != nil, current!.children!.count == 0 || !current!.isDir {
let parent = current!.parent
self.context.delete(current!)
context.delete(current!)
current = parent
}
self.saveUpdatedContext()
saveUpdatedContext()
}
public func saveUpdatedContext() {
@ -659,26 +655,25 @@ public class PasswordStore {
}
// get a list of local commits
if let localCommits = try getLocalCommits(),
localCommits.count > 0 {
!localCommits.isEmpty {
// get the oldest local commit
guard let firstLocalCommit = localCommits.last,
firstLocalCommit.parents.count == 1,
let newHead = firstLocalCommit.parents.first else {
throw AppError.GitReset
throw AppError.GitReset
}
try storeRepository.reset(to: newHead, resetType: .hard)
self.setAllSynced()
self.updatePasswordEntityCoreData()
setAllSynced()
updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil)
return localCommits.count
} else {
return 0 // no new commit
return 0 // no new commit
}
}
private func getLocalCommits() throws -> [GTCommit]? {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -727,8 +722,8 @@ public class PasswordStore {
public func findGPGID(from url: URL) -> String {
var path = url
while !FileManager.default.fileExists(atPath: path.appendingPathComponent(".gpg-id").path)
&& path.path != "file:///" {
while !FileManager.default.fileExists(atPath: path.appendingPathComponent(".gpg-id").path),
path.path != "file:///" {
path = path.deletingLastPathComponent()
}
path = path.appendingPathComponent(".gpg-id")

View file

@ -24,16 +24,16 @@ public class PasswordTableEntry: NSObject {
}
public func match(_ searchText: String) -> Bool {
return PasswordTableEntry.match(nameWithCategory: passwordEntity.nameWithCategory, searchText: searchText)
PasswordTableEntry.match(nameWithCategory: passwordEntity.nameWithCategory, searchText: searchText)
}
public static func match(nameWithCategory: String, searchText: String) -> Bool {
let titleSplit = nameWithCategory.split{ !($0.isLetter || $0.isNumber || $0 == ".") }
let titleSplit = nameWithCategory.split { !($0.isLetter || $0.isNumber || $0 == ".") }
for str in titleSplit {
if (str.localizedCaseInsensitiveContains(searchText)) {
if str.localizedCaseInsensitiveContains(searchText) {
return true
}
if (searchText.localizedCaseInsensitiveContains(str)) {
if searchText.localizedCaseInsensitiveContains(str) {
return true
}
}
@ -41,4 +41,3 @@ public class PasswordTableEntry: NSObject {
return false
}
}

View file

@ -7,7 +7,6 @@
//
public struct AdditionField: Hashable {
public let title: String, content: String
public init(title: String = "", content: String = "") {
@ -16,7 +15,7 @@ public struct AdditionField: Hashable {
}
var asString: String {
return title.isEmpty ? content : title + ": " + content
title.isEmpty ? content : title + ": " + content
}
var asTuple: (String, String) {
@ -25,21 +24,20 @@ public struct AdditionField: Hashable {
}
extension AdditionField {
static func | (left: String, right: AdditionField) -> String {
return left | right.asString
left | right.asString
}
static func | (left: AdditionField, right: String) -> String {
return left.asString | right
left.asString | right
}
static func | (left: AdditionField, right: AdditionField) -> String {
return left.asString | right
left.asString | right
}
}
infix operator =>: MultiplicationPrecedence
public func => (key: String, value: String) -> AdditionField {
return AdditionField(title: key, content: value)
AdditionField(title: key, content: value)
}

View file

@ -7,7 +7,6 @@
//
public struct Constants {
static let OTP_SECRET = "otp_secret"
static let OTP_TYPE = "otp_type"
static let OTP_ALGORITHM = "otp_algorithm"
@ -54,7 +53,7 @@ public struct Constants {
}
static func isOtpKeyword(_ keyword: String) -> Bool {
return OTP_KEYWORDS.contains(keyword.lowercased())
OTP_KEYWORDS.contains(keyword.lowercased())
}
static func isUnknown(_ string: String) -> Bool {
@ -63,10 +62,10 @@ public struct Constants {
}
static func unknown(_ number: UInt) -> String {
return "\(UNKNOWN) \(number)"
"\(UNKNOWN) \(number)"
}
static func getSeparator(breakingLines: Bool) -> String {
return breakingLines ? MULTILINE_WITH_LINE_BREAK_SEPARATOR : MULTILINE_WITHOUT_LINE_BREAK_SEPARATOR
breakingLines ? MULTILINE_WITH_LINE_BREAK_SEPARATOR : MULTILINE_WITHOUT_LINE_BREAK_SEPARATOR
}
}

View file

@ -14,7 +14,7 @@ public enum OTPType: String {
case none = "None"
var description: String {
return rawValue.localize()
rawValue.localize()
}
init(token: Token?) {

View file

@ -7,7 +7,6 @@
//
class Parser {
let firstLine: String
let additionsSection: String
let purgedAdditionalLines: [String]
@ -19,8 +18,8 @@ class Parser {
let splittedPlainText = plainText.splitByNewline()
firstLine = splittedPlainText.first!
additionsSection = splittedPlainText[1...].joined(separator: "\n")
purgedAdditionalLines = splittedPlainText[1...].filter { !$0.isEmpty }
self.additionsSection = splittedPlainText[1...].joined(separator: "\n")
self.purgedAdditionalLines = splittedPlainText[1...].filter { !$0.isEmpty }
}
private func getAdditionFields() -> [AdditionField] {
@ -57,7 +56,7 @@ class Parser {
}
let initialBlanks = String(repeating: Constants.BLANK, count: numberInitialBlanks)
while i < purgedAdditionalLines.count && purgedAdditionalLines[i].starts(with: initialBlanks) {
while i < purgedAdditionalLines.count, purgedAdditionalLines[i].starts(with: initialBlanks) {
result.append(String(purgedAdditionalLines[i].dropFirst(numberInitialBlanks)))
result.append(Constants.getSeparator(breakingLines: !removingLineBreaks))
i += 1

View file

@ -27,7 +27,6 @@ import OneTimePassword
/// * digits: `6` (default: `6`, optional)
///
class TokenBuilder {
private var name: String = ""
private var secret: Data?
private var type: OTPType = .totp
@ -80,7 +79,6 @@ class TokenBuilder {
return self
}
func build() -> Token? {
guard secret != nil, digits != nil else {
return nil

View file

@ -7,7 +7,6 @@
//
public struct PasswordGenerator: Codable {
private static let digits = "0123456789"
private static let letters = "abcdefghijklmnopqrstuvwxyz"
private static let capitalLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@ -114,7 +113,7 @@ public struct PasswordGenerator: Codable {
}
private func selectRandomly(count: Int, from string: String) -> [Character] {
return (0 ..< count).map { _ in string.randomElement()! }
(0 ..< count).map { _ in string.randomElement()! }
}
}

View file

@ -13,7 +13,7 @@ public enum PasswordGeneratorFlavor: String {
case xkcd = "XKCD"
public var localized: String {
return rawValue.localize()
rawValue.localize()
}
public var longNameLocalized: String {

View file

@ -8,11 +8,11 @@
import XCTest
// swiftformat:disable:next sortedImports
@testable import passKit
@testable import Crypto
class CryptoFrameworkTest: XCTestCase {
typealias MessageConverter = (CryptoPGPMessage, NSErrorPointer) -> CryptoPGPMessage?
private let testText = "Hello World!"
@ -47,7 +47,7 @@ class CryptoFrameworkTest: XCTestCase {
].forEach { testKeyInfo in
var error: NSError?
guard let publicKey = CryptoNewKeyFromArmored(testKeyInfo.publicKey, &error),
let privateKey = CryptoNewKeyFromArmored(testKeyInfo.privateKey, &error) else {
let privateKey = CryptoNewKeyFromArmored(testKeyInfo.privateKey, &error) else {
XCTFail("Keys cannot be initialized.")
return
}

View file

@ -6,8 +6,8 @@
// Copyright © 2019 Bob Sun. All rights reserved.
//
import XCTest
import SwiftyUserDefaults
import XCTest
@testable import passKit
@ -165,4 +165,3 @@ class PGPAgentTest: XCTestCase {
try KeyFileManager(keyType: PgpKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: privateKey)
}
}

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
class ArraySlicesTest: XCTestCase {
func testZeroCount() {
XCTAssertEqual([1, 2, 3].slices(count: 0), [])
}

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
class StringUtilitiesTest: XCTestCase {
func testTrimmed() {
[
(" ", ""),

View file

@ -6,20 +6,20 @@
//
import Foundation
import XCTest
import ObjectiveGit
import XCTest
@testable import passKit
class PasswordStoreTest: XCTestCase {
let cloneOptions: [String : GTCredentialProvider] = {
let credentialProvider = GTCredentialProvider { (_, _, _) -> (GTCredential?) in
try? GTCredential(userName: "", password: "")
}
return [GTRepositoryCloneOptionsCredentialProvider: credentialProvider]
}()
let remoteRepoURL = URL(string: "https://github.com/mssun/passforios-password-store.git")!
let cloneOptions: [String: GTCredentialProvider] = {
let credentialProvider = GTCredentialProvider { (_, _, _) -> (GTCredential?) in
try? GTCredential(userName: "", password: "")
}
return [GTRepositoryCloneOptionsCredentialProvider: credentialProvider]
}()
let remoteRepoURL = URL(string: "https://github.com/mssun/passforios-password-store.git")!
func testCloneAndDecryptMultiKeys() throws {
let url = URL(fileURLWithPath: "\(Globals.repositoryPath)-test")
@ -38,8 +38,8 @@ class PasswordStoreTest: XCTestCase {
[
("work/github.com", "4712286271220DB299883EA7062E678DA1024DAE"),
("personal/github.com", "787EAE1A5FA3E749AA34CC6AA0645EBED862027E")
].forEach {(path, id) in
("personal/github.com", "787EAE1A5FA3E749AA34CC6AA0645EBED862027E"),
].forEach { path, id in
let keyID = findGPGID(from: url.appendingPathComponent(path))
XCTAssertEqual(keyID, id)
}
@ -57,17 +57,14 @@ class PasswordStoreTest: XCTestCase {
let testPassword = Password(name: "test", url: URL(string: "test.gpg")!, plainText: "testpassword")
let testPasswordEntity = try passwordStore.add(password: testPassword)!
let testPasswordPlain = try passwordStore.decrypt(passwordEntity: testPasswordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase )
let testPasswordPlain = try passwordStore.decrypt(passwordEntity: testPasswordEntity, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
XCTAssertEqual(testPasswordPlain.plainText, "testpassword")
passwordStore.erase()
}
private func decrypt(passwordStore: PasswordStore, path: String, passphrase: String) throws -> Password {
private func decrypt(passwordStore: PasswordStore, path: String, passphrase _: String) throws -> Password {
let entity = passwordStore.getPasswordEntity(by: path, isDir: false)!
return try passwordStore.decrypt(passwordEntity: entity, requestPGPKeyPassphrase: requestPGPKeyPassphrase )
return try passwordStore.decrypt(passwordEntity: entity, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
}
}

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
class PasswordTableEntryTest: XCTestCase {
func testExample() {
let nameWithCategoryList = [
"github",
@ -27,11 +26,11 @@ class PasswordTableEntryTest: XCTestCase {
]
let searchTextList1 = [
"github.com",
"www.github.com"
"www.github.com",
]
let searchTextList2 = [
"xx.com",
"www.xx.com"
"www.xx.com",
]
for nameWithCategory in nameWithCategoryList {
@ -43,5 +42,4 @@ class PasswordTableEntryTest: XCTestCase {
}
}
}
}

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
class PasswordTest: XCTestCase {
func testUrl() {
let password = getPasswordObjectWith(content: "")
@ -245,7 +244,6 @@ class PasswordTest: XCTestCase {
XCTAssertEqual(password.nameFromPath, "exampleusername")
}
func testMultilineValues() {
let lineBreakField = "with line breaks" => "|\n This is \n text spread over \n multiple lines! "
let noLineBreakField = "without line breaks" => " > \n This is \n text spread over\n multiple lines!"

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
class AdditionFieldTest: XCTestCase {
func testAdditionField() {
let field1 = AdditionField(title: "key", content: "value")
let field2 = AdditionField(title: "no content")

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
class ConstantsTest: XCTestCase {
func testIsOtpRelated() {
XCTAssert(Constants.isOtpRelated(line: "otpauth://something"))
XCTAssert(Constants.isOtpRelated(line: "otp_algorithm: algorithm"))

View file

@ -12,7 +12,6 @@ import XCTest
@testable import passKit
class OTPTypeTest: XCTestCase {
func testInitFromToken() {
let secret = "secret".data(using: .utf8)!

View file

@ -6,11 +6,10 @@
// Copyright © 2018 Bob Sun. All rights reserved.
//
@testable import passKit
import XCTest
@testable import passKit
class ParserTest: XCTestCase {
func testInit() {
[
("", "", "", []),

View file

@ -13,7 +13,6 @@ import XCTest
@testable import passKit
class TokenBuilderTest: XCTestCase {
private let SECRET = "secret"
private let DIGITS = Constants.DEFAULT_DIGITS
private let TIMER = Generator.Factor.timer(period: Constants.DEFAULT_PERIOD)

View file

@ -11,10 +11,9 @@ import XCTest
@testable import passKit
class PasswordGeneratorFlavorTest: XCTestCase {
func testLengthLimits() {
// Ensure properly chosen length limits. So this check no longer needs to be performed in the code.
PasswordGeneratorFlavor.allCases.map { $0.lengthLimits }.forEach {
PasswordGeneratorFlavor.allCases.map(\.lengthLimits).forEach {
XCTAssertLessThanOrEqual($0.min, $0.max)
}
}

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
class PasswordGeneratorTest: XCTestCase {
func testLimitedLength() {
[
PasswordGenerator(length: 15),

View file

@ -21,15 +21,15 @@ class DictBasedKeychain: KeyStore {
}
public func contains(key: String) -> Bool {
return store[key] != nil
store[key] != nil
}
public func get(for key: String) -> Data? {
return store[key] as? Data
store[key] as? Data
}
public func get(for key: String) -> String? {
return store[key] as? String
store[key] as? String
}
public func removeContent(for key: String) {

View file

@ -29,7 +29,7 @@ let MULTILINE_BLOCK_START = "multiline block" => "|"
let MULTILINE_LINE_START = "multiline line" => ">"
func getPasswordObjectWith(content: String, url: URL? = nil) -> Password {
return Password(name: "password", url: url ?? PASSWORD_URL, plainText: content)
Password(name: "password", url: url ?? PASSWORD_URL, plainText: content)
}
func assertDefaults(in password: Password, with passwordString: String, and additions: String,
@ -44,11 +44,11 @@ func assertDefaults(in password: Password, with passwordString: String, and addi
}
infix operator : AdditionPrecedence
func (field: AdditionField, password: Password) -> Bool {
return password.getFilteredAdditions().contains(field)
func (field: AdditionField, password: Password) -> Bool {
password.getFilteredAdditions().contains(field)
}
infix operator : AdditionPrecedence
func (field: AdditionField, password: Password) -> Bool {
return !(field password)
func (field: AdditionField, password: Password) -> Bool {
!(field password)
}

View file

@ -11,7 +11,6 @@ import XCTest
@testable import passKit
struct PGPTestSet {
fileprivate static var ALL_TEST_SETS: [String: PGPTestSet] = [:]
let publicKey: String
@ -81,14 +80,14 @@ let NISTP384 = PGPTestSet(
passphrase: "soirofssap"
).collect()
let RSA2048_RSA4096 = MultiKeyPGPTestSet(
let RSA2048_RSA4096 = MultiKeyPGPTestSet(
publicKeys: PGP_RSA2048_PUBLIC_KEY | PGP_RSA4096_PUBLIC_KEY,
privateKeys: PGP_RSA2048_PRIVATE_KEY | PGP_RSA4096_PRIVATE_KEY,
fingerprints: ["a1024dae", "d862027e"],
passphrases: ["passforios", "passforios"]
)
let ED25519_NISTP384 = MultiKeyPGPTestSet(
let ED25519_NISTP384 = MultiKeyPGPTestSet(
publicKeys: PGP_ED25519_PUBLIC_KEY | PGP_NISTP384_PUBLIC_KEY,
privateKeys: PGP_ED25519_PRIVATE_KEY | PGP_NISTP384_PRIVATE_KEY,
fingerprints: ["e9444483", "5af3c085"],

View file

@ -10,7 +10,6 @@ import XCTest
@testable import passKit
class passKitTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
@ -28,9 +27,8 @@ class passKitTests: XCTestCase {
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
measure {
// Put the code you want to measure the time of here.
}
}
}

View file

@ -10,7 +10,6 @@ import Intents
import passKit
class IntentHandler: INExtension {
override func handler(for intent: INIntent) -> Any {
guard intent is SyncRepositoryIntent else {
fatalError("Unhandled intent type \(intent).")

View file

@ -10,7 +10,6 @@ import Intents
import passKit
public class SyncRepositoryIntentHandler: NSObject, SyncRepositoryIntentHandling {
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
@ -24,7 +23,7 @@ public class SyncRepositoryIntentHandler: NSObject, SyncRepositoryIntentHandling
}
}
public func handle(intent: SyncRepositoryIntent, completion: @escaping (SyncRepositoryIntentResponse) -> Void) {
public func handle(intent _: SyncRepositoryIntent, completion: @escaping (SyncRepositoryIntentResponse) -> Void) {
guard passwordStore.repositoryExists() else {
completion(SyncRepositoryIntentResponse(code: .noRepository, userActivity: nil))
return

View file

@ -9,7 +9,6 @@
import XCTest
class passTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
@ -27,9 +26,8 @@ class passTests: XCTestCase {
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
measure {
// Put the code you want to measure the time of here.
}
}
}