diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift
index 08b27bd97..21edfcbe5 100644
--- a/Frameworks/Account/AccountManager.swift
+++ b/Frameworks/Account/AccountManager.swift
@@ -72,6 +72,14 @@ public final class AccountManager: UnreadCountProvider {
return CombinedRefreshProgress(downloadProgressArray: downloadProgressArray)
}
+ public convenience init() {
+ let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
+ let accountsURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)
+ let accountsFolder = accountsURL!.appendingPathComponent("Accounts").absoluteString
+ let accountsFolderPath = accountsFolder.suffix(from: accountsFolder.index(accountsFolder.startIndex, offsetBy: 7))
+ self.init(accountsFolder: String(accountsFolderPath))
+ }
+
public init(accountsFolder: String) {
self.accountsFolder = accountsFolder
diff --git a/Frameworks/Account/Credentials/CredentialsManager.swift b/Frameworks/Account/Credentials/CredentialsManager.swift
index 6ac291cca..b52b6c30c 100644
--- a/Frameworks/Account/Credentials/CredentialsManager.swift
+++ b/Frameworks/Account/Credentials/CredentialsManager.swift
@@ -10,6 +10,15 @@ import Foundation
public struct CredentialsManager {
+ private static var keychainGroup: String? = {
+ guard let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String else {
+ return nil
+ }
+ let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String
+ let appGroupSuffix = appGroup.suffix(appGroup.count - 6)
+ return "\(appIdentifierPrefix)\(appGroupSuffix)"
+ }()
+
public static func storeCredentials(_ credentials: Credentials, server: String) throws {
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
@@ -20,6 +29,10 @@ public struct CredentialsManager {
query[kSecAttrSecurityDomain as String] = credentials.type.rawValue
}
+ if let securityGroup = keychainGroup {
+ query[kSecAttrAccessGroup as String] = securityGroup
+ }
+
let secretData = credentials.secret.data(using: String.Encoding.utf8)!
let attributes: [String: Any] = [kSecValueData as String: secretData]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
@@ -59,6 +72,10 @@ public struct CredentialsManager {
query[kSecAttrSecurityDomain as String] = type.rawValue
}
+ if let securityGroup = keychainGroup {
+ query[kSecAttrAccessGroup as String] = securityGroup
+ }
+
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
@@ -93,6 +110,10 @@ public struct CredentialsManager {
query[kSecAttrSecurityDomain as String] = type.rawValue
}
+ if let securityGroup = keychainGroup {
+ query[kSecAttrAccessGroup as String] = securityGroup
+ }
+
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else {
throw CredentialsError.unhandledError(status: status)
diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift
index 79b2a0854..2bce61ac6 100644
--- a/iOS/AppDelegate.swift
+++ b/iOS/AppDelegate.swift
@@ -55,12 +55,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
// Force lazy initialization of the web view provider so that it can warm up the queue of prepared web views
let _ = DetailViewControllerWebViewProvider.shared
-
- let accountsURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.\(Bundle.main.bundleIdentifier!)")
- let accountsFolder = accountsURL!.appendingPathComponent("Accounts").absoluteString
- let accountsFolderPath = accountsFolder.suffix(from: accountsFolder.index(accountsFolder.startIndex, offsetBy: 7))
- AccountManager.shared = AccountManager(accountsFolder: String(accountsFolderPath))
-
+ AccountManager.shared = AccountManager()
AppDefaults.shared = UserDefaults.init(suiteName: "group.\(Bundle.main.bundleIdentifier!)")!
registerBackgroundTasks()
diff --git a/iOS/Resources/Info.plist b/iOS/Resources/Info.plist
index cee7acd46..c5fa02fdd 100644
--- a/iOS/Resources/Info.plist
+++ b/iOS/Resources/Info.plist
@@ -2,6 +2,10 @@
+ AppGroup
+ group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS
+ AppIdentifierPrefix
+ $(AppIdentifierPrefix)
BGTaskSchedulerPermittedIdentifiers
com.ranchero.NetNewsWire.FeedRefresh
diff --git a/iOS/Resources/NetNewsWire.entitlements b/iOS/Resources/NetNewsWire.entitlements
index d6b369115..05d04e805 100644
--- a/iOS/Resources/NetNewsWire.entitlements
+++ b/iOS/Resources/NetNewsWire.entitlements
@@ -6,5 +6,9 @@
group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS
+
diff --git a/iOS/ShareExtension/Info.plist b/iOS/ShareExtension/Info.plist
index 2ecaf1743..d25defda1 100644
--- a/iOS/ShareExtension/Info.plist
+++ b/iOS/ShareExtension/Info.plist
@@ -2,6 +2,10 @@
+ AppGroup
+ group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS
+ AppIdentifierPrefix
+ $(AppIdentifierPrefix)
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
diff --git a/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements b/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements
index d6b369115..05d04e805 100644
--- a/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements
+++ b/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements
@@ -6,5 +6,9 @@
group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS
+
diff --git a/iOS/ShareExtension/ShareViewController.swift b/iOS/ShareExtension/ShareViewController.swift
index 0fd4550f8..f3b21e2ec 100644
--- a/iOS/ShareExtension/ShareViewController.swift
+++ b/iOS/ShareExtension/ShareViewController.swift
@@ -23,11 +23,7 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont
override func viewDidLoad() {
- let rootID = Bundle.main.bundleIdentifier!.replacingOccurrences(of: ".Share-Extension", with: "")
- let accountsURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.\(rootID)")
- let accountsFolder = accountsURL!.appendingPathComponent("Accounts").absoluteString
- let accountsFolderPath = accountsFolder.suffix(from: accountsFolder.index(accountsFolder.startIndex, offsetBy: 7))
- AccountManager.shared = AccountManager(accountsFolder: String(accountsFolderPath))
+ AccountManager.shared = AccountManager()
pickerData = FlattenedAccountFolderPickerData()