Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6d113edcd | ||
|
|
8bba64076f | ||
|
|
a8d612f745 | ||
|
|
5a59867d48 | ||
|
|
dfb728e4ab | ||
|
|
5281ce4895 | ||
|
|
e52cf3f39b | ||
|
|
a70ea40acd | ||
|
|
3ee729070a | ||
|
|
da5ad145b0 |
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.420",
|
||||||
|
"green" : "0.271",
|
||||||
|
"blue" : "0.467",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.651",
|
||||||
|
"green" : "0.498",
|
||||||
|
"blue" : "0.690",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.980",
|
||||||
|
"green" : "0.957",
|
||||||
|
"blue" : "0.925",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.141",
|
||||||
|
"green" : "0.125",
|
||||||
|
"blue" : "0.094",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>group.org.antiloop222.mypass</string>
|
<string>group.org.antiloop222.keevault</string>
|
||||||
</array>
|
</array>
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(AppIdentifierPrefix)org.antiloop222.mypass</string>
|
<string>$(AppIdentifierPrefix)org.antiloop222.keevault</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -17,36 +17,7 @@
|
|||||||
<view key="view" contentMode="scaleToFill" id="BuU-Ak-iZz">
|
<view key="view" contentMode="scaleToFill" id="BuU-Ak-iZz">
|
||||||
<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>
|
|
||||||
<navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3wq-kG-lGu">
|
|
||||||
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
|
||||||
<items>
|
|
||||||
<navigationItem id="cbj-pk-SYj">
|
|
||||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="bEZ-MG-jDy">
|
|
||||||
<connections>
|
|
||||||
<action selector="cancel:" destination="Xki-Si-B7m" id="6ap-3Q-iEX"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
</navigationItem>
|
|
||||||
</items>
|
|
||||||
</navigationBar>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a7v-ug-QzG">
|
|
||||||
<rect key="frame" x="87.5" y="327" width="199" height="33"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<state key="normal" title="Return Example Password"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="passwordSelected:" destination="Xki-Si-B7m" eventType="touchUpInside" id="ODd-lr-mud"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="Ky8-vK-JVj" firstAttribute="top" secondItem="3wq-kG-lGu" secondAttribute="top" id="BIN-jb-uNd"/>
|
|
||||||
<constraint firstItem="3wq-kG-lGu" firstAttribute="width" secondItem="BuU-Ak-iZz" secondAttribute="width" id="UkD-v4-BcH"/>
|
|
||||||
<constraint firstItem="a7v-ug-QzG" firstAttribute="centerY" secondItem="Ky8-vK-JVj" secondAttribute="centerY" id="fAC-0v-NFE"/>
|
|
||||||
<constraint firstItem="a7v-ug-QzG" firstAttribute="centerX" secondItem="3wq-kG-lGu" secondAttribute="centerX" id="io1-ZS-gwn"/>
|
|
||||||
<constraint firstItem="3wq-kG-lGu" firstAttribute="centerX" secondItem="BuU-Ak-iZz" secondAttribute="centerX" id="rtV-5c-0bl"/>
|
|
||||||
</constraints>
|
|
||||||
<viewLayoutGuide key="safeArea" id="Ky8-vK-JVj"/>
|
<viewLayoutGuide key="safeArea" id="Ky8-vK-JVj"/>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
|
|||||||
@@ -6,53 +6,45 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import AuthenticationServices
|
import AuthenticationServices
|
||||||
|
import SwiftUI
|
||||||
|
import KeeVaultCore
|
||||||
|
|
||||||
class CredentialProviderViewController: ASCredentialProviderViewController {
|
final class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||||
|
|
||||||
/*
|
private let session = VaultSession()
|
||||||
Prepare your UI to list available credentials for the user to choose from. The items in
|
private let bookmarkService = FileBookmarkService()
|
||||||
'serviceIdentifiers' describe the service the user is logging in to, so your extension can
|
private let keychainStore = KeychainStore(accessGroup: "F2KB6W8N4R.org.antiloop222.keevault")
|
||||||
prioritize the most relevant credentials in the list.
|
private let biometricService = BiometricAuthService()
|
||||||
*/
|
|
||||||
|
// Called when the user selects KeeVault from the QuickType bar.
|
||||||
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
||||||
|
let ids = serviceIdentifiers.map(\.identifier)
|
||||||
|
showUI(serviceIdentifiers: ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
private func showUI(serviceIdentifiers: [String]) {
|
||||||
Implement this method if your extension supports showing credentials in the QuickType bar.
|
let rootView = ExtensionRootView(
|
||||||
When the user selects a credential from your app, this method will be called with the
|
session: session,
|
||||||
ASPasswordCredentialIdentity your app has previously saved to the ASCredentialIdentityStore.
|
serviceIdentifiers: serviceIdentifiers,
|
||||||
Provide the password by completing the extension request with the associated ASPasswordCredential.
|
bookmarkService: bookmarkService,
|
||||||
If using the credential would require showing custom UI for authenticating the user, cancel
|
keychainStore: keychainStore,
|
||||||
the request with error code ASExtensionError.userInteractionRequired.
|
biometricService: biometricService,
|
||||||
|
onSelect: { [weak self] (entry: Entry) in
|
||||||
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
|
let credential = ASPasswordCredential(
|
||||||
let databaseIsUnlocked = true
|
user: entry.username,
|
||||||
if (databaseIsUnlocked) {
|
password: entry.password.reveal()
|
||||||
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
|
)
|
||||||
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
|
self?.extensionContext.completeRequest(withSelectedCredential: credential, completionHandler: nil)
|
||||||
} else {
|
},
|
||||||
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
|
onCancel: { [weak self] in
|
||||||
|
self?.extensionContext.cancelRequest(withError: ASExtensionError(.userCanceled))
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
let host = UIHostingController(rootView: rootView)
|
||||||
|
addChild(host)
|
||||||
|
view.addSubview(host.view)
|
||||||
|
host.view.frame = view.bounds
|
||||||
|
host.view.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight]
|
||||||
|
host.didMove(toParent: self)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
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) {
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@IBAction func cancel(_ sender: AnyObject?) {
|
|
||||||
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func passwordSelected(_ sender: AnyObject?) {
|
|
||||||
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
|
|
||||||
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>ASCredentialProviderExtensionCapabilities</key>
|
||||||
|
<dict>
|
||||||
|
<key>ProvidesPasswords</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
<key>NSExtensionMainStoryboard</key>
|
<key>NSExtensionMainStoryboard</key>
|
||||||
<string>MainInterface</string>
|
<string>MainInterface</string>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// AutoFill/Views/CredentialListView.swift
|
||||||
|
import SwiftUI
|
||||||
|
import KeeVaultCore
|
||||||
|
|
||||||
|
struct CredentialListView: View {
|
||||||
|
@ObservedObject var vm: ExtensionViewModel
|
||||||
|
@State private var searchQuery: String = ""
|
||||||
|
|
||||||
|
private var displayedSuggested: [Entry] {
|
||||||
|
searchQuery.isEmpty ? vm.suggestedEntries : []
|
||||||
|
}
|
||||||
|
|
||||||
|
private var displayedAll: [Entry] {
|
||||||
|
let entries = searchQuery.isEmpty ? vm.allEntries : vm.session.allEntries()
|
||||||
|
guard !searchQuery.isEmpty else { return entries }
|
||||||
|
let q = searchQuery.lowercased()
|
||||||
|
return entries.filter {
|
||||||
|
$0.title.lowercased().contains(q)
|
||||||
|
|| $0.username.lowercased().contains(q)
|
||||||
|
|| $0.url.lowercased().contains(q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
List {
|
||||||
|
if !displayedSuggested.isEmpty {
|
||||||
|
Section("Suggested") {
|
||||||
|
ForEach(displayedSuggested) { entry in
|
||||||
|
CredentialRow(entry: entry) { vm.select(entry) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section(displayedSuggested.isEmpty ? "" : "All Entries") {
|
||||||
|
if displayedAll.isEmpty {
|
||||||
|
Text("No entries found").foregroundStyle(.secondary)
|
||||||
|
} else {
|
||||||
|
ForEach(displayedAll) { entry in
|
||||||
|
CredentialRow(entry: entry) { vm.select(entry) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.background(Color("VaultBackground"))
|
||||||
|
.searchable(text: $searchQuery, prompt: "Search…")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct CredentialRow: View {
|
||||||
|
let entry: Entry
|
||||||
|
let onSelect: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button(action: onSelect) {
|
||||||
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
|
Text(entry.title).font(.body).foregroundStyle(.primary)
|
||||||
|
Text(entry.username).font(.caption).foregroundStyle(.secondary)
|
||||||
|
if !entry.url.isEmpty {
|
||||||
|
Text(entry.url).font(.caption2).foregroundStyle(.tertiary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
// AutoFill/Views/ExtensionUnlockView.swift
|
||||||
|
import SwiftUI
|
||||||
|
import Combine
|
||||||
|
import KeeVaultCore
|
||||||
|
|
||||||
|
/// Root view for the extension -- shows unlock screen or credential list depending on state.
|
||||||
|
struct ExtensionRootView: View {
|
||||||
|
@StateObject private var vm: ExtensionViewModel
|
||||||
|
|
||||||
|
init(
|
||||||
|
session: VaultSession,
|
||||||
|
serviceIdentifiers: [String],
|
||||||
|
bookmarkService: FileBookmarkService,
|
||||||
|
keychainStore: KeychainStore,
|
||||||
|
biometricService: BiometricAuthService,
|
||||||
|
onSelect: @escaping (Entry) -> Void,
|
||||||
|
onCancel: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
_vm = StateObject(wrappedValue: ExtensionViewModel(
|
||||||
|
session: session,
|
||||||
|
serviceIdentifiers: serviceIdentifiers,
|
||||||
|
bookmarkService: bookmarkService,
|
||||||
|
keychainStore: keychainStore,
|
||||||
|
biometricService: biometricService,
|
||||||
|
onSelect: onSelect,
|
||||||
|
onCancel: onCancel
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
SwiftUI.Group {
|
||||||
|
if vm.isLocked {
|
||||||
|
extensionUnlockView
|
||||||
|
} else {
|
||||||
|
CredentialListView(vm: vm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(Color("VaultBackground"))
|
||||||
|
.navigationTitle("KeeVault")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
Button("Cancel", action: vm.cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tint(Color("AccentColor"))
|
||||||
|
.task { await vm.tryBiometricUnlock() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var extensionUnlockView: some View {
|
||||||
|
VStack(spacing: 24) {
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "lock.shield.fill").font(.system(size: 48)).foregroundStyle(.tint)
|
||||||
|
Text("Vault Locked").font(.headline)
|
||||||
|
|
||||||
|
if vm.canUseBiometrics {
|
||||||
|
Button(action: { Task { await vm.tryBiometricUnlock() } }) {
|
||||||
|
Label("Use Face ID / Touch ID", systemImage: "faceid")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureField("Master Password", text: $vm.password)
|
||||||
|
.textFieldStyle(.roundedBorder)
|
||||||
|
.onSubmit { Task { await vm.unlockWithPassword() } }
|
||||||
|
|
||||||
|
Button("Unlock") { Task { await vm.unlockWithPassword() } }
|
||||||
|
.buttonStyle(.bordered)
|
||||||
|
.disabled(vm.password.isEmpty || vm.isUnlocking)
|
||||||
|
|
||||||
|
if let msg = vm.errorMessage {
|
||||||
|
Text(msg).foregroundStyle(.red).font(.caption).multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !vm.hasVault || vm.errorMessage != nil {
|
||||||
|
Link(destination: URL(string: "keevault://unlock")!) {
|
||||||
|
Label("Open KeeVault to set up vault", systemImage: "arrow.up.right")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class ExtensionViewModel: ObservableObject {
|
||||||
|
@Published var password: String = ""
|
||||||
|
@Published var errorMessage: String?
|
||||||
|
@Published var isUnlocking: Bool = false
|
||||||
|
|
||||||
|
let session: VaultSession
|
||||||
|
let serviceIdentifiers: [String]
|
||||||
|
private let bookmarkService: FileBookmarkService
|
||||||
|
private let keychainStore: KeychainStore
|
||||||
|
private let biometricService: BiometricAuthService
|
||||||
|
private let onSelect: (Entry) -> Void
|
||||||
|
private let onCancel: () -> Void
|
||||||
|
private var sessionCancellable: AnyCancellable?
|
||||||
|
|
||||||
|
init(
|
||||||
|
session: VaultSession,
|
||||||
|
serviceIdentifiers: [String],
|
||||||
|
bookmarkService: FileBookmarkService,
|
||||||
|
keychainStore: KeychainStore,
|
||||||
|
biometricService: BiometricAuthService,
|
||||||
|
onSelect: @escaping (Entry) -> Void,
|
||||||
|
onCancel: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
self.session = session
|
||||||
|
self.serviceIdentifiers = serviceIdentifiers
|
||||||
|
self.bookmarkService = bookmarkService
|
||||||
|
self.keychainStore = keychainStore
|
||||||
|
self.biometricService = biometricService
|
||||||
|
self.onSelect = onSelect
|
||||||
|
self.onCancel = onCancel
|
||||||
|
sessionCancellable = session.objectWillChange.sink { [weak self] in
|
||||||
|
self?.objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isLocked: Bool { session.isLocked }
|
||||||
|
var hasVault: Bool { bookmarkService.hasBookmark }
|
||||||
|
var canUseBiometrics: Bool { biometricService.isAvailable && keychainStore.exists(for: "masterPassword") }
|
||||||
|
|
||||||
|
var suggestedEntries: [Entry] {
|
||||||
|
CredentialMatcher.filter(entries: session.allEntries(), for: serviceIdentifiers).suggested
|
||||||
|
}
|
||||||
|
|
||||||
|
var allEntries: [Entry] {
|
||||||
|
CredentialMatcher.filter(entries: session.allEntries(), for: serviceIdentifiers).all
|
||||||
|
}
|
||||||
|
|
||||||
|
func select(_ entry: Entry) { onSelect(entry) }
|
||||||
|
func cancel() { onCancel() }
|
||||||
|
|
||||||
|
func tryBiometricUnlock() async {
|
||||||
|
guard canUseBiometrics, session.isLocked else { return }
|
||||||
|
isUnlocking = true
|
||||||
|
do {
|
||||||
|
try await biometricService.authenticate(reason: "Unlock KeeVault")
|
||||||
|
try performUnlockFromKeychain()
|
||||||
|
} catch {
|
||||||
|
errorMessage = error.localizedDescription
|
||||||
|
}
|
||||||
|
isUnlocking = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockWithPassword() async {
|
||||||
|
isUnlocking = true
|
||||||
|
errorMessage = nil
|
||||||
|
do {
|
||||||
|
let url = try bookmarkService.resolveURL()
|
||||||
|
defer { bookmarkService.stopAccess(url: url) }
|
||||||
|
try session.unlock(url: url, password: password)
|
||||||
|
try keychainStore.save(password: password, for: "masterPassword")
|
||||||
|
password = ""
|
||||||
|
} catch {
|
||||||
|
errorMessage = error.localizedDescription
|
||||||
|
}
|
||||||
|
isUnlocking = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func performUnlockFromKeychain() throws {
|
||||||
|
let pw = try keychainStore.load(for: "masterPassword")
|
||||||
|
let url = try bookmarkService.resolveURL()
|
||||||
|
defer { bookmarkService.stopAccess(url: url) }
|
||||||
|
try session.unlock(url: url, password: pw)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,12 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
205678DB2FC0EB5200251C6B /* MyPassCore in Frameworks */ = {isa = PBXBuildFile; productRef = 205678DA2FC0EB5200251C6B /* MyPassCore */; };
|
205678DB2FC0EB5200251C6B /* KeeVaultCore in Frameworks */ = {isa = PBXBuildFile; productRef = 205678DA2FC0EB5200251C6B /* KeeVaultCore */; };
|
||||||
2096B432305EC2B200C2F253 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 205678E32FC0F52A00251C6B /* AuthenticationServices.framework */; };
|
2096B432305EC2B200C2F253 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 205678E32FC0F52A00251C6B /* AuthenticationServices.framework */; };
|
||||||
2096B43D305EC2B200C2F253 /* AutoFill.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 2096B431305EC2B200C2F253 /* AutoFill.appex */; platformFilters = (ios, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
2096B43D305EC2B200C2F253 /* AutoFill.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 2096B431305EC2B200C2F253 /* AutoFill.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
67DE2FCFC21FD6DF5E761C87 /* MyPassCore in Frameworks */ = {isa = PBXBuildFile; productRef = 205678DA2FC0EB5200251C6B /* MyPassCore */; };
|
67DE2FCFC21FD6DF5E761C87 /* KeeVaultCore in Frameworks */ = {isa = PBXBuildFile; productRef = 205678DA2FC0EB5200251C6B /* KeeVaultCore */; };
|
||||||
|
982CE38F924E4FD387BC2C20 /* BiometricAuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C7DFA4A59640D7BF6B4960 /* BiometricAuthService.swift */; };
|
||||||
|
EABAC2B148F845C8A20EC2CC /* FileBookmarkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADBBB9F1BA62473294D2B5B5 /* FileBookmarkService.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -19,14 +21,14 @@
|
|||||||
containerPortal = 2056788B2FBF9CBB00251C6B /* Project object */;
|
containerPortal = 2056788B2FBF9CBB00251C6B /* Project object */;
|
||||||
proxyType = 1;
|
proxyType = 1;
|
||||||
remoteGlobalIDString = 205678922FBF9CBB00251C6B;
|
remoteGlobalIDString = 205678922FBF9CBB00251C6B;
|
||||||
remoteInfo = MyPass;
|
remoteInfo = KeeVault;
|
||||||
};
|
};
|
||||||
205678AD2FBF9CBC00251C6B /* PBXContainerItemProxy */ = {
|
205678AD2FBF9CBC00251C6B /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 2056788B2FBF9CBB00251C6B /* Project object */;
|
containerPortal = 2056788B2FBF9CBB00251C6B /* Project object */;
|
||||||
proxyType = 1;
|
proxyType = 1;
|
||||||
remoteGlobalIDString = 205678922FBF9CBB00251C6B;
|
remoteGlobalIDString = 205678922FBF9CBB00251C6B;
|
||||||
remoteInfo = MyPass;
|
remoteInfo = KeeVault;
|
||||||
};
|
};
|
||||||
2096B43B305EC2B200C2F253 /* PBXContainerItemProxy */ = {
|
2096B43B305EC2B200C2F253 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
@@ -52,11 +54,13 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
205678932FBF9CBB00251C6B /* MyPass.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyPass.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
205678932FBF9CBB00251C6B /* KeeVault.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeeVault.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
205678A22FBF9CBC00251C6B /* MyPassTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MyPassTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
205678A22FBF9CBC00251C6B /* KeeVaultTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeeVaultTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
205678AC2FBF9CBC00251C6B /* MyPassUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MyPassUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
205678AC2FBF9CBC00251C6B /* KeeVaultUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeeVaultUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
205678E32FC0F52A00251C6B /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = System/Library/Frameworks/AuthenticationServices.framework; sourceTree = SDKROOT; };
|
205678E32FC0F52A00251C6B /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = System/Library/Frameworks/AuthenticationServices.framework; sourceTree = SDKROOT; };
|
||||||
2096B431305EC2B200C2F253 /* AutoFill.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AutoFill.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
2096B431305EC2B200C2F253 /* AutoFill.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AutoFill.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
ADBBB9F1BA62473294D2B5B5 /* FileBookmarkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FileBookmarkService.swift; path = KeeVault/Services/FileBookmarkService.swift; sourceTree = SOURCE_ROOT; };
|
||||||
|
D6C7DFA4A59640D7BF6B4960 /* BiometricAuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BiometricAuthService.swift; path = KeeVault/Services/BiometricAuthService.swift; sourceTree = SOURCE_ROOT; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
@@ -70,19 +74,19 @@
|
|||||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
205678952FBF9CBB00251C6B /* MyPass */ = {
|
205678952FBF9CBB00251C6B /* KeeVault */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
path = MyPass;
|
path = KeeVault;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
205678A52FBF9CBC00251C6B /* MyPassTests */ = {
|
205678A52FBF9CBC00251C6B /* KeeVaultTests */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
path = MyPassTests;
|
path = KeeVaultTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
205678AF2FBF9CBC00251C6B /* MyPassUITests */ = {
|
205678AF2FBF9CBC00251C6B /* KeeVaultUITests */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
path = MyPassUITests;
|
path = KeeVaultUITests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
2096B433305EC2B200C2F253 /* AutoFill */ = {
|
2096B433305EC2B200C2F253 /* AutoFill */ = {
|
||||||
@@ -100,7 +104,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
205678DB2FC0EB5200251C6B /* MyPassCore in Frameworks */,
|
205678DB2FC0EB5200251C6B /* KeeVaultCore in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -123,20 +127,30 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
2096B432305EC2B200C2F253 /* AuthenticationServices.framework in Frameworks */,
|
2096B432305EC2B200C2F253 /* AuthenticationServices.framework in Frameworks */,
|
||||||
67DE2FCFC21FD6DF5E761C87 /* MyPassCore in Frameworks */,
|
67DE2FCFC21FD6DF5E761C87 /* KeeVaultCore in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
1A2B3C4D5E6F7A8B9C0D1E2F /* AutoFill Shared Services */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
ADBBB9F1BA62473294D2B5B5 /* FileBookmarkService.swift */,
|
||||||
|
D6C7DFA4A59640D7BF6B4960 /* BiometricAuthService.swift */,
|
||||||
|
);
|
||||||
|
name = "AutoFill Shared Services";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
2056788A2FBF9CBB00251C6B = {
|
2056788A2FBF9CBB00251C6B = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
205678952FBF9CBB00251C6B /* MyPass */,
|
205678952FBF9CBB00251C6B /* KeeVault */,
|
||||||
205678A52FBF9CBC00251C6B /* MyPassTests */,
|
205678A52FBF9CBC00251C6B /* KeeVaultTests */,
|
||||||
205678AF2FBF9CBC00251C6B /* MyPassUITests */,
|
205678AF2FBF9CBC00251C6B /* KeeVaultUITests */,
|
||||||
2096B433305EC2B200C2F253 /* AutoFill */,
|
2096B433305EC2B200C2F253 /* AutoFill */,
|
||||||
|
1A2B3C4D5E6F7A8B9C0D1E2F /* AutoFill Shared Services */,
|
||||||
205678E22FC0F52A00251C6B /* Frameworks */,
|
205678E22FC0F52A00251C6B /* Frameworks */,
|
||||||
205678942FBF9CBB00251C6B /* Products */,
|
205678942FBF9CBB00251C6B /* Products */,
|
||||||
);
|
);
|
||||||
@@ -145,9 +159,9 @@
|
|||||||
205678942FBF9CBB00251C6B /* Products */ = {
|
205678942FBF9CBB00251C6B /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
205678932FBF9CBB00251C6B /* MyPass.app */,
|
205678932FBF9CBB00251C6B /* KeeVault.app */,
|
||||||
205678A22FBF9CBC00251C6B /* MyPassTests.xctest */,
|
205678A22FBF9CBC00251C6B /* KeeVaultTests.xctest */,
|
||||||
205678AC2FBF9CBC00251C6B /* MyPassUITests.xctest */,
|
205678AC2FBF9CBC00251C6B /* KeeVaultUITests.xctest */,
|
||||||
2096B431305EC2B200C2F253 /* AutoFill.appex */,
|
2096B431305EC2B200C2F253 /* AutoFill.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
@@ -164,9 +178,9 @@
|
|||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
205678922FBF9CBB00251C6B /* MyPass */ = {
|
205678922FBF9CBB00251C6B /* KeeVault */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPass" */;
|
buildConfigurationList = 205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVault" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
2056788F2FBF9CBB00251C6B /* Sources */,
|
2056788F2FBF9CBB00251C6B /* Sources */,
|
||||||
205678902FBF9CBB00251C6B /* Frameworks */,
|
205678902FBF9CBB00251C6B /* Frameworks */,
|
||||||
@@ -179,19 +193,19 @@
|
|||||||
2096B43C305EC2B200C2F253 /* PBXTargetDependency */,
|
2096B43C305EC2B200C2F253 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
205678952FBF9CBB00251C6B /* MyPass */,
|
205678952FBF9CBB00251C6B /* KeeVault */,
|
||||||
);
|
);
|
||||||
name = MyPass;
|
name = KeeVault;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
205678DA2FC0EB5200251C6B /* MyPassCore */,
|
205678DA2FC0EB5200251C6B /* KeeVaultCore */,
|
||||||
);
|
);
|
||||||
productName = MyPass;
|
productName = KeeVault;
|
||||||
productReference = 205678932FBF9CBB00251C6B /* MyPass.app */;
|
productReference = 205678932FBF9CBB00251C6B /* KeeVault.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
205678A12FBF9CBC00251C6B /* MyPassTests */ = {
|
205678A12FBF9CBC00251C6B /* KeeVaultTests */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassTests" */;
|
buildConfigurationList = 205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
2056789E2FBF9CBC00251C6B /* Sources */,
|
2056789E2FBF9CBC00251C6B /* Sources */,
|
||||||
2056789F2FBF9CBC00251C6B /* Frameworks */,
|
2056789F2FBF9CBC00251C6B /* Frameworks */,
|
||||||
@@ -203,18 +217,18 @@
|
|||||||
205678A42FBF9CBC00251C6B /* PBXTargetDependency */,
|
205678A42FBF9CBC00251C6B /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
205678A52FBF9CBC00251C6B /* MyPassTests */,
|
205678A52FBF9CBC00251C6B /* KeeVaultTests */,
|
||||||
);
|
);
|
||||||
name = MyPassTests;
|
name = KeeVaultTests;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
);
|
);
|
||||||
productName = MyPassTests;
|
productName = KeeVaultTests;
|
||||||
productReference = 205678A22FBF9CBC00251C6B /* MyPassTests.xctest */;
|
productReference = 205678A22FBF9CBC00251C6B /* KeeVaultTests.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
};
|
};
|
||||||
205678AB2FBF9CBC00251C6B /* MyPassUITests */ = {
|
205678AB2FBF9CBC00251C6B /* KeeVaultUITests */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassUITests" */;
|
buildConfigurationList = 205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultUITests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
205678A82FBF9CBC00251C6B /* Sources */,
|
205678A82FBF9CBC00251C6B /* Sources */,
|
||||||
205678A92FBF9CBC00251C6B /* Frameworks */,
|
205678A92FBF9CBC00251C6B /* Frameworks */,
|
||||||
@@ -226,13 +240,13 @@
|
|||||||
205678AE2FBF9CBC00251C6B /* PBXTargetDependency */,
|
205678AE2FBF9CBC00251C6B /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
205678AF2FBF9CBC00251C6B /* MyPassUITests */,
|
205678AF2FBF9CBC00251C6B /* KeeVaultUITests */,
|
||||||
);
|
);
|
||||||
name = MyPassUITests;
|
name = KeeVaultUITests;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
);
|
);
|
||||||
productName = MyPassUITests;
|
productName = KeeVaultUITests;
|
||||||
productReference = 205678AC2FBF9CBC00251C6B /* MyPassUITests.xctest */;
|
productReference = 205678AC2FBF9CBC00251C6B /* KeeVaultUITests.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.ui-testing";
|
productType = "com.apple.product-type.bundle.ui-testing";
|
||||||
};
|
};
|
||||||
2096B430305EC2B200C2F253 /* AutoFill */ = {
|
2096B430305EC2B200C2F253 /* AutoFill */ = {
|
||||||
@@ -252,7 +266,7 @@
|
|||||||
);
|
);
|
||||||
name = AutoFill;
|
name = AutoFill;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
205678DA2FC0EB5200251C6B /* MyPassCore */,
|
205678DA2FC0EB5200251C6B /* KeeVaultCore */,
|
||||||
);
|
);
|
||||||
productName = AutoFill;
|
productName = AutoFill;
|
||||||
productReference = 2096B431305EC2B200C2F253 /* AutoFill.appex */;
|
productReference = 2096B431305EC2B200C2F253 /* AutoFill.appex */;
|
||||||
@@ -266,7 +280,7 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastSwiftUpdateCheck = 2660;
|
LastSwiftUpdateCheck = 2660;
|
||||||
LastUpgradeCheck = 2650;
|
LastUpgradeCheck = 2660;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
205678922FBF9CBB00251C6B = {
|
205678922FBF9CBB00251C6B = {
|
||||||
CreatedOnToolsVersion = 26.5;
|
CreatedOnToolsVersion = 26.5;
|
||||||
@@ -284,7 +298,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 2056788E2FBF9CBB00251C6B /* Build configuration list for PBXProject "MyPass" */;
|
buildConfigurationList = 2056788E2FBF9CBB00251C6B /* Build configuration list for PBXProject "KeeVault" */;
|
||||||
developmentRegion = en;
|
developmentRegion = en;
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
knownRegions = (
|
knownRegions = (
|
||||||
@@ -294,16 +308,16 @@
|
|||||||
mainGroup = 2056788A2FBF9CBB00251C6B;
|
mainGroup = 2056788A2FBF9CBB00251C6B;
|
||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "MyPassCore" */,
|
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "KeeVaultCore" */,
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = 205678942FBF9CBB00251C6B /* Products */;
|
productRefGroup = 205678942FBF9CBB00251C6B /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
205678922FBF9CBB00251C6B /* MyPass */,
|
205678922FBF9CBB00251C6B /* KeeVault */,
|
||||||
205678A12FBF9CBC00251C6B /* MyPassTests */,
|
205678A12FBF9CBC00251C6B /* KeeVaultTests */,
|
||||||
205678AB2FBF9CBC00251C6B /* MyPassUITests */,
|
205678AB2FBF9CBC00251C6B /* KeeVaultUITests */,
|
||||||
2096B430305EC2B200C2F253 /* AutoFill */,
|
2096B430305EC2B200C2F253 /* AutoFill */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -366,6 +380,8 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
EABAC2B148F845C8A20EC2CC /* FileBookmarkService.swift in Sources */,
|
||||||
|
982CE38F924E4FD387BC2C20 /* BiometricAuthService.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -374,17 +390,17 @@
|
|||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
205678A42FBF9CBC00251C6B /* PBXTargetDependency */ = {
|
205678A42FBF9CBC00251C6B /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 205678922FBF9CBB00251C6B /* MyPass */;
|
target = 205678922FBF9CBB00251C6B /* KeeVault */;
|
||||||
targetProxy = 205678A32FBF9CBC00251C6B /* PBXContainerItemProxy */;
|
targetProxy = 205678A32FBF9CBC00251C6B /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
205678AE2FBF9CBC00251C6B /* PBXTargetDependency */ = {
|
205678AE2FBF9CBC00251C6B /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 205678922FBF9CBB00251C6B /* MyPass */;
|
target = 205678922FBF9CBB00251C6B /* KeeVault */;
|
||||||
targetProxy = 205678AD2FBF9CBC00251C6B /* PBXContainerItemProxy */;
|
targetProxy = 205678AD2FBF9CBC00251C6B /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
2096B43C305EC2B200C2F253 /* PBXTargetDependency */ = {
|
2096B43C305EC2B200C2F253 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
platformFilters = (ios, );
|
platformFilter = ios;
|
||||||
target = 2096B430305EC2B200C2F253 /* AutoFill */;
|
target = 2096B430305EC2B200C2F253 /* AutoFill */;
|
||||||
targetProxy = 2096B43B305EC2B200C2F253 /* PBXContainerItemProxy */;
|
targetProxy = 2096B43B305EC2B200C2F253 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
@@ -425,6 +441,7 @@
|
|||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
@@ -447,6 +464,7 @@
|
|||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
};
|
};
|
||||||
@@ -486,6 +504,7 @@
|
|||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
@@ -501,6 +520,7 @@
|
|||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -510,14 +530,18 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = MyPass/MyPass.entitlements;
|
CODE_SIGN_ENTITLEMENTS = KeeVault/KeeVault.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_USER_SELECTED_FILES = readonly;
|
ENABLE_USER_SELECTED_FILES = readonly;
|
||||||
|
ENTITLEMENTS_REQUIRED = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
|
||||||
|
INFOPLIST_KEY_NSFaceIDUsageDescription = "Unlock your vault with Face ID.";
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||||
@@ -533,10 +557,8 @@
|
|||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault;
|
||||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.antiloop222.MyPass;
|
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.KeeVault;
|
||||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = org.antiloop222.MyPass;
|
|
||||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.MyPass;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
@@ -558,14 +580,18 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = MyPass/MyPass.entitlements;
|
CODE_SIGN_ENTITLEMENTS = KeeVault/KeeVault.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_USER_SELECTED_FILES = readonly;
|
ENABLE_USER_SELECTED_FILES = readonly;
|
||||||
|
ENTITLEMENTS_REQUIRED = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
|
||||||
|
INFOPLIST_KEY_NSFaceIDUsageDescription = "Unlock your vault with Face ID.";
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||||
@@ -581,10 +607,8 @@
|
|||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault;
|
||||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.antiloop222.MyPass;
|
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.KeeVault;
|
||||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = org.antiloop222.MyPass;
|
|
||||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.MyPass;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
@@ -607,11 +631,12 @@
|
|||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassTests;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaulttests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
@@ -622,7 +647,7 @@
|
|||||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MyPass.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MyPass";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KeeVault.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/KeeVault";
|
||||||
XROS_DEPLOYMENT_TARGET = 26.5;
|
XROS_DEPLOYMENT_TARGET = 26.5;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -633,11 +658,12 @@
|
|||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassTests;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaulttests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
@@ -648,7 +674,7 @@
|
|||||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MyPass.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MyPass";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KeeVault.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/KeeVault";
|
||||||
XROS_DEPLOYMENT_TARGET = 26.5;
|
XROS_DEPLOYMENT_TARGET = 26.5;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -658,11 +684,12 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassUITests;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaultuitests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
@@ -673,7 +700,7 @@
|
|||||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_TARGET_NAME = MyPass;
|
TEST_TARGET_NAME = KeeVault;
|
||||||
XROS_DEPLOYMENT_TARGET = 26.5;
|
XROS_DEPLOYMENT_TARGET = 26.5;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -683,11 +710,12 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassUITests;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaultuitests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
@@ -698,7 +726,7 @@
|
|||||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_TARGET_NAME = MyPass;
|
TEST_TARGET_NAME = KeeVault;
|
||||||
XROS_DEPLOYMENT_TARGET = 26.5;
|
XROS_DEPLOYMENT_TARGET = 26.5;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -706,13 +734,16 @@
|
|||||||
2096B43E305EC2B200C2F253 /* Debug */ = {
|
2096B43E305EC2B200C2F253 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoFill/AutoFill.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoFill/AutoFill.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||||
|
ENTITLEMENTS_REQUIRED = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = AutoFill/Info.plist;
|
INFOPLIST_FILE = AutoFill/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = AutoFill;
|
INFOPLIST_KEY_CFBundleDisplayName = AutoFill;
|
||||||
|
INFOPLIST_KEY_NSFaceIDUsageDescription = "Unlock your vault with Face ID.";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -721,7 +752,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass.AutoFill;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault.autofill;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -741,13 +772,16 @@
|
|||||||
2096B43F305EC2B200C2F253 /* Release */ = {
|
2096B43F305EC2B200C2F253 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoFill/AutoFill.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoFill/AutoFill.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||||
|
ENTITLEMENTS_REQUIRED = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = AutoFill/Info.plist;
|
INFOPLIST_FILE = AutoFill/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = AutoFill;
|
INFOPLIST_KEY_CFBundleDisplayName = AutoFill;
|
||||||
|
INFOPLIST_KEY_NSFaceIDUsageDescription = "Unlock your vault with Face ID.";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -756,7 +790,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass.AutoFill;
|
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault.autofill;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -777,7 +811,7 @@
|
|||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
2056788E2FBF9CBB00251C6B /* Build configuration list for PBXProject "MyPass" */ = {
|
2056788E2FBF9CBB00251C6B /* Build configuration list for PBXProject "KeeVault" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
205678B42FBF9CBC00251C6B /* Debug */,
|
205678B42FBF9CBC00251C6B /* Debug */,
|
||||||
@@ -786,7 +820,7 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPass" */ = {
|
205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVault" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
205678B72FBF9CBC00251C6B /* Debug */,
|
205678B72FBF9CBC00251C6B /* Debug */,
|
||||||
@@ -795,7 +829,7 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassTests" */ = {
|
205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultTests" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
205678BA2FBF9CBC00251C6B /* Debug */,
|
205678BA2FBF9CBC00251C6B /* Debug */,
|
||||||
@@ -804,7 +838,7 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassUITests" */ = {
|
205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultUITests" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
205678BD2FBF9CBC00251C6B /* Debug */,
|
205678BD2FBF9CBC00251C6B /* Debug */,
|
||||||
@@ -825,16 +859,16 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCLocalSwiftPackageReference section */
|
/* Begin XCLocalSwiftPackageReference section */
|
||||||
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "MyPassCore" */ = {
|
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "KeeVaultCore" */ = {
|
||||||
isa = XCLocalSwiftPackageReference;
|
isa = XCLocalSwiftPackageReference;
|
||||||
relativePath = MyPassCore;
|
relativePath = KeeVaultCore;
|
||||||
};
|
};
|
||||||
/* End XCLocalSwiftPackageReference section */
|
/* End XCLocalSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
205678DA2FC0EB5200251C6B /* MyPassCore */ = {
|
205678DA2FC0EB5200251C6B /* KeeVaultCore */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = MyPassCore;
|
productName = KeeVaultCore;
|
||||||
};
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
@@ -7,12 +7,12 @@
|
|||||||
<key>AutoFill.xcscheme_^#shared#^_</key>
|
<key>AutoFill.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>4</integer>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>MyPass.xcscheme_^#shared#^_</key>
|
<key>KeeVault.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>3</integer>
|
<integer>1</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.420",
|
||||||
|
"green" : "0.271",
|
||||||
|
"blue" : "0.467",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.651",
|
||||||
|
"green" : "0.498",
|
||||||
|
"blue" : "0.690",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024",
|
||||||
|
"filename" : "icon-1024-light.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"appearances" : [
|
"appearances" : [
|
||||||
@@ -14,7 +15,8 @@
|
|||||||
],
|
],
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024",
|
||||||
|
"filename" : "icon-1024-dark.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"appearances" : [
|
"appearances" : [
|
||||||
@@ -25,57 +27,68 @@
|
|||||||
],
|
],
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024",
|
||||||
|
"filename" : "icon-1024-tinted.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "16x16"
|
"size" : "16x16",
|
||||||
|
"filename" : "icon-mac-16.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "16x16"
|
"size" : "16x16",
|
||||||
|
"filename" : "icon-mac-16@2x.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "32x32"
|
"size" : "32x32",
|
||||||
|
"filename" : "icon-mac-32.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "32x32"
|
"size" : "32x32",
|
||||||
|
"filename" : "icon-mac-32@2x.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "128x128"
|
"size" : "128x128",
|
||||||
|
"filename" : "icon-mac-128.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "128x128"
|
"size" : "128x128",
|
||||||
|
"filename" : "icon-mac-128@2x.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "256x256"
|
"size" : "256x256",
|
||||||
|
"filename" : "icon-mac-256.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "256x256"
|
"size" : "256x256",
|
||||||
|
"filename" : "icon-mac-256@2x.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "512x512"
|
"size" : "512x512",
|
||||||
|
"filename" : "icon-mac-512.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "512x512"
|
"size" : "512x512",
|
||||||
|
"filename" : "icon-mac-512@2x.png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 138 KiB |
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "icon-light.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filename" : "icon-dark.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 138 KiB |
@@ -1,9 +1,4 @@
|
|||||||
{
|
{
|
||||||
"colors" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
"info" : {
|
||||||
"author" : "xcode",
|
"author" : "xcode",
|
||||||
"version" : 1
|
"version" : 1
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.980",
|
||||||
|
"green" : "0.957",
|
||||||
|
"blue" : "0.925",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "0.141",
|
||||||
|
"green" : "0.125",
|
||||||
|
"blue" : "0.094",
|
||||||
|
"alpha" : "1.000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
//
|
//
|
||||||
// ContentView.swift
|
// ContentView.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
// Created by Christophe Vila on 21/05/2026.
|
// Created by Christophe Vila on 21/05/2026.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
import AppKit
|
import AppKit
|
||||||
#endif
|
#endif
|
||||||
@@ -22,6 +22,7 @@ struct ContentView: View {
|
|||||||
VaultRootView(session: session)
|
VaultRootView(session: session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.background(Color("VaultBackground"))
|
||||||
.onReceive(
|
.onReceive(
|
||||||
NotificationCenter.default.publisher(for: sceneBackgroundNotification)
|
NotificationCenter.default.publisher(for: sceneBackgroundNotification)
|
||||||
) { _ in
|
) { _ in
|
||||||
@@ -2,13 +2,15 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||||
|
<true/>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>group.org.antiloop222.mypass</string>
|
<string>group.org.antiloop222.keevault</string>
|
||||||
</array>
|
</array>
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(AppIdentifierPrefix)org.antiloop222.mypass</string>
|
<string>$(AppIdentifierPrefix)org.antiloop222.keevault</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// MyPassApp.swift
|
// KeeVaultApp.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
// Created by Christophe Vila on 21/05/2026.
|
// Created by Christophe Vila on 21/05/2026.
|
||||||
//
|
//
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct MyPassApp: App {
|
struct KeeVaultApp: App {
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView()
|
ContentView()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// BiometricAuthService.swift
|
// BiometricAuthService.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
|
|
||||||
import LocalAuthentication
|
import LocalAuthentication
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// ClipboardService.swift
|
// ClipboardService.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// FileBookmarkService.swift
|
// FileBookmarkService.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -10,7 +10,7 @@ public final class FileBookmarkService {
|
|||||||
private static let key = "kdbxBookmark"
|
private static let key = "kdbxBookmark"
|
||||||
private let defaults: UserDefaults
|
private let defaults: UserDefaults
|
||||||
|
|
||||||
public init(appGroup: String = "group.org.antiloop222.mypass") {
|
public init(appGroup: String = "group.org.antiloop222.keevault") {
|
||||||
defaults = UserDefaults(suiteName: appGroup) ?? .standard
|
defaults = UserDefaults(suiteName: appGroup) ?? .standard
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +33,11 @@ public final class FileBookmarkService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func save(url: URL) throws {
|
public func save(url: URL) throws {
|
||||||
|
// For File Provider-backed URLs (e.g. iCloud Drive), creating a bookmark without
|
||||||
|
// an active security scope produces one that resolves later with
|
||||||
|
// NSCocoaErrorDomain Code=4 ("The file doesn't exist"), even immediately after picking.
|
||||||
|
let accessing = url.startAccessingSecurityScopedResource()
|
||||||
|
defer { if accessing { url.stopAccessingSecurityScopedResource() } }
|
||||||
let data = try url.bookmarkData(
|
let data = try url.bookmarkData(
|
||||||
options: Self.creationOptions,
|
options: Self.creationOptions,
|
||||||
includingResourceValuesForKeys: nil,
|
includingResourceValuesForKeys: nil,
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
// MyPass/ViewModels/EntryEditViewModel.swift
|
// KeeVault/ViewModels/EntryEditViewModel.swift
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class EntryEditViewModel: ObservableObject {
|
final class EntryEditViewModel: ObservableObject {
|
||||||
@@ -20,7 +20,7 @@ final class EntryEditViewModel: ObservableObject {
|
|||||||
private let existingEntry: Entry?
|
private let existingEntry: Entry?
|
||||||
|
|
||||||
/// `groupId` is the target group for a new entry, or the entry's current group when
|
/// `groupId` is the target group for a new entry, or the entry's current group when
|
||||||
/// editing (shown read-only in that case -- MyPass doesn't support re-parenting an
|
/// editing (shown read-only in that case -- KeeVault doesn't support re-parenting an
|
||||||
/// existing entry to a different group yet).
|
/// existing entry to a different group yet).
|
||||||
init(session: VaultSession, groupId: UUID, existing: Entry? = nil) {
|
init(session: VaultSession, groupId: UUID, existing: Entry? = nil) {
|
||||||
self.session = session
|
self.session = session
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class GroupFilterViewModel: ObservableObject {
|
final class GroupFilterViewModel: ObservableObject {
|
||||||
@@ -16,11 +16,11 @@ final class GroupFilterViewModel: ObservableObject {
|
|||||||
self.session = session
|
self.session = session
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSelected(_ group: MyPassCore.Group) -> Bool {
|
func isSelected(_ group: KeeVaultCore.Group) -> Bool {
|
||||||
selectedGroupIds.contains(group.id)
|
selectedGroupIds.contains(group.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggle(_ group: MyPassCore.Group) {
|
func toggle(_ group: KeeVaultCore.Group) {
|
||||||
guard let root = session.database?.root else { return }
|
guard let root = session.database?.root else { return }
|
||||||
selectedGroupIds = GroupSelection.toggling(group.id, in: root, current: selectedGroupIds)
|
selectedGroupIds = GroupSelection.toggling(group.id, in: root, current: selectedGroupIds)
|
||||||
lastToggledGroupId = selectedGroupIds.isEmpty ? nil : group.id
|
lastToggledGroupId = selectedGroupIds.isEmpty ? nil : group.id
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
//
|
//
|
||||||
// UnlockViewModel.swift
|
// UnlockViewModel.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class UnlockViewModel: ObservableObject {
|
final class UnlockViewModel: ObservableObject {
|
||||||
@@ -24,7 +24,7 @@ final class UnlockViewModel: ObservableObject {
|
|||||||
init(
|
init(
|
||||||
session: VaultSession,
|
session: VaultSession,
|
||||||
bookmarkService: FileBookmarkService = .init(),
|
bookmarkService: FileBookmarkService = .init(),
|
||||||
keychainStore: KeychainStore = .init(accessGroup: "org.antiloop222.mypass"),
|
keychainStore: KeychainStore = .init(accessGroup: "F2KB6W8N4R.org.antiloop222.keevault"),
|
||||||
biometricService: BiometricAuthService = .init()
|
biometricService: BiometricAuthService = .init()
|
||||||
) {
|
) {
|
||||||
self.session = session
|
self.session = session
|
||||||
@@ -44,7 +44,7 @@ final class UnlockViewModel: ObservableObject {
|
|||||||
isUnlocking = true
|
isUnlocking = true
|
||||||
errorMessage = nil
|
errorMessage = nil
|
||||||
do {
|
do {
|
||||||
try await biometricService.authenticate(reason: "Unlock MyPass")
|
try await biometricService.authenticate(reason: "Unlock KeeVault")
|
||||||
let storedPassword = try keychainStore.load(for: keychainAccount)
|
let storedPassword = try keychainStore.load(for: keychainAccount)
|
||||||
let url = try bookmarkService.resolveURL()
|
let url = try bookmarkService.resolveURL()
|
||||||
defer { bookmarkService.stopAccess(url: url) }
|
defer { bookmarkService.stopAccess(url: url) }
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// MyPass/ViewModels/VaultViewModel.swift
|
// KeeVault/ViewModels/VaultViewModel.swift
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class VaultViewModel: ObservableObject {
|
final class VaultViewModel: ObservableObject {
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
//
|
//
|
||||||
// EntryDetailView.swift
|
// EntryDetailView.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Combine
|
import Combine
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
struct EntryDetailView: View {
|
struct EntryDetailView: View {
|
||||||
let entry: Entry
|
let entry: Entry
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// MyPass/Views/EntryEditView.swift
|
// KeeVault/Views/EntryEditView.swift
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
struct EntryEditView: View {
|
struct EntryEditView: View {
|
||||||
@ObservedObject var vm: EntryEditViewModel
|
@ObservedObject var vm: EntryEditViewModel
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
// MyPass/Views/EntryListView.swift
|
// KeeVault/Views/EntryListView.swift
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
struct EntryListView: View {
|
struct EntryListView: View {
|
||||||
@ObservedObject var vm: VaultViewModel
|
@ObservedObject var vm: VaultViewModel
|
||||||
@State private var isSearching = false
|
|
||||||
@State private var showAddEntry = false
|
@State private var showAddEntry = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -24,17 +23,11 @@ struct EntryListView: View {
|
|||||||
offsets.map { vm.displayedEntries[$0] }.forEach(vm.deleteEntry)
|
offsets.map { vm.displayedEntries[$0] }.forEach(vm.deleteEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("MyPass")
|
.scrollContentBackground(.hidden)
|
||||||
.searchable(text: $vm.searchQuery, isPresented: $isSearching, prompt: "Search entries…")
|
.background(Color("VaultBackground"))
|
||||||
|
.navigationTitle("KeeVault")
|
||||||
|
.searchable(text: $vm.searchQuery, prompt: "Search entries…")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
|
||||||
Button {
|
|
||||||
if isSearching { vm.searchQuery = "" }
|
|
||||||
isSearching.toggle()
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "magnifyingglass")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button { showAddEntry = true } label: {
|
Button { showAddEntry = true } label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
@@ -1,35 +1,39 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
struct GroupFilterView: View {
|
struct GroupFilterView: View {
|
||||||
@ObservedObject var vm: GroupFilterViewModel
|
@ObservedObject var vm: GroupFilterViewModel
|
||||||
let rootGroup: MyPassCore.Group
|
let rootGroup: KeeVaultCore.Group
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
if !vm.selectedGroupIds.isEmpty {
|
|
||||||
Button("Clear Filter", action: vm.clear)
|
|
||||||
}
|
|
||||||
GroupFilterNode(group: rootGroup, vm: vm)
|
GroupFilterNode(group: rootGroup, vm: vm)
|
||||||
}
|
}
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.background(Color("VaultBackground"))
|
||||||
.navigationTitle("Groups")
|
.navigationTitle("Groups")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
Button("Clear", action: vm.clear)
|
||||||
|
.disabled(vm.selectedGroupIds.isEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct GroupFilterNode: View {
|
private struct GroupFilterNode: View {
|
||||||
let group: MyPassCore.Group
|
let group: KeeVaultCore.Group
|
||||||
@ObservedObject var vm: GroupFilterViewModel
|
@ObservedObject var vm: GroupFilterViewModel
|
||||||
|
var depth: Int = 0
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if group.subgroups.isEmpty {
|
// A plain SwiftUI.Group (not a container view) flattens its children into
|
||||||
|
// separate List rows, so the whole subtree renders always-expanded --
|
||||||
|
// no DisclosureGroup, no tap-to-reveal -- active filters are visible at a glance.
|
||||||
|
SwiftUI.Group {
|
||||||
row
|
row
|
||||||
} else {
|
|
||||||
DisclosureGroup {
|
|
||||||
ForEach(group.subgroups) { sub in
|
ForEach(group.subgroups) { sub in
|
||||||
GroupFilterNode(group: sub, vm: vm)
|
GroupFilterNode(group: sub, vm: vm, depth: depth + 1)
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
row
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,5 +49,6 @@ private struct GroupFilterNode: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
.padding(.leading, CGFloat(depth) * 16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
//
|
//
|
||||||
// UnlockView.swift
|
// UnlockView.swift
|
||||||
// MyPass
|
// KeeVault
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
import MyPassCore
|
import KeeVaultCore
|
||||||
|
|
||||||
struct UnlockView: View {
|
struct UnlockView: View {
|
||||||
@ObservedObject var vm: UnlockViewModel
|
@ObservedObject var vm: UnlockViewModel
|
||||||
@@ -13,10 +13,11 @@ struct UnlockView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 24) {
|
VStack(spacing: 24) {
|
||||||
Spacer()
|
Spacer()
|
||||||
Image(systemName: "lock.shield.fill")
|
Image("AppIconImage")
|
||||||
.font(.system(size: 64))
|
.resizable()
|
||||||
.foregroundStyle(.tint)
|
.frame(width: 88, height: 88)
|
||||||
Text("MyPass")
|
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
|
||||||
|
Text("KeeVault")
|
||||||
.font(.largeTitle.bold())
|
.font(.largeTitle.bold())
|
||||||
|
|
||||||
if vm.hasVault {
|
if vm.hasVault {
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>SchemeUserState</key>
|
||||||
|
<dict>
|
||||||
|
<key>KeeVaultCore.xcscheme_^#shared#^_</key>
|
||||||
|
<dict>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -2,24 +2,24 @@
|
|||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "MyPassCore",
|
name: "KeeVaultCore",
|
||||||
platforms: [.iOS(.v17), .macOS(.v14)],
|
platforms: [.iOS(.v17), .macOS(.v14)],
|
||||||
products: [
|
products: [
|
||||||
.library(name: "MyPassCore", targets: ["MyPassCore"]),
|
.library(name: "KeeVaultCore", targets: ["KeeVaultCore"]),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(path: "../Vendor/KeePassKit"),
|
.package(path: "../Vendor/KeePassKit"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "MyPassCore",
|
name: "KeeVaultCore",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.product(name: "KeePassKit", package: "KeePassKit"),
|
.product(name: "KeePassKit", package: "KeePassKit"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MyPassCoreTests",
|
name: "KeeVaultCoreTests",
|
||||||
dependencies: ["MyPassCore"],
|
dependencies: ["KeeVaultCore"],
|
||||||
resources: [.copy("Fixtures")]
|
resources: [.copy("Fixtures")]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -25,7 +25,14 @@ public enum CredentialMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static func host(from urlString: String) -> String? {
|
private static func host(from urlString: String) -> String? {
|
||||||
URL(string: urlString)?.host
|
// KDBX entries and AutoFill service identifiers commonly omit the scheme
|
||||||
|
// (e.g. "allocine.fr"), but URL(string:) only populates `.host` when an
|
||||||
|
// authority component ("//") is present -- without it, the whole string
|
||||||
|
// is parsed as a relative path and `.host` is nil.
|
||||||
|
if let host = URL(string: urlString)?.host, !host.isEmpty {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
return URL(string: "https://" + urlString)?.host
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func hostsMatch(_ a: String, _ b: String) -> Bool {
|
private static func hostsMatch(_ a: String, _ b: String) -> Bool {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Maps KeePass's standard icon palette (indices 0-68) to an approximate SF Symbol.
|
/// Maps KeePass's standard icon palette (indices 0-68) to an approximate SF Symbol.
|
||||||
/// This is a best-effort visual match, not the actual KeePass icon artwork -- MyPass's
|
/// This is a best-effort visual match, not the actual KeePass icon artwork -- KeeVault's
|
||||||
/// data model only stores the numeric iconIndex, not custom icon images.
|
/// data model only stores the numeric iconIndex, not custom icon images.
|
||||||
public enum KeePassIconMapper {
|
public enum KeePassIconMapper {
|
||||||
private static let symbolsByIndex: [Int: String] = [
|
private static let symbolsByIndex: [Int: String] = [
|
||||||
@@ -3,7 +3,7 @@ import KeePassKit
|
|||||||
|
|
||||||
enum KDBXMapper {
|
enum KDBXMapper {
|
||||||
|
|
||||||
// MARK: - KeePassKit → MyPassCore
|
// MARK: - KeePassKit → KeeVaultCore
|
||||||
|
|
||||||
static func database(from tree: KPKTree) -> KDBXDatabase {
|
static func database(from tree: KPKTree) -> KDBXDatabase {
|
||||||
let meta = DatabaseMetadata(
|
let meta = DatabaseMetadata(
|
||||||
@@ -67,7 +67,7 @@ enum KDBXMapper {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MyPassCore → KeePassKit
|
// MARK: - KeeVaultCore → KeePassKit
|
||||||
|
|
||||||
static func tree(from db: KDBXDatabase) -> KPKTree {
|
static func tree(from db: KDBXDatabase) -> KPKTree {
|
||||||
let tree = KPKTree()
|
let tree = KPKTree()
|
||||||
@@ -129,7 +129,7 @@ enum KDBXMapper {
|
|||||||
var components = URLComponents()
|
var components = URLComponents()
|
||||||
components.scheme = "otpauth"
|
components.scheme = "otpauth"
|
||||||
components.host = "totp"
|
components.host = "totp"
|
||||||
components.path = "/MyPass"
|
components.path = "/KeeVault"
|
||||||
components.queryItems = [
|
components.queryItems = [
|
||||||
URLQueryItem(name: "secret", value: config.secret),
|
URLQueryItem(name: "secret", value: config.secret),
|
||||||
URLQueryItem(name: "period", value: String(config.period)),
|
URLQueryItem(name: "period", value: String(config.period)),
|
||||||
@@ -5,7 +5,7 @@ public struct KeychainStore {
|
|||||||
private let accessGroup: String
|
private let accessGroup: String
|
||||||
private let service: String
|
private let service: String
|
||||||
|
|
||||||
public init(accessGroup: String, service: String = "org.antiloop222.mypass") {
|
public init(accessGroup: String, service: String = "org.antiloop222.keevault") {
|
||||||
self.accessGroup = accessGroup
|
self.accessGroup = accessGroup
|
||||||
self.service = service
|
self.service = service
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import MyPassCore
|
@testable import KeeVaultCore
|
||||||
|
|
||||||
final class CredentialMatcherTests: XCTestCase {
|
final class CredentialMatcherTests: XCTestCase {
|
||||||
func makeEntry(url: String) -> Entry {
|
func makeEntry(url: String) -> Entry {
|
||||||
@@ -31,6 +31,21 @@ final class CredentialMatcherTests: XCTestCase {
|
|||||||
XCTAssertFalse(CredentialMatcher.matches(entry: e, serviceIdentifier: "https://github.com"))
|
XCTAssertFalse(CredentialMatcher.matches(entry: e, serviceIdentifier: "https://github.com"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_bareDomainSubdomainMatch() {
|
||||||
|
let e = makeEntry(url: "allocine.fr")
|
||||||
|
XCTAssertTrue(CredentialMatcher.matches(entry: e, serviceIdentifier: "mon.allocine.fr"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_bareDomainExactMatch() {
|
||||||
|
let e = makeEntry(url: "allocine.fr")
|
||||||
|
XCTAssertTrue(CredentialMatcher.matches(entry: e, serviceIdentifier: "allocine.fr"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_bareDomainDifferentDomain_noMatch() {
|
||||||
|
let e = makeEntry(url: "allocine.fr")
|
||||||
|
XCTAssertFalse(CredentialMatcher.matches(entry: e, serviceIdentifier: "notallocine.fr"))
|
||||||
|
}
|
||||||
|
|
||||||
func test_filter_suggestedAndRest() {
|
func test_filter_suggestedAndRest() {
|
||||||
let entries = [
|
let entries = [
|
||||||
makeEntry(url: "https://github.com"),
|
makeEntry(url: "https://github.com"),
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import MyPassCore
|
@testable import KeeVaultCore
|
||||||
|
|
||||||
final class GroupSelectionTests: XCTestCase {
|
final class GroupSelectionTests: XCTestCase {
|
||||||
private func makeSampleTree() -> (root: Group, work: Group, email: Group, personal: Group) {
|
private func makeSampleTree() -> (root: Group, work: Group, email: Group, personal: Group) {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import MyPassCore
|
@testable import KeeVaultCore
|
||||||
|
|
||||||
final class GroupTreeTests: XCTestCase {
|
final class GroupTreeTests: XCTestCase {
|
||||||
private func makeSampleTree() -> (root: Group, work: Group, email: Group) {
|
private func makeSampleTree() -> (root: Group, work: Group, email: Group) {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import MyPassCore
|
@testable import KeeVaultCore
|
||||||
|
|
||||||
final class KDBXDocumentTests: XCTestCase {
|
final class KDBXDocumentTests: XCTestCase {
|
||||||
var fixtureURL: URL!
|
var fixtureURL: URL!
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import MyPassCore
|
@testable import KeeVaultCore
|
||||||
|
|
||||||
final class KeePassIconMapperTests: XCTestCase {
|
final class KeePassIconMapperTests: XCTestCase {
|
||||||
func test_knownIndex_returnsMappedSymbol() {
|
func test_knownIndex_returnsMappedSymbol() {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import MyPassCore
|
@testable import KeeVaultCore
|
||||||
|
|
||||||
final class ProtectedStringTests: XCTestCase {
|
final class ProtectedStringTests: XCTestCase {
|
||||||
func test_reveal_returnsOriginalValue() {
|
func test_reveal_returnsOriginalValue() {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import MyPassCore
|
@testable import KeeVaultCore
|
||||||
|
|
||||||
final class TOTPGeneratorTests: XCTestCase {
|
final class TOTPGeneratorTests: XCTestCase {
|
||||||
// RFC 6238 Section 8 test vectors for SHA-1
|
// RFC 6238 Section 8 test vectors for SHA-1
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
//
|
//
|
||||||
// MyPassTests.swift
|
// KeeVaultTests.swift
|
||||||
// MyPassTests
|
// KeeVaultTests
|
||||||
//
|
//
|
||||||
// Created by Christophe Vila on 21/05/2026.
|
// Created by Christophe Vila on 21/05/2026.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
struct MyPassTests {
|
struct KeeVaultTests {
|
||||||
|
|
||||||
@Test func example() async throws {
|
@Test func example() async throws {
|
||||||
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
//
|
//
|
||||||
// MyPassUITests.swift
|
// KeeVaultUITests.swift
|
||||||
// MyPassUITests
|
// KeeVaultUITests
|
||||||
//
|
//
|
||||||
// Created by Christophe Vila on 21/05/2026.
|
// Created by Christophe Vila on 21/05/2026.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
final class MyPassUITests: XCTestCase {
|
final class KeeVaultUITests: XCTestCase {
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
//
|
//
|
||||||
// MyPassUITestsLaunchTests.swift
|
// KeeVaultUITestsLaunchTests.swift
|
||||||
// MyPassUITests
|
// KeeVaultUITests
|
||||||
//
|
//
|
||||||
// Created by Christophe Vila on 21/05/2026.
|
// Created by Christophe Vila on 21/05/2026.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
final class MyPassUITestsLaunchTests: XCTestCase {
|
final class KeeVaultUITestsLaunchTests: XCTestCase {
|
||||||
|
|
||||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||||
true
|
true
|
||||||
@@ -152,7 +152,7 @@ NavigationSplitView(columnVisibility:)
|
|||||||
`NavigationSplitView` is used on **both** platforms instead of maintaining separate iOS/macOS navigation code. On iPhone-width layouts it automatically collapses the sidebar into an overlay; on iPad/Mac it can sit persistently alongside the detail column. This behavior is built into the component — no platform-specific branching needed for it.
|
`NavigationSplitView` is used on **both** platforms instead of maintaining separate iOS/macOS navigation code. On iPhone-width layouts it automatically collapses the sidebar into an overlay; on iPad/Mac it can sit persistently alongside the detail column. This behavior is built into the component — no platform-specific branching needed for it.
|
||||||
|
|
||||||
- **Hamburger button** (leading toolbar item, `"line.3.horizontal"`) toggles `columnVisibility` between `.all` and `.detailOnly`, showing/hiding the sidebar.
|
- **Hamburger button** (leading toolbar item, `"line.3.horizontal"`) toggles `columnVisibility` between `.all` and `.detailOnly`, showing/hiding the sidebar.
|
||||||
- **Search icon** (trailing toolbar item, `"magnifyingglass"`) toggles a `.searchable()` bar over `EntryListView`; tapping again hides it and clears the query.
|
- **Search** is always visible via `.searchable()` over `EntryListView` (no toggle icon -- changed 2026-09-19 after hands-on use showed the extra tap added no value over an always-present search field).
|
||||||
- Swipe-to-delete on entry rows; toolbar `+` button opens `EntryEditView` for a new entry (see "Add-entry flow" below).
|
- Swipe-to-delete on entry rows; toolbar `+` button opens `EntryEditView` for a new entry (see "Add-entry flow" below).
|
||||||
- Tapping a password field in `EntryDetailView` copies to clipboard (clears after 30 s).
|
- Tapping a password field in `EntryDetailView` copies to clipboard (clears after 30 s).
|
||||||
- TOTP code displays with a countdown ring and refreshes automatically.
|
- TOTP code displays with a countdown ring and refreshes automatically.
|
||||||
|
|||||||