Add suggested passwords in AutoFill

This commit is contained in:
Mingshen Sun 2021-01-02 22:13:48 -08:00
parent 156588bd93
commit d4669bbfcb
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
4 changed files with 94 additions and 6 deletions

View file

@ -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"/>

View file

@ -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: "")
}
}

View file

@ -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))

View file

@ -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
} }
} }