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"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.antiloop222.mypass</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)org.antiloop222.mypass</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.antiloop222.keevault</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)org.antiloop222.keevault</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -17,36 +17,7 @@
|
||||
<view key="view" contentMode="scaleToFill" id="BuU-Ak-iZz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
</view>
|
||||
</viewController>
|
||||
|
||||
@@ -6,53 +6,45 @@
|
||||
//
|
||||
|
||||
import AuthenticationServices
|
||||
import SwiftUI
|
||||
import KeeVaultCore
|
||||
|
||||
class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||
final class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||
|
||||
/*
|
||||
Prepare your UI to list available credentials for the user to choose from. The items in
|
||||
'serviceIdentifiers' describe the service the user is logging in to, so your extension can
|
||||
prioritize the most relevant credentials in the list.
|
||||
*/
|
||||
private let session = VaultSession()
|
||||
private let bookmarkService = FileBookmarkService()
|
||||
private let keychainStore = KeychainStore(accessGroup: "F2KB6W8N4R.org.antiloop222.keevault")
|
||||
private let biometricService = BiometricAuthService()
|
||||
|
||||
// Called when the user selects KeeVault from the QuickType bar.
|
||||
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
||||
let ids = serviceIdentifiers.map(\.identifier)
|
||||
showUI(serviceIdentifiers: ids)
|
||||
}
|
||||
|
||||
/*
|
||||
Implement this method if your extension supports showing credentials in the QuickType bar.
|
||||
When the user selects a credential from your app, this method will be called with the
|
||||
ASPasswordCredentialIdentity your app has previously saved to the ASCredentialIdentityStore.
|
||||
Provide the password by completing the extension request with the associated ASPasswordCredential.
|
||||
If using the credential would require showing custom UI for authenticating the user, cancel
|
||||
the request with error code ASExtensionError.userInteractionRequired.
|
||||
|
||||
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
|
||||
let databaseIsUnlocked = true
|
||||
if (databaseIsUnlocked) {
|
||||
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
|
||||
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
|
||||
} else {
|
||||
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
|
||||
}
|
||||
private func showUI(serviceIdentifiers: [String]) {
|
||||
let rootView = ExtensionRootView(
|
||||
session: session,
|
||||
serviceIdentifiers: serviceIdentifiers,
|
||||
bookmarkService: bookmarkService,
|
||||
keychainStore: keychainStore,
|
||||
biometricService: biometricService,
|
||||
onSelect: { [weak self] (entry: Entry) in
|
||||
let credential = ASPasswordCredential(
|
||||
user: entry.username,
|
||||
password: entry.password.reveal()
|
||||
)
|
||||
self?.extensionContext.completeRequest(withSelectedCredential: credential, completionHandler: nil)
|
||||
},
|
||||
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>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>ASCredentialProviderExtensionCapabilities</key>
|
||||
<dict>
|
||||
<key>ProvidesPasswords</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<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 = {
|
||||
|
||||
/* 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 */; };
|
||||
2096B43D305EC2B200C2F253 /* AutoFill.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 2096B431305EC2B200C2F253 /* AutoFill.appex */; platformFilters = (ios, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
67DE2FCFC21FD6DF5E761C87 /* MyPassCore in Frameworks */ = {isa = PBXBuildFile; productRef = 205678DA2FC0EB5200251C6B /* MyPassCore */; };
|
||||
2096B43D305EC2B200C2F253 /* AutoFill.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 2096B431305EC2B200C2F253 /* AutoFill.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -19,14 +21,14 @@
|
||||
containerPortal = 2056788B2FBF9CBB00251C6B /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 205678922FBF9CBB00251C6B;
|
||||
remoteInfo = MyPass;
|
||||
remoteInfo = KeeVault;
|
||||
};
|
||||
205678AD2FBF9CBC00251C6B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 2056788B2FBF9CBB00251C6B /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 205678922FBF9CBB00251C6B;
|
||||
remoteInfo = MyPass;
|
||||
remoteInfo = KeeVault;
|
||||
};
|
||||
2096B43B305EC2B200C2F253 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
@@ -52,11 +54,13 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
205678932FBF9CBB00251C6B /* MyPass.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyPass.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
205678A22FBF9CBC00251C6B /* MyPassTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MyPassTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
205678AC2FBF9CBC00251C6B /* MyPassUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MyPassUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
205678932FBF9CBB00251C6B /* KeeVault.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeeVault.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
205678A22FBF9CBC00251C6B /* KeeVaultTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeeVaultTests.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; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
@@ -70,19 +74,19 @@
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
205678952FBF9CBB00251C6B /* MyPass */ = {
|
||||
205678952FBF9CBB00251C6B /* KeeVault */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = MyPass;
|
||||
path = KeeVault;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
205678A52FBF9CBC00251C6B /* MyPassTests */ = {
|
||||
205678A52FBF9CBC00251C6B /* KeeVaultTests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = MyPassTests;
|
||||
path = KeeVaultTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
205678AF2FBF9CBC00251C6B /* MyPassUITests */ = {
|
||||
205678AF2FBF9CBC00251C6B /* KeeVaultUITests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = MyPassUITests;
|
||||
path = KeeVaultUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2096B433305EC2B200C2F253 /* AutoFill */ = {
|
||||
@@ -100,7 +104,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
205678DB2FC0EB5200251C6B /* MyPassCore in Frameworks */,
|
||||
205678DB2FC0EB5200251C6B /* KeeVaultCore in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -123,20 +127,30 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2096B432305EC2B200C2F253 /* AuthenticationServices.framework in Frameworks */,
|
||||
67DE2FCFC21FD6DF5E761C87 /* MyPassCore in Frameworks */,
|
||||
67DE2FCFC21FD6DF5E761C87 /* KeeVaultCore in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1A2B3C4D5E6F7A8B9C0D1E2F /* AutoFill Shared Services */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ADBBB9F1BA62473294D2B5B5 /* FileBookmarkService.swift */,
|
||||
D6C7DFA4A59640D7BF6B4960 /* BiometricAuthService.swift */,
|
||||
);
|
||||
name = "AutoFill Shared Services";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2056788A2FBF9CBB00251C6B = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
205678952FBF9CBB00251C6B /* MyPass */,
|
||||
205678A52FBF9CBC00251C6B /* MyPassTests */,
|
||||
205678AF2FBF9CBC00251C6B /* MyPassUITests */,
|
||||
205678952FBF9CBB00251C6B /* KeeVault */,
|
||||
205678A52FBF9CBC00251C6B /* KeeVaultTests */,
|
||||
205678AF2FBF9CBC00251C6B /* KeeVaultUITests */,
|
||||
2096B433305EC2B200C2F253 /* AutoFill */,
|
||||
1A2B3C4D5E6F7A8B9C0D1E2F /* AutoFill Shared Services */,
|
||||
205678E22FC0F52A00251C6B /* Frameworks */,
|
||||
205678942FBF9CBB00251C6B /* Products */,
|
||||
);
|
||||
@@ -145,9 +159,9 @@
|
||||
205678942FBF9CBB00251C6B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
205678932FBF9CBB00251C6B /* MyPass.app */,
|
||||
205678A22FBF9CBC00251C6B /* MyPassTests.xctest */,
|
||||
205678AC2FBF9CBC00251C6B /* MyPassUITests.xctest */,
|
||||
205678932FBF9CBB00251C6B /* KeeVault.app */,
|
||||
205678A22FBF9CBC00251C6B /* KeeVaultTests.xctest */,
|
||||
205678AC2FBF9CBC00251C6B /* KeeVaultUITests.xctest */,
|
||||
2096B431305EC2B200C2F253 /* AutoFill.appex */,
|
||||
);
|
||||
name = Products;
|
||||
@@ -164,9 +178,9 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
205678922FBF9CBB00251C6B /* MyPass */ = {
|
||||
205678922FBF9CBB00251C6B /* KeeVault */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPass" */;
|
||||
buildConfigurationList = 205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVault" */;
|
||||
buildPhases = (
|
||||
2056788F2FBF9CBB00251C6B /* Sources */,
|
||||
205678902FBF9CBB00251C6B /* Frameworks */,
|
||||
@@ -179,19 +193,19 @@
|
||||
2096B43C305EC2B200C2F253 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
205678952FBF9CBB00251C6B /* MyPass */,
|
||||
205678952FBF9CBB00251C6B /* KeeVault */,
|
||||
);
|
||||
name = MyPass;
|
||||
name = KeeVault;
|
||||
packageProductDependencies = (
|
||||
205678DA2FC0EB5200251C6B /* MyPassCore */,
|
||||
205678DA2FC0EB5200251C6B /* KeeVaultCore */,
|
||||
);
|
||||
productName = MyPass;
|
||||
productReference = 205678932FBF9CBB00251C6B /* MyPass.app */;
|
||||
productName = KeeVault;
|
||||
productReference = 205678932FBF9CBB00251C6B /* KeeVault.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
205678A12FBF9CBC00251C6B /* MyPassTests */ = {
|
||||
205678A12FBF9CBC00251C6B /* KeeVaultTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassTests" */;
|
||||
buildConfigurationList = 205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultTests" */;
|
||||
buildPhases = (
|
||||
2056789E2FBF9CBC00251C6B /* Sources */,
|
||||
2056789F2FBF9CBC00251C6B /* Frameworks */,
|
||||
@@ -203,18 +217,18 @@
|
||||
205678A42FBF9CBC00251C6B /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
205678A52FBF9CBC00251C6B /* MyPassTests */,
|
||||
205678A52FBF9CBC00251C6B /* KeeVaultTests */,
|
||||
);
|
||||
name = MyPassTests;
|
||||
name = KeeVaultTests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = MyPassTests;
|
||||
productReference = 205678A22FBF9CBC00251C6B /* MyPassTests.xctest */;
|
||||
productName = KeeVaultTests;
|
||||
productReference = 205678A22FBF9CBC00251C6B /* KeeVaultTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
205678AB2FBF9CBC00251C6B /* MyPassUITests */ = {
|
||||
205678AB2FBF9CBC00251C6B /* KeeVaultUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassUITests" */;
|
||||
buildConfigurationList = 205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultUITests" */;
|
||||
buildPhases = (
|
||||
205678A82FBF9CBC00251C6B /* Sources */,
|
||||
205678A92FBF9CBC00251C6B /* Frameworks */,
|
||||
@@ -226,13 +240,13 @@
|
||||
205678AE2FBF9CBC00251C6B /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
205678AF2FBF9CBC00251C6B /* MyPassUITests */,
|
||||
205678AF2FBF9CBC00251C6B /* KeeVaultUITests */,
|
||||
);
|
||||
name = MyPassUITests;
|
||||
name = KeeVaultUITests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = MyPassUITests;
|
||||
productReference = 205678AC2FBF9CBC00251C6B /* MyPassUITests.xctest */;
|
||||
productName = KeeVaultUITests;
|
||||
productReference = 205678AC2FBF9CBC00251C6B /* KeeVaultUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
2096B430305EC2B200C2F253 /* AutoFill */ = {
|
||||
@@ -252,7 +266,7 @@
|
||||
);
|
||||
name = AutoFill;
|
||||
packageProductDependencies = (
|
||||
205678DA2FC0EB5200251C6B /* MyPassCore */,
|
||||
205678DA2FC0EB5200251C6B /* KeeVaultCore */,
|
||||
);
|
||||
productName = AutoFill;
|
||||
productReference = 2096B431305EC2B200C2F253 /* AutoFill.appex */;
|
||||
@@ -266,7 +280,7 @@
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 2660;
|
||||
LastUpgradeCheck = 2650;
|
||||
LastUpgradeCheck = 2660;
|
||||
TargetAttributes = {
|
||||
205678922FBF9CBB00251C6B = {
|
||||
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;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@@ -294,16 +308,16 @@
|
||||
mainGroup = 2056788A2FBF9CBB00251C6B;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
packageReferences = (
|
||||
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "MyPassCore" */,
|
||||
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "KeeVaultCore" */,
|
||||
);
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 205678942FBF9CBB00251C6B /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
205678922FBF9CBB00251C6B /* MyPass */,
|
||||
205678A12FBF9CBC00251C6B /* MyPassTests */,
|
||||
205678AB2FBF9CBC00251C6B /* MyPassUITests */,
|
||||
205678922FBF9CBB00251C6B /* KeeVault */,
|
||||
205678A12FBF9CBC00251C6B /* KeeVaultTests */,
|
||||
205678AB2FBF9CBC00251C6B /* KeeVaultUITests */,
|
||||
2096B430305EC2B200C2F253 /* AutoFill */,
|
||||
);
|
||||
};
|
||||
@@ -366,6 +380,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
EABAC2B148F845C8A20EC2CC /* FileBookmarkService.swift in Sources */,
|
||||
982CE38F924E4FD387BC2C20 /* BiometricAuthService.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -374,17 +390,17 @@
|
||||
/* Begin PBXTargetDependency section */
|
||||
205678A42FBF9CBC00251C6B /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 205678922FBF9CBB00251C6B /* MyPass */;
|
||||
target = 205678922FBF9CBB00251C6B /* KeeVault */;
|
||||
targetProxy = 205678A32FBF9CBC00251C6B /* PBXContainerItemProxy */;
|
||||
};
|
||||
205678AE2FBF9CBC00251C6B /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 205678922FBF9CBB00251C6B /* MyPass */;
|
||||
target = 205678922FBF9CBB00251C6B /* KeeVault */;
|
||||
targetProxy = 205678AD2FBF9CBC00251C6B /* PBXContainerItemProxy */;
|
||||
};
|
||||
2096B43C305EC2B200C2F253 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilters = (ios, );
|
||||
platformFilter = ios;
|
||||
target = 2096B430305EC2B200C2F253 /* AutoFill */;
|
||||
targetProxy = 2096B43B305EC2B200C2F253 /* PBXContainerItemProxy */;
|
||||
};
|
||||
@@ -425,6 +441,7 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
@@ -447,6 +464,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
@@ -486,6 +504,7 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -501,6 +520,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
};
|
||||
name = Release;
|
||||
@@ -510,14 +530,18 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = MyPass/MyPass.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = KeeVault/KeeVault.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
ENTITLEMENTS_REQUIRED = 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=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||
@@ -533,10 +557,8 @@
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.antiloop222.MyPass;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = org.antiloop222.MyPass;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.MyPass;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.KeeVault;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = auto;
|
||||
@@ -558,14 +580,18 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = MyPass/MyPass.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = KeeVault/KeeVault.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
ENTITLEMENTS_REQUIRED = 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=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||
@@ -581,10 +607,8 @@
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.antiloop222.MyPass;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = org.antiloop222.MyPass;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.MyPass;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=xros*]" = org.antiloop222.KeeVault;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = auto;
|
||||
@@ -607,11 +631,12 @@
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaulttests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
@@ -622,7 +647,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
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;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -633,11 +658,12 @@
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaulttests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
@@ -648,7 +674,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
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;
|
||||
};
|
||||
name = Release;
|
||||
@@ -658,11 +684,12 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassUITests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaultuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
@@ -673,7 +700,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = MyPass;
|
||||
TEST_TARGET_NAME = KeeVault;
|
||||
XROS_DEPLOYMENT_TARGET = 26.5;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -683,11 +710,12 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPassUITests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevaultuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
@@ -698,7 +726,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = MyPass;
|
||||
TEST_TARGET_NAME = KeeVault;
|
||||
XROS_DEPLOYMENT_TARGET = 26.5;
|
||||
};
|
||||
name = Release;
|
||||
@@ -706,13 +734,16 @@
|
||||
2096B43E305EC2B200C2F253 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoFill/AutoFill.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||
ENTITLEMENTS_REQUIRED = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = AutoFill/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AutoFill;
|
||||
INFOPLIST_KEY_NSFaceIDUsageDescription = "Unlock your vault with Face ID.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -721,7 +752,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass.AutoFill;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault.autofill;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -741,13 +772,16 @@
|
||||
2096B43F305EC2B200C2F253 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoFill/AutoFill.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = F2KB6W8N4R;
|
||||
ENTITLEMENTS_REQUIRED = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = AutoFill/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AutoFill;
|
||||
INFOPLIST_KEY_NSFaceIDUsageDescription = "Unlock your vault with Face ID.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -756,7 +790,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.MyPass.AutoFill;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.antiloop222.keevault.autofill;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -777,7 +811,7 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
2056788E2FBF9CBB00251C6B /* Build configuration list for PBXProject "MyPass" */ = {
|
||||
2056788E2FBF9CBB00251C6B /* Build configuration list for PBXProject "KeeVault" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
205678B42FBF9CBC00251C6B /* Debug */,
|
||||
@@ -786,7 +820,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPass" */ = {
|
||||
205678B62FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVault" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
205678B72FBF9CBC00251C6B /* Debug */,
|
||||
@@ -795,7 +829,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassTests" */ = {
|
||||
205678B92FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
205678BA2FBF9CBC00251C6B /* Debug */,
|
||||
@@ -804,7 +838,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "MyPassUITests" */ = {
|
||||
205678BC2FBF9CBC00251C6B /* Build configuration list for PBXNativeTarget "KeeVaultUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
205678BD2FBF9CBC00251C6B /* Debug */,
|
||||
@@ -825,16 +859,16 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "MyPassCore" */ = {
|
||||
205678D92FC0EB5200251C6B /* XCLocalSwiftPackageReference "KeeVaultCore" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = MyPassCore;
|
||||
relativePath = KeeVaultCore;
|
||||
};
|
||||
/* End XCLocalSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
205678DA2FC0EB5200251C6B /* MyPassCore */ = {
|
||||
205678DA2FC0EB5200251C6B /* KeeVaultCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = MyPassCore;
|
||||
productName = KeeVaultCore;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
@@ -7,12 +7,12 @@
|
||||
<key>AutoFill.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>MyPass.xcscheme_^#shared#^_</key>
|
||||
<key>KeeVault.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
<integer>1</integer>
|
||||
</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",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
"size" : "1024x1024",
|
||||
"filename" : "icon-1024-light.png"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
@@ -14,7 +15,8 @@
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
"size" : "1024x1024",
|
||||
"filename" : "icon-1024-dark.png"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
@@ -25,57 +27,68 @@
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
"size" : "1024x1024",
|
||||
"filename" : "icon-1024-tinted.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
"size" : "16x16",
|
||||
"filename" : "icon-mac-16.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
"size" : "16x16",
|
||||
"filename" : "icon-mac-16@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
"size" : "32x32",
|
||||
"filename" : "icon-mac-32.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
"size" : "32x32",
|
||||
"filename" : "icon-mac-32@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
"size" : "128x128",
|
||||
"filename" : "icon-mac-128.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
"size" : "128x128",
|
||||
"filename" : "icon-mac-128@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
"size" : "256x256",
|
||||
"filename" : "icon-mac-256.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
"size" : "256x256",
|
||||
"filename" : "icon-mac-256@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
"size" : "512x512",
|
||||
"filename" : "icon-mac-512.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
"size" : "512x512",
|
||||
"filename" : "icon-mac-512@2x.png"
|
||||
}
|
||||
],
|
||||
"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" : {
|
||||
"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,12 +1,12 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// MyPass
|
||||
// KeeVault
|
||||
//
|
||||
// Created by Christophe Vila on 21/05/2026.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#endif
|
||||
@@ -22,6 +22,7 @@ struct ContentView: View {
|
||||
VaultRootView(session: session)
|
||||
}
|
||||
}
|
||||
.background(Color("VaultBackground"))
|
||||
.onReceive(
|
||||
NotificationCenter.default.publisher(for: sceneBackgroundNotification)
|
||||
) { _ in
|
||||
@@ -2,13 +2,15 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.antiloop222.mypass</string>
|
||||
<string>group.org.antiloop222.keevault</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)org.antiloop222.mypass</string>
|
||||
<string>$(AppIdentifierPrefix)org.antiloop222.keevault</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// MyPassApp.swift
|
||||
// MyPass
|
||||
// KeeVaultApp.swift
|
||||
// KeeVault
|
||||
//
|
||||
// Created by Christophe Vila on 21/05/2026.
|
||||
//
|
||||
@@ -8,7 +8,7 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct MyPassApp: App {
|
||||
struct KeeVaultApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// BiometricAuthService.swift
|
||||
// MyPass
|
||||
// KeeVault
|
||||
//
|
||||
|
||||
import LocalAuthentication
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// ClipboardService.swift
|
||||
// MyPass
|
||||
// KeeVault
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// FileBookmarkService.swift
|
||||
// MyPass
|
||||
// KeeVault
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@@ -10,7 +10,7 @@ public final class FileBookmarkService {
|
||||
private static let key = "kdbxBookmark"
|
||||
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
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ public final class FileBookmarkService {
|
||||
}
|
||||
|
||||
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(
|
||||
options: Self.creationOptions,
|
||||
includingResourceValuesForKeys: nil,
|
||||
@@ -1,8 +1,8 @@
|
||||
// MyPass/ViewModels/EntryEditViewModel.swift
|
||||
// KeeVault/ViewModels/EntryEditViewModel.swift
|
||||
import Foundation
|
||||
import Combine
|
||||
import SwiftUI
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
@MainActor
|
||||
final class EntryEditViewModel: ObservableObject {
|
||||
@@ -20,7 +20,7 @@ final class EntryEditViewModel: ObservableObject {
|
||||
private let existingEntry: Entry?
|
||||
|
||||
/// `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).
|
||||
init(session: VaultSession, groupId: UUID, existing: Entry? = nil) {
|
||||
self.session = session
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
@MainActor
|
||||
final class GroupFilterViewModel: ObservableObject {
|
||||
@@ -16,11 +16,11 @@ final class GroupFilterViewModel: ObservableObject {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
func isSelected(_ group: MyPassCore.Group) -> Bool {
|
||||
func isSelected(_ group: KeeVaultCore.Group) -> Bool {
|
||||
selectedGroupIds.contains(group.id)
|
||||
}
|
||||
|
||||
func toggle(_ group: MyPassCore.Group) {
|
||||
func toggle(_ group: KeeVaultCore.Group) {
|
||||
guard let root = session.database?.root else { return }
|
||||
selectedGroupIds = GroupSelection.toggling(group.id, in: root, current: selectedGroupIds)
|
||||
lastToggledGroupId = selectedGroupIds.isEmpty ? nil : group.id
|
||||
@@ -1,11 +1,11 @@
|
||||
//
|
||||
// UnlockViewModel.swift
|
||||
// MyPass
|
||||
// KeeVault
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
@MainActor
|
||||
final class UnlockViewModel: ObservableObject {
|
||||
@@ -24,7 +24,7 @@ final class UnlockViewModel: ObservableObject {
|
||||
init(
|
||||
session: VaultSession,
|
||||
bookmarkService: FileBookmarkService = .init(),
|
||||
keychainStore: KeychainStore = .init(accessGroup: "org.antiloop222.mypass"),
|
||||
keychainStore: KeychainStore = .init(accessGroup: "F2KB6W8N4R.org.antiloop222.keevault"),
|
||||
biometricService: BiometricAuthService = .init()
|
||||
) {
|
||||
self.session = session
|
||||
@@ -44,7 +44,7 @@ final class UnlockViewModel: ObservableObject {
|
||||
isUnlocking = true
|
||||
errorMessage = nil
|
||||
do {
|
||||
try await biometricService.authenticate(reason: "Unlock MyPass")
|
||||
try await biometricService.authenticate(reason: "Unlock KeeVault")
|
||||
let storedPassword = try keychainStore.load(for: keychainAccount)
|
||||
let url = try bookmarkService.resolveURL()
|
||||
defer { bookmarkService.stopAccess(url: url) }
|
||||
@@ -1,7 +1,7 @@
|
||||
// MyPass/ViewModels/VaultViewModel.swift
|
||||
// KeeVault/ViewModels/VaultViewModel.swift
|
||||
import Foundation
|
||||
import Combine
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
@MainActor
|
||||
final class VaultViewModel: ObservableObject {
|
||||
@@ -1,11 +1,11 @@
|
||||
//
|
||||
// EntryDetailView.swift
|
||||
// MyPass
|
||||
// KeeVault
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
struct EntryDetailView: View {
|
||||
let entry: Entry
|
||||
@@ -1,6 +1,6 @@
|
||||
// MyPass/Views/EntryEditView.swift
|
||||
// KeeVault/Views/EntryEditView.swift
|
||||
import SwiftUI
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
struct EntryEditView: View {
|
||||
@ObservedObject var vm: EntryEditViewModel
|
||||
@@ -1,10 +1,9 @@
|
||||
// MyPass/Views/EntryListView.swift
|
||||
// KeeVault/Views/EntryListView.swift
|
||||
import SwiftUI
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
struct EntryListView: View {
|
||||
@ObservedObject var vm: VaultViewModel
|
||||
@State private var isSearching = false
|
||||
@State private var showAddEntry = false
|
||||
|
||||
var body: some View {
|
||||
@@ -24,17 +23,11 @@ struct EntryListView: View {
|
||||
offsets.map { vm.displayedEntries[$0] }.forEach(vm.deleteEntry)
|
||||
}
|
||||
}
|
||||
.navigationTitle("MyPass")
|
||||
.searchable(text: $vm.searchQuery, isPresented: $isSearching, prompt: "Search entries…")
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color("VaultBackground"))
|
||||
.navigationTitle("KeeVault")
|
||||
.searchable(text: $vm.searchQuery, prompt: "Search entries…")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button {
|
||||
if isSearching { vm.searchQuery = "" }
|
||||
isSearching.toggle()
|
||||
} label: {
|
||||
Image(systemName: "magnifyingglass")
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button { showAddEntry = true } label: {
|
||||
Image(systemName: "plus")
|
||||
@@ -1,35 +1,39 @@
|
||||
import SwiftUI
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
struct GroupFilterView: View {
|
||||
@ObservedObject var vm: GroupFilterViewModel
|
||||
let rootGroup: MyPassCore.Group
|
||||
let rootGroup: KeeVaultCore.Group
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
if !vm.selectedGroupIds.isEmpty {
|
||||
Button("Clear Filter", action: vm.clear)
|
||||
}
|
||||
GroupFilterNode(group: rootGroup, vm: vm)
|
||||
}
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color("VaultBackground"))
|
||||
.navigationTitle("Groups")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Clear", action: vm.clear)
|
||||
.disabled(vm.selectedGroupIds.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct GroupFilterNode: View {
|
||||
let group: MyPassCore.Group
|
||||
let group: KeeVaultCore.Group
|
||||
@ObservedObject var vm: GroupFilterViewModel
|
||||
var depth: Int = 0
|
||||
|
||||
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
|
||||
} else {
|
||||
DisclosureGroup {
|
||||
ForEach(group.subgroups) { sub in
|
||||
GroupFilterNode(group: sub, vm: vm)
|
||||
}
|
||||
} label: {
|
||||
row
|
||||
ForEach(group.subgroups) { sub in
|
||||
GroupFilterNode(group: sub, vm: vm, depth: depth + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,5 +49,6 @@ private struct GroupFilterNode: View {
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.leading, CGFloat(depth) * 16)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
//
|
||||
// UnlockView.swift
|
||||
// MyPass
|
||||
// KeeVault
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
import MyPassCore
|
||||
import KeeVaultCore
|
||||
|
||||
struct UnlockView: View {
|
||||
@ObservedObject var vm: UnlockViewModel
|
||||
@@ -13,10 +13,11 @@ struct UnlockView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 24) {
|
||||
Spacer()
|
||||
Image(systemName: "lock.shield.fill")
|
||||
.font(.system(size: 64))
|
||||
.foregroundStyle(.tint)
|
||||
Text("MyPass")
|
||||
Image("AppIconImage")
|
||||
.resizable()
|
||||
.frame(width: 88, height: 88)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
|
||||
Text("KeeVault")
|
||||
.font(.largeTitle.bold())
|
||||
|
||||
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
|
||||
|
||||
let package = Package(
|
||||
name: "MyPassCore",
|
||||
name: "KeeVaultCore",
|
||||
platforms: [.iOS(.v17), .macOS(.v14)],
|
||||
products: [
|
||||
.library(name: "MyPassCore", targets: ["MyPassCore"]),
|
||||
.library(name: "KeeVaultCore", targets: ["KeeVaultCore"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "../Vendor/KeePassKit"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "MyPassCore",
|
||||
name: "KeeVaultCore",
|
||||
dependencies: [
|
||||
.product(name: "KeePassKit", package: "KeePassKit"),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "MyPassCoreTests",
|
||||
dependencies: ["MyPassCore"],
|
||||
name: "KeeVaultCoreTests",
|
||||
dependencies: ["KeeVaultCore"],
|
||||
resources: [.copy("Fixtures")]
|
||||
),
|
||||
]
|
||||
@@ -25,7 +25,14 @@ public enum CredentialMatcher {
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
/// 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.
|
||||
public enum KeePassIconMapper {
|
||||
private static let symbolsByIndex: [Int: String] = [
|
||||
@@ -3,7 +3,7 @@ import KeePassKit
|
||||
|
||||
enum KDBXMapper {
|
||||
|
||||
// MARK: - KeePassKit → MyPassCore
|
||||
// MARK: - KeePassKit → KeeVaultCore
|
||||
|
||||
static func database(from tree: KPKTree) -> KDBXDatabase {
|
||||
let meta = DatabaseMetadata(
|
||||
@@ -67,7 +67,7 @@ enum KDBXMapper {
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - MyPassCore → KeePassKit
|
||||
// MARK: - KeeVaultCore → KeePassKit
|
||||
|
||||
static func tree(from db: KDBXDatabase) -> KPKTree {
|
||||
let tree = KPKTree()
|
||||
@@ -129,7 +129,7 @@ enum KDBXMapper {
|
||||
var components = URLComponents()
|
||||
components.scheme = "otpauth"
|
||||
components.host = "totp"
|
||||
components.path = "/MyPass"
|
||||
components.path = "/KeeVault"
|
||||
components.queryItems = [
|
||||
URLQueryItem(name: "secret", value: config.secret),
|
||||
URLQueryItem(name: "period", value: String(config.period)),
|
||||
@@ -5,7 +5,7 @@ public struct KeychainStore {
|
||||
private let accessGroup: 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.service = service
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import MyPassCore
|
||||
@testable import KeeVaultCore
|
||||
|
||||
final class CredentialMatcherTests: XCTestCase {
|
||||
func makeEntry(url: String) -> Entry {
|
||||
@@ -31,6 +31,21 @@ final class CredentialMatcherTests: XCTestCase {
|
||||
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() {
|
||||
let entries = [
|
||||
makeEntry(url: "https://github.com"),
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import MyPassCore
|
||||
@testable import KeeVaultCore
|
||||
|
||||
final class GroupSelectionTests: XCTestCase {
|
||||
private func makeSampleTree() -> (root: Group, work: Group, email: Group, personal: Group) {
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import MyPassCore
|
||||
@testable import KeeVaultCore
|
||||
|
||||
final class GroupTreeTests: XCTestCase {
|
||||
private func makeSampleTree() -> (root: Group, work: Group, email: Group) {
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import MyPassCore
|
||||
@testable import KeeVaultCore
|
||||
|
||||
final class KDBXDocumentTests: XCTestCase {
|
||||
var fixtureURL: URL!
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import MyPassCore
|
||||
@testable import KeeVaultCore
|
||||
|
||||
final class KeePassIconMapperTests: XCTestCase {
|
||||
func test_knownIndex_returnsMappedSymbol() {
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import MyPassCore
|
||||
@testable import KeeVaultCore
|
||||
|
||||
final class ProtectedStringTests: XCTestCase {
|
||||
func test_reveal_returnsOriginalValue() {
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import MyPassCore
|
||||
@testable import KeeVaultCore
|
||||
|
||||
final class TOTPGeneratorTests: XCTestCase {
|
||||
// RFC 6238 Section 8 test vectors for SHA-1
|
||||
@@ -1,13 +1,13 @@
|
||||
//
|
||||
// MyPassTests.swift
|
||||
// MyPassTests
|
||||
// KeeVaultTests.swift
|
||||
// KeeVaultTests
|
||||
//
|
||||
// Created by Christophe Vila on 21/05/2026.
|
||||
//
|
||||
|
||||
import Testing
|
||||
|
||||
struct MyPassTests {
|
||||
struct KeeVaultTests {
|
||||
|
||||
@Test func example() async throws {
|
||||
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
||||
@@ -1,13 +1,13 @@
|
||||
//
|
||||
// MyPassUITests.swift
|
||||
// MyPassUITests
|
||||
// KeeVaultUITests.swift
|
||||
// KeeVaultUITests
|
||||
//
|
||||
// Created by Christophe Vila on 21/05/2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class MyPassUITests: XCTestCase {
|
||||
final class KeeVaultUITests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
@@ -1,13 +1,13 @@
|
||||
//
|
||||
// MyPassUITestsLaunchTests.swift
|
||||
// MyPassUITests
|
||||
// KeeVaultUITestsLaunchTests.swift
|
||||
// KeeVaultUITests
|
||||
//
|
||||
// Created by Christophe Vila on 21/05/2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class MyPassUITestsLaunchTests: XCTestCase {
|
||||
final class KeeVaultUITestsLaunchTests: XCTestCase {
|
||||
|
||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
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.
|
||||
|
||||
- **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).
|
||||
- Tapping a password field in `EntryDetailView` copies to clipboard (clears after 30 s).
|
||||
- TOTP code displays with a countdown ring and refreshes automatically.
|
||||
|
||||