Add suggested passwords in AutoFill
This commit is contained in:
parent
156588bd93
commit
d4669bbfcb
4 changed files with 94 additions and 6 deletions
|
|
@ -17,13 +17,13 @@
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="XmI-l4-SgT">
|
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="XmI-l4-SgT">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="passwordTableViewCell" textLabel="U0x-8f-AET" detailTextLabel="kY1-Ac-C3d" style="IBUITableViewCellStyleValue1" id="fXA-SG-IOe" customClass="PasswordTableViewCell" customModule="passAutoFillExtension" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="passwordTableViewCell" textLabel="U0x-8f-AET" detailTextLabel="kY1-Ac-C3d" style="IBUITableViewCellStyleValue1" id="fXA-SG-IOe" customClass="PasswordTableViewCell" customModule="passAutoFillExtension" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="28" width="375" height="43.5"/>
|
<rect key="frame" x="0.0" y="55.5" width="375" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fXA-SG-IOe" id="KPa-Az-i6V">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fXA-SG-IOe" id="KPa-Az-i6V">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||||
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
||||||
let url = serviceIdentifiers.first.flatMap { URL(string: $0.identifier) }
|
let url = serviceIdentifiers.first.flatMap { URL(string: $0.identifier) }
|
||||||
passwordsViewController.navigationItem.prompt = url?.host
|
passwordsViewController.navigationItem.prompt = url?.host
|
||||||
|
let keywords = url?.host?.sanitizedDomain?.components(separatedBy: ".") ?? []
|
||||||
|
passwordsViewController.showPasswordsWithSuggstion(keywords)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,3 +52,14 @@ extension CredentialProviderViewController: PasswordSelectionDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension String {
|
||||||
|
var sanitizedDomain: String? {
|
||||||
|
replacingOccurrences(of: ".com", with: "")
|
||||||
|
.replacingOccurrences(of: ".org", with: "")
|
||||||
|
.replacingOccurrences(of: ".edu", with: "")
|
||||||
|
.replacingOccurrences(of: ".net", with: "")
|
||||||
|
.replacingOccurrences(of: ".gov", with: "")
|
||||||
|
.replacingOccurrences(of: "www.", with: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,18 +27,27 @@ class PasswordsViewController: UIViewController {
|
||||||
return uiSearchController
|
return uiSearchController
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
lazy var searchBar: UISearchBar = {
|
||||||
|
self.searchController.searchBar
|
||||||
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
navigationItem.searchController = searchController
|
navigationItem.searchController = searchController
|
||||||
navigationItem.hidesSearchBarWhenScrolling = false
|
navigationItem.hidesSearchBarWhenScrolling = false
|
||||||
|
|
||||||
searchController.searchBar.delegate = self
|
searchBar.delegate = self
|
||||||
|
|
||||||
tableView.delegate = self
|
tableView.delegate = self
|
||||||
tableView.dataSource = dataSource
|
tableView.dataSource = dataSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showPasswordsWithSuggstion(_ keywords: [String]) {
|
||||||
|
dataSource.showTableEntriesWithSuggestion(matching: keywords)
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction
|
@IBAction
|
||||||
private func cancel(_: AnyObject?) {
|
private func cancel(_: AnyObject?) {
|
||||||
self.extensionContext?.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
self.extensionContext?.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,69 @@ import passKit
|
||||||
class PasswordsTableDataSource: NSObject, UITableViewDataSource {
|
class PasswordsTableDataSource: NSObject, UITableViewDataSource {
|
||||||
var passwordTableEntries: [PasswordTableEntry]
|
var passwordTableEntries: [PasswordTableEntry]
|
||||||
var filteredPasswordsTableEntries: [PasswordTableEntry]
|
var filteredPasswordsTableEntries: [PasswordTableEntry]
|
||||||
|
var suggestedPasswordsTableEntries: [PasswordTableEntry]
|
||||||
|
var otherPasswordsTableEntries: [PasswordTableEntry]
|
||||||
|
|
||||||
|
var showSuggestion: Bool = false
|
||||||
|
|
||||||
init(entries: [PasswordTableEntry] = []) {
|
init(entries: [PasswordTableEntry] = []) {
|
||||||
passwordTableEntries = entries
|
passwordTableEntries = entries
|
||||||
filteredPasswordsTableEntries = passwordTableEntries
|
filteredPasswordsTableEntries = passwordTableEntries
|
||||||
|
suggestedPasswordsTableEntries = []
|
||||||
|
otherPasswordsTableEntries = []
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
|
if !showSuggestion {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
filteredPasswordsTableEntries.count
|
if !showSuggestion {
|
||||||
|
return filteredPasswordsTableEntries.count
|
||||||
|
}
|
||||||
|
|
||||||
|
if section == 0 {
|
||||||
|
return suggestedPasswordsTableEntries.count
|
||||||
|
} else {
|
||||||
|
return otherPasswordsTableEntries.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
|
if suggestedPasswordsTableEntries.isEmpty {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !showSuggestion {
|
||||||
|
return "All Passwords"
|
||||||
|
}
|
||||||
|
|
||||||
|
if section == 0 {
|
||||||
|
return "Suggested Passwords"
|
||||||
|
} else {
|
||||||
|
return "Other Passwords"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) as! PasswordTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) as! PasswordTableViewCell
|
||||||
|
|
||||||
let entry = filteredPasswordsTableEntries[indexPath.row]
|
var entry: PasswordTableEntry!
|
||||||
|
if !showSuggestion {
|
||||||
|
entry = filteredPasswordsTableEntries[indexPath.row]
|
||||||
|
cell.configure(with: entry)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
if indexPath.section == 0 {
|
||||||
|
entry = suggestedPasswordsTableEntries[indexPath.row]
|
||||||
|
} else {
|
||||||
|
entry = otherPasswordsTableEntries[indexPath.row]
|
||||||
|
}
|
||||||
cell.configure(with: entry)
|
cell.configure(with: entry)
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
|
|
@ -34,9 +83,26 @@ class PasswordsTableDataSource: NSObject, UITableViewDataSource {
|
||||||
func showTableEntries(matching text: String) {
|
func showTableEntries(matching text: String) {
|
||||||
guard !text.isEmpty else {
|
guard !text.isEmpty else {
|
||||||
filteredPasswordsTableEntries = passwordTableEntries
|
filteredPasswordsTableEntries = passwordTableEntries
|
||||||
|
showSuggestion = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredPasswordsTableEntries = passwordTableEntries.filter { $0.match(text) }
|
filteredPasswordsTableEntries = passwordTableEntries.filter { $0.match(text) }
|
||||||
|
showSuggestion = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func showTableEntriesWithSuggestion(matching keywords: [String]) {
|
||||||
|
for entry in passwordTableEntries {
|
||||||
|
var match = false
|
||||||
|
for keyword in keywords {
|
||||||
|
match = match || entry.match(keyword)
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
suggestedPasswordsTableEntries.append(entry)
|
||||||
|
} else {
|
||||||
|
otherPasswordsTableEntries.append(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showSuggestion = !suggestedPasswordsTableEntries.isEmpty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue