diff --git a/Source/ios-framework/CommonSource/Store.swift b/Source/ios-framework/CommonSource/Store.swift index 1d31371..9cdae6c 100644 --- a/Source/ios-framework/CommonSource/Store.swift +++ b/Source/ios-framework/CommonSource/Store.swift @@ -258,14 +258,11 @@ public class Store: CustomDebugStringConvertible { let entitlements = signingInfo[kSecCodeInfoEntitlementsDict] as? [NSString: NSObject], let applicationGroups = entitlements["com.apple.security.application-groups"] as? [NSString] { - // Semaphore names in macOS are limited to 31 characters. - // Internally, we need up to 11 chars to identify the semaphore, - // thus the group ID must be equal or less than 20 (ASCII) charaters. - if let appGroupIdentifier = applicationGroups.first(where: { $0.length <= 20 }) { - obx_posix_sem_prefix_set(appGroupIdentifier.appending("/")) - // print("found appGroupIdentifier \(appGroupIdentifier)") + if let prefix = Store.semaphorePrefix(from: applicationGroups.map { $0 as String }) { + obx_posix_sem_prefix_set(prefix) + // print("found appGroupIdentifier \(prefix)") } else { - print("Could not find an application group identifier of 20 characters or fewer.") + print("Could not find an application group identifier that fits within the 20-character prefix limit.") } } else if err3 != noErr { // noErr means app has no entitlements, likely not sandboxed. print("Error reading entitlements: \(err3)") @@ -375,4 +372,19 @@ public class Store: CustomDebugStringConvertible { try block() }) } + + // MARK: - POSIX Semaphore Prefix + + /// Finds the first application group identifier that fits within the POSIX semaphore name budget. + /// + /// Semaphore names in macOS are limited to 31 characters. Internally, ObjectBox needs up to 11 characters + /// to identify the semaphore, so the prefix (group ID + "/") must be at most 20 characters. + /// + /// The trailing "/" is appended if not already present before checking the length, matching the behavior + /// of the Dart API. + static func semaphorePrefix(from applicationGroups: [String]) -> String? { + return applicationGroups.lazy + .map { $0.hasSuffix("/") ? $0 : $0.appending("/") } + .first(where: { $0.count <= 20 }) + } } diff --git a/Source/ios-framework/CommonTests/StoreTests.swift b/Source/ios-framework/CommonTests/StoreTests.swift index 5e6c258..7e6e6cf 100644 --- a/Source/ios-framework/CommonTests/StoreTests.swift +++ b/Source/ios-framework/CommonTests/StoreTests.swift @@ -346,4 +346,15 @@ class StoreTests: XCTestCase { func testClone() throws { try testAttachOrClone(clone: true) } + + // MARK: - Semaphore Prefix + + func testSemaphorePrefix() { + // Appends "/" and checks total length <= 20 + XCTAssertEqual(Store.semaphorePrefix(from: ["ABCDEFGHIJ.12345678"]), "ABCDEFGHIJ.12345678/") // 19 + "/" = 20, OK + XCTAssertNil(Store.semaphorePrefix(from: ["ABCDEFGHIJ.123456789"])) // 20 + "/" = 21, too long + XCTAssertEqual(Store.semaphorePrefix(from: ["ABCDEFGHIJ.1234567/"]), "ABCDEFGHIJ.1234567/") // already has "/", no double slash + XCTAssertEqual(Store.semaphorePrefix(from: ["ABCDEFGHIJ.123456789", "SHORT.id"]), "SHORT.id/") // skips first, picks second + XCTAssertNil(Store.semaphorePrefix(from: [])) // empty + } }