Skip to content

Commit d0c2623

Browse files
committed
Await async storage persistence for app version to prevent duplicate install events
storage().set() writes to the in-memory cache synchronously but persists to AsyncStorage asynchronously without being awaited. If the app crashes or is force-quit before the async write completes, the version data is lost and the next launch would fire a duplicate "Application Installed" event. Switch to awaiting setAsync() to ensure persistence completes. https://claude.ai/code/session_0117WwRELH1nzVbxRj7Kmm5N
1 parent 3af3d57 commit d0c2623

2 files changed

Lines changed: 8 additions & 4 deletions

File tree

src/__tests__/lifecycle.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ jest.mock('react-native', () => ({
1515
const mockStorageInstance = {
1616
get: jest.fn().mockReturnValue(null),
1717
set: jest.fn(),
18+
setAsync: jest.fn().mockResolvedValue(undefined),
1819
remove: jest.fn(),
1920
isAvailable: jest.fn().mockReturnValue(true),
2021
};
@@ -52,6 +53,7 @@ describe('AppLifecycleManager', () => {
5253
mockAnalytics = { track: jest.fn().mockResolvedValue(undefined) };
5354
mockStorageInstance.get.mockReturnValue(null);
5455
mockStorageInstance.set.mockReturnValue(undefined);
56+
mockStorageInstance.setAsync.mockResolvedValue(undefined);
5557
mockStorageManager.hasPersistentStorage.mockReturnValue(true);
5658
(storage as jest.Mock).mockReturnValue(mockStorageInstance);
5759
(getStorageManager as jest.Mock).mockReturnValue(mockStorageManager);
@@ -89,8 +91,8 @@ describe('AppLifecycleManager', () => {
8991

9092
await manager.start({ version: '1.0.0', build: '1' });
9193

92-
expect(mockStorageInstance.set).toHaveBeenCalledWith('app_version', '1.0.0');
93-
expect(mockStorageInstance.set).toHaveBeenCalledWith('app_build', '1');
94+
expect(mockStorageInstance.setAsync).toHaveBeenCalledWith('app_version', '1.0.0');
95+
expect(mockStorageInstance.setAsync).toHaveBeenCalledWith('app_build', '1');
9496
});
9597
});
9698

src/lib/lifecycle/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,10 @@ export class AppLifecycleManager {
159159
}
160160

161161
// Persist current version/build for next comparison
162-
storage().set(LOCAL_APP_VERSION_KEY, version);
163-
storage().set(LOCAL_APP_BUILD_KEY, build);
162+
// Use setAsync to ensure data is written to AsyncStorage before continuing,
163+
// preventing duplicate install events if the app is terminated before persistence completes
164+
await storage().setAsync(LOCAL_APP_VERSION_KEY, version);
165+
await storage().setAsync(LOCAL_APP_BUILD_KEY, build);
164166
}
165167

166168
/**

0 commit comments

Comments
 (0)