Compare commits

...

24 Commits

Author SHA1 Message Date
742fdb2e86 Update release.yml 2024-08-26 15:00:40 -07:00
d0b0ff30b1 Update nightly.yml 2024-08-26 14:59:57 -07:00
64e5337d61 Update test.yml 2024-08-26 14:59:23 -07:00
5929137f20 Add Catalan localization (#558) 2024-08-26 21:21:45 +00:00
35a7c99cba Turn down concurrency warnings until Swift 6 branch is merged. (#562) 2024-08-26 20:59:20 +00:00
a543de0737 GitHub Actions updates (#554)
* Bump actions/add-to-project to v1.0.1

This _might_ address the failed workflow runs dating back to at least
the last six months:

https://github.com/maxgoedjen/secretive/actions/workflows/add-to-project.yml

* Bump actions/upload-artifact to v4

This should get rid of the deprecation notices displayed as annotations
beneath each Nightly job run. See:

https://github.com/maxgoedjen/secretive/actions/runs/9461831554

* Bump actions/upload-artifact to v4

Similar to cf25db6, this should silence some deprecation notices.
2024-06-25 21:08:38 +00:00
fc21018eb4 Add Japanese translations (#546) 2024-04-28 22:33:51 +00:00
52cc08424e Add Korean localization (#537) 2024-03-01 22:58:08 +00:00
d13f4ee7ba Revert "Use Apple Silicon runners (#519)" (#533)
This reverts commit 409efa5f9f.
2024-02-26 00:24:48 +00:00
6f4226f97a Standardize newline handling (#522)
* Standardize newline handling

* Fix some unterminated bolds in other languages

* Set language back
2024-01-25 02:14:34 +00:00
3315a4bfbc Add Finnish localization (#521) 2024-01-23 00:58:36 +00:00
85a7a64bc9 Updated Italian localization strings (#520)
* 🇮🇹 Initial proposal for Italian localization

* 🇮🇹 Updated Italian localization

---------

Co-authored-by: Max Goedjen <max.goedjen@gmail.com>
2024-01-18 17:35:36 +00:00
409efa5f9f Use Apple Silicon runners (#519)
* Test running on XL (does this work for OSS projects?)

* Move over test/release
2024-01-17 19:28:29 +00:00
bb63ae8469 Set min width/height for setup. (#518) 2024-01-17 04:08:48 +00:00
30c1d36974 Mark newlines as verbatim (#517)
* Merge

* Add missing key
2024-01-17 03:49:14 +00:00
de8d18f9e9 Switch to Xcode 15.2 (#516) 2024-01-16 21:39:07 +00:00
1ae0996e2c 🇮🇹 Initial proposal for Italian localization (#512)
Co-authored-by: Max Goedjen <max.goedjen@gmail.com>
2024-01-16 21:35:28 +00:00
3d50a99430 Add German Localization 🇩🇪 (#514)
* Add German localization

* Small adjustments

* Add German localization in project file

---------

Co-authored-by: Max Goedjen <max.goedjen@gmail.com>
2024-01-16 21:15:07 +00:00
45fc356f0f Add PT-BR to the i18n (#515) 2024-01-16 21:12:46 +00:00
212678b94e Fix missing French translations (#510) 2024-01-13 14:32:08 -08:00
2e1f4881a9 Translate_Addlang_ZH_Han (#508)
Co-authored-by: Alex Q <ln41xgpy@addymail.com>
2024-01-13 06:25:20 +00:00
c2b80e3c7c Localization fixes (#507)
* Consolidate localization files into one file that both targets reference

* Update readme

* Secret Agent/Agent consolidation

* NSLS -> String(localized:)

* Auth contexts
2024-01-12 12:50:52 -08:00
15d2afd2cb Add French localization (#506) 2024-01-12 12:14:02 -08:00
2a4da36c4e Fix escaped text on Agent is running popover (#505) 2024-01-11 23:09:04 +00:00
14 changed files with 4629 additions and 180 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

View File

@ -10,7 +10,7 @@ jobs:
name: Add issue to project name: Add issue to project
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/add-to-project@v0.0.3 - uses: actions/add-to-project@v1.0.1
with: with:
project-url: https://github.com/users/maxgoedjen/projects/1 project-url: https://github.com/users/maxgoedjen/projects/1
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}

View File

@ -6,7 +6,7 @@ on:
jobs: jobs:
build: build:
# runs-on: macOS-latest # runs-on: macOS-latest
runs-on: macos-13 runs-on: macos-14
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -20,7 +20,7 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: ./.github/scripts/signing.sh run: ./.github/scripts/signing.sh
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_15.1.app run: sudo xcrun xcode-select -s /Applications/Xcode_15.4.app
- name: Update Build Number - name: Update Build Number
env: env:
RUN_ID: ${{ github.run_id }} RUN_ID: ${{ github.run_id }}
@ -48,7 +48,7 @@ jobs:
shasum -a 256 Secretive.zip shasum -a 256 Secretive.zip
shasum -a 256 Archive.zip shasum -a 256 Archive.zip
- name: Upload App to Artifacts - name: Upload App to Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Secretive.zip name: Secretive.zip
path: Secretive.zip path: Secretive.zip

View File

@ -7,7 +7,7 @@ on:
jobs: jobs:
test: test:
# runs-on: macOS-latest # runs-on: macOS-latest
runs-on: macos-13 runs-on: macos-14
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -21,7 +21,7 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: ./.github/scripts/signing.sh run: ./.github/scripts/signing.sh
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_15.1.app run: sudo xcrun xcode-select -s /Applications/Xcode_15.4.app
- name: Test - name: Test
run: | run: |
pushd Sources/Packages pushd Sources/Packages
@ -29,7 +29,7 @@ jobs:
popd popd
build: build:
# runs-on: macOS-latest # runs-on: macOS-latest
runs-on: macos-13 runs-on: macos-14
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -43,7 +43,7 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: ./.github/scripts/signing.sh run: ./.github/scripts/signing.sh
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_15.1.app run: sudo xcrun xcode-select -s /Applications/Xcode_15.4.app
- name: Update Build Number - name: Update Build Number
env: env:
TAG_NAME: ${{ github.ref }} TAG_NAME: ${{ github.ref }}
@ -107,12 +107,12 @@ jobs:
asset_name: Secretive.zip asset_name: Secretive.zip
asset_content_type: application/zip asset_content_type: application/zip
- name: Upload App to Artifacts - name: Upload App to Artifacts
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v4
with: with:
name: Secretive.zip name: Secretive.zip
path: Secretive.zip path: Secretive.zip
- name: Upload Archive to Artifacts - name: Upload Archive to Artifacts
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v4
with: with:
name: Xcode_Archive.zip name: Xcode_Archive.zip
path: Archive.zip path: Archive.zip

View File

@ -4,12 +4,12 @@ on: [push, pull_request]
jobs: jobs:
test: test:
# runs-on: macOS-latest # runs-on: macOS-latest
runs-on: macos-13 runs-on: macos-14
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_15.1.app run: sudo xcrun xcode-select -s /Applications/Xcode_15.4.app
- name: Test - name: Test
run: | run: |
pushd Sources/Packages pushd Sources/Packages

View File

@ -16,7 +16,7 @@ Clone Secretive using [these instructions from GitHub](https://docs.github.com/e
Open [Sources/Secretive.xcodeproj](Sources/Secretive.xcodeproj) in Xcode. Open [Sources/Secretive.xcodeproj](Sources/Secretive.xcodeproj) in Xcode.
### Localize the Main App ### Translate
Navigate to [Secretive/Localizable](Sources/Secretive/Localizable.xcstrings). Navigate to [Secretive/Localizable](Sources/Secretive/Localizable.xcstrings).
@ -28,14 +28,6 @@ If your language already has an in-progress localization, select it from the lis
Start translating! You'll see a list of english phrases, and a space to add a translation of your language. Start translating! You'll see a list of english phrases, and a space to add a translation of your language.
### Localize SecretAgent
Navigate to [Secretive/Localizable](Sources/SecretAgent/Localizable.xcstrings).
<img src="/.github/readme/localize_sidebar_agent.png" alt="Screenshot of Xcode navigating to the Localizable file" width="300">
Repeat the same steps from the process of localizing the main app.
### Create a Pull Request ### Create a Pull Request
Push your changes and open a pull request. Push your changes and open a pull request.

View File

@ -39,22 +39,22 @@ let package = Package(
.testTarget( .testTarget(
name: "SecretKitTests", name: "SecretKitTests",
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"], dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] swiftSettings: [.unsafeFlags(["-warnings-as-errors"])]
), ),
.target( .target(
name: "SecureEnclaveSecretKit", name: "SecureEnclaveSecretKit",
dependencies: ["SecretKit"], dependencies: ["SecretKit"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] swiftSettings: [.unsafeFlags(["-warnings-as-errors"])]
), ),
.target( .target(
name: "SmartCardSecretKit", name: "SmartCardSecretKit",
dependencies: ["SecretKit"], dependencies: ["SecretKit"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] swiftSettings: [.unsafeFlags(["-warnings-as-errors"])]
), ),
.target( .target(
name: "SecretAgentKit", name: "SecretAgentKit",
dependencies: ["SecretKit", "SecretAgentKitHeaders"], dependencies: ["SecretKit", "SecretAgentKitHeaders"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] swiftSettings: [.unsafeFlags(["-warnings-as-errors"])]
), ),
.systemLibrary( .systemLibrary(
name: "SecretAgentKitHeaders" name: "SecretAgentKitHeaders"

View File

@ -17,7 +17,7 @@ extension SecureEnclave {
TKTokenWatcher().tokenIDs.contains("com.apple.setoken") TKTokenWatcher().tokenIDs.contains("com.apple.setoken")
} }
public let id = UUID() public let id = UUID()
public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave") public let name = String(localized: "secure_enclave")
@Published public private(set) var secrets: [Secret] = [] @Published public private(set) var secrets: [Secret] = []
private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:] private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:]
@ -107,10 +107,10 @@ extension SecureEnclave {
context = existing.context context = existing.context
} else { } else {
let newContext = LAContext() let newContext = LAContext()
newContext.localizedCancelTitle = "Deny" newContext.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
context = newContext context = newContext
} }
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\"" context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
let attributes = KeychainDictionary([ let attributes = KeychainDictionary([
kSecClass: kSecClassKey, kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeyClass: kSecAttrKeyClassPrivate,
@ -140,8 +140,8 @@ extension SecureEnclave {
public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool { public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool {
let context = LAContext() let context = LAContext()
context.localizedReason = "verify a signature using secret \"\(secret.name)\"" context.localizedReason = String(localized: "auth_context_request_verify_description_\(secret.name)")
context.localizedCancelTitle = "Deny" context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([ let attributes = KeychainDictionary([
kSecClass: kSecClassKey, kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeyClass: kSecAttrKeyClassPrivate,
@ -181,16 +181,16 @@ extension SecureEnclave {
public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) throws { public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) throws {
let newContext = LAContext() let newContext = LAContext()
newContext.touchIDAuthenticationAllowableReuseDuration = duration newContext.touchIDAuthenticationAllowableReuseDuration = duration
newContext.localizedCancelTitle = "Deny" newContext.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let formatter = DateComponentsFormatter() let formatter = DateComponentsFormatter()
formatter.unitsStyle = .spellOut formatter.unitsStyle = .spellOut
formatter.allowedUnits = [.hour, .minute, .day] formatter.allowedUnits = [.hour, .minute, .day]
if let durationString = formatter.string(from: duration) { if let durationString = formatter.string(from: duration) {
newContext.localizedReason = "unlock secret \"\(secret.name)\" for \(durationString)" newContext.localizedReason = String(localized: "auth_context_persist_for_duration_\(secret.name)_\(durationString)")
} else { } else {
newContext.localizedReason = "unlock secret \"\(secret.name)\"" newContext.localizedReason = String(localized: "auth_context_persist_for_duration_unknown_\(secret.name)")
} }
newContext.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: newContext.localizedReason) { [weak self] success, _ in newContext.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: newContext.localizedReason) { [weak self] success, _ in
guard success else { return } guard success else { return }
@ -260,7 +260,7 @@ extension SecureEnclave.Store {
nil)! nil)!
let wrapped: [SecureEnclave.Secret] = publicTyped.map { let wrapped: [SecureEnclave.Secret] = publicTyped.map {
let name = $0[kSecAttrLabel] as? String ?? "Unnamed" let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_secret")
let id = $0[kSecAttrApplicationLabel] as! Data let id = $0[kSecAttrApplicationLabel] as! Data
let publicKeyRef = $0[kSecValueRef] as! SecKey let publicKeyRef = $0[kSecValueRef] as! SecKey
let publicKeyAttributes = SecKeyCopyAttributes(publicKeyRef) as! [CFString: Any] let publicKeyAttributes = SecKeyCopyAttributes(publicKeyRef) as! [CFString: Any]

View File

@ -12,7 +12,7 @@ extension SmartCard {
@Published public var isAvailable: Bool = false @Published public var isAvailable: Bool = false
public let id = UUID() public let id = UUID()
public private(set) var name = NSLocalizedString("Smart Card", comment: "Smart Card") public private(set) var name = String(localized: "smart_card")
@Published public private(set) var secrets: [Secret] = [] @Published public private(set) var secrets: [Secret] = []
private let watcher = TKTokenWatcher() private let watcher = TKTokenWatcher()
private var tokenID: String? private var tokenID: String?
@ -50,8 +50,8 @@ extension SmartCard {
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data { public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data {
guard let tokenID = tokenID else { fatalError() } guard let tokenID = tokenID else { fatalError() }
let context = LAContext() let context = LAContext()
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\"" context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
context.localizedCancelTitle = "Deny" context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([ let attributes = KeychainDictionary([
kSecClass: kSecClassKey, kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeyClass: kSecAttrKeyClassPrivate,
@ -138,7 +138,7 @@ extension SmartCard.Store {
private func loadSecrets() { private func loadSecrets() {
guard let tokenID = tokenID else { return } guard let tokenID = tokenID else { return }
let fallbackName = NSLocalizedString("Smart Card", comment: "Smart Card") let fallbackName = String(localized: "smart_card")
if let driverName = watcher.tokenInfo(forTokenID: tokenID)?.driverName { if let driverName = watcher.tokenInfo(forTokenID: tokenID)?.driverName {
name = driverName name = driverName
} else { } else {
@ -156,7 +156,7 @@ extension SmartCard.Store {
SecItemCopyMatching(attributes, &untyped) SecItemCopyMatching(attributes, &untyped)
guard let typed = untyped as? [[CFString: Any]] else { return } guard let typed = untyped as? [[CFString: Any]] else { return }
let wrapped = typed.map { let wrapped = typed.map {
let name = $0[kSecAttrLabel] as? String ?? "Unnamed" let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_secret")
let tokenID = $0[kSecAttrApplicationLabel] as! Data let tokenID = $0[kSecAttrApplicationLabel] as! Data
let algorithm = Algorithm(secAttr: $0[kSecAttrKeyType] as! NSNumber) let algorithm = Algorithm(secAttr: $0[kSecAttrKeyType] as! NSNumber)
let keySize = $0[kSecAttrKeySizeInBits] as! Int let keySize = $0[kSecAttrKeySizeInBits] as! Int
@ -183,8 +183,8 @@ extension SmartCard.Store {
/// - Warning: Encryption functions are deliberately only exposed on a library level, and are not exposed in Secretive itself to prevent users from data loss. Any pull requests which expose this functionality in the app will not be merged. /// - Warning: Encryption functions are deliberately only exposed on a library level, and are not exposed in Secretive itself to prevent users from data loss. Any pull requests which expose this functionality in the app will not be merged.
public func encrypt(data: Data, with secret: SecretType) throws -> Data { public func encrypt(data: Data, with secret: SecretType) throws -> Data {
let context = LAContext() let context = LAContext()
context.localizedReason = "encrypt data using secret \"\(secret.name)\"" context.localizedReason = String(localized: "auth_context_request_encrypt_description_\(secret.name)")
context.localizedCancelTitle = "Deny" context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([ let attributes = KeychainDictionary([
kSecAttrKeyType: secret.algorithm.secAttrKeyType, kSecAttrKeyType: secret.algorithm.secAttrKeyType,
kSecAttrKeySizeInBits: secret.keySize, kSecAttrKeySizeInBits: secret.keySize,
@ -212,8 +212,8 @@ extension SmartCard.Store {
public func decrypt(data: Data, with secret: SecretType) throws -> Data { public func decrypt(data: Data, with secret: SecretType) throws -> Data {
guard let tokenID = tokenID else { fatalError() } guard let tokenID = tokenID else { fatalError() }
let context = LAContext() let context = LAContext()
context.localizedReason = "decrypt data using secret \"\(secret.name)\"" context.localizedReason = String(localized: "auth_context_request_decrypt_description_\(secret.name)")
context.localizedCancelTitle = "Deny" context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([ let attributes = KeychainDictionary([
kSecClass: kSecClassKey, kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeyClass: kSecAttrKeyClassPrivate,

View File

@ -1,114 +0,0 @@
{
"sourceLanguage" : "en",
"strings" : {
"persist_authentication_accept_button" : {
"comment" : "When the user authorizes an action using a secret that requires unlock, they're shown a notification offering to leave the secret unlocked for a set period of time. This is the title for the notification.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Leave Unlocked"
}
}
}
},
"persist_authentication_decline_button" : {
"comment" : "When the user authorizes an action using a secret that requires unlock, they're shown a notification offering to leave the secret unlocked for a set period of time. This is the decline button for the notification.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Do Not Unlock"
}
}
}
},
"signed_notification_description_%@" : {
"comment" : "When the user performs an action using a secret, they're shown a notification describing what happened. This is the description, showing which secret was used. The placeholder is the name of the secret.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Using secret %1$@"
}
}
}
},
"signed_notification_title_%@" : {
"comment" : "When the user performs an action using a secret, they're shown a notification describing what happened. This is the title, showing which app requested the action. The placeholder is the name of the app.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Signed Request from %1$@"
}
}
}
},
"update_notification_ignore_button" : {
"comment" : "When an update is available, a notification is shown. This is the button to decline an update.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ignore"
}
}
}
},
"update_notification_update_button" : {
"comment" : "When an update is available, a notification is shown. This is the button to download an update.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Update"
}
}
}
},
"update_notification_update_critical_title_%@" : {
"comment" : "When an update is available, a notification is shown. This is the title for a very high priority update with security fixes. The placeholder is for the application version, eg \"Critical Security Update - 2.0\"",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Critical Security Update - %1$@"
}
}
}
},
"update_notification_update_description" : {
"comment" : "When an update is available, a notification is shown. This is the description to download an update.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Click to Update"
}
}
}
},
"update_notification_update_normal_title_%@" : {
"comment" : "When an update is available, a notification is shown. This is the title for a normal priority update. The placeholder is for the application version, eg \"Update Available - 2.0\"",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Update Available - %1$@"
}
}
}
}
},
"version" : "1.0"
}

View File

@ -19,7 +19,6 @@
5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; }; 5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; };
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; }; 5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; };
500B93C32B478D8400E157DE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C22B478D8400E157DE /* Localizable.xcstrings */; }; 500B93C32B478D8400E157DE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C22B478D8400E157DE /* Localizable.xcstrings */; };
500B93C72B479E2E00E157DE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C62B479E2E00E157DE /* Localizable.xcstrings */; };
501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; }; 501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; };
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; }; 50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
@ -53,6 +52,7 @@
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; }; 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; };
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; }; 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; }; 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; };
50E9CF422B51D596004AB36D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C22B478D8400E157DE /* Localizable.xcstrings */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -111,7 +111,6 @@
50033AC227813F1700253856 /* BundleIDs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIDs.swift; sourceTree = "<group>"; }; 50033AC227813F1700253856 /* BundleIDs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIDs.swift; sourceTree = "<group>"; };
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; }; 5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; };
500B93C22B478D8400E157DE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; }; 500B93C22B478D8400E157DE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
500B93C62B479E2E00E157DE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; }; 50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; };
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; }; 50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
@ -316,7 +315,6 @@
50A3B79824026B7600D209EA /* Info.plist */, 50A3B79824026B7600D209EA /* Info.plist */,
508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */, 508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */,
50A3B79924026B7600D209EA /* SecretAgent.entitlements */, 50A3B79924026B7600D209EA /* SecretAgent.entitlements */,
500B93C62B479E2E00E157DE /* Localizable.xcstrings */,
50A3B79224026B7600D209EA /* Preview Content */, 50A3B79224026B7600D209EA /* Preview Content */,
); );
path = SecretAgent; path = SecretAgent;
@ -431,6 +429,13 @@
knownRegions = ( knownRegions = (
en, en,
Base, Base,
it,
fr,
de,
"pt-BR",
fi,
ko,
ca,
); );
mainGroup = 50617D7623FCE48D0099B055; mainGroup = 50617D7623FCE48D0099B055;
productRefGroup = 50617D8023FCE48E0099B055 /* Products */; productRefGroup = 50617D8023FCE48E0099B055 /* Products */;
@ -469,7 +474,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
50A3B79724026B7600D209EA /* Main.storyboard in Resources */, 50A3B79724026B7600D209EA /* Main.storyboard in Resources */,
500B93C72B479E2E00E157DE /* Localizable.xcstrings in Resources */, 50E9CF422B51D596004AB36D /* Localizable.xcstrings in Resources */,
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */, 50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */,
50A3B79124026B7600D209EA /* Assets.xcassets in Resources */, 50A3B79124026B7600D209EA /* Assets.xcassets in Resources */,
508BF2AA25B4F1CB009EFB7E /* InternetAccessPolicy.plist in Resources */, 508BF2AA25B4F1CB009EFB7E /* InternetAccessPolicy.plist in Resources */,
@ -609,13 +614,14 @@
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;
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-concurrency -Xfrontend -enable-actor-data-race-checks"; OTHER_SWIFT_FLAGS = "";
SDKROOT = macosx; SDKROOT = macosx;
STRIP_INSTALLED_PRODUCT = NO; STRIP_INSTALLED_PRODUCT = NO;
STRIP_SWIFT_SYMBOLS = NO; STRIP_SWIFT_SYMBOLS = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = targeted;
}; };
name = Debug; name = Debug;
}; };
@ -668,13 +674,14 @@
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-concurrency -Xfrontend -enable-actor-data-race-checks"; OTHER_SWIFT_FLAGS = "";
SDKROOT = macosx; SDKROOT = macosx;
STRIP_INSTALLED_PRODUCT = NO; STRIP_INSTALLED_PRODUCT = NO;
STRIP_SWIFT_SYMBOLS = NO; STRIP_SWIFT_SYMBOLS = NO;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_STRICT_CONCURRENCY = targeted;
}; };
name = Release; name = Release;
}; };
@ -834,13 +841,14 @@
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;
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-concurrency -Xfrontend -enable-actor-data-race-checks"; OTHER_SWIFT_FLAGS = "";
SDKROOT = macosx; SDKROOT = macosx;
STRIP_INSTALLED_PRODUCT = NO; STRIP_INSTALLED_PRODUCT = NO;
STRIP_SWIFT_SYMBOLS = NO; STRIP_SWIFT_SYMBOLS = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = targeted;
}; };
name = Test; name = Test;
}; };

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@ struct SetupView: View {
} }
} }
} }
.frame(idealWidth: 500, idealHeight: 500) .frame(minWidth: 500, idealWidth: 500, minHeight: 500, idealHeight: 500)
} }

View File

@ -42,16 +42,16 @@ struct UpdateDetailView<UpdaterType: Updater>: View {
if let prefix = split.first { if let prefix = split.first {
switch prefix { switch prefix {
case "#": case "#":
attributed = Text(unprefixed).font(.title) + Text("\n") attributed = Text(unprefixed).font(.title) + Text(verbatim: "\n")
case "##": case "##":
attributed = Text(unprefixed).font(.title2) + Text("\n") attributed = Text(unprefixed).font(.title2) + Text(verbatim: "\n")
case "###": case "###":
attributed = Text(unprefixed).font(.title3) + Text("\n") attributed = Text(unprefixed).font(.title3) + Text(verbatim: "\n")
default: default:
attributed = Text(line) + Text("\n\n") attributed = Text(line) + Text(verbatim: "\n\n")
} }
} else { } else {
attributed = Text(line) + Text("\n\n") attributed = Text(line) + Text(verbatim: "\n\n")
} }
text = text + attributed text = text + attributed
} }