docs: async function initialize(dispatch) {
  	shimInit();
  
  	// blobTest();
  
  	Setting.setConstant('env', __DEV__ ? 'dev' : 'prod');
  	Setting.setConstant('appId', 'net.cozic.joplin-mobile');
  	Setting.setConstant('appType', 'mobile');
  	//Setting.setConstant('resourceDir', () => { return RNFetchBlob.fs.dirs.DocumentDir; });
  	Setting.setConstant('resourceDir', RNFetchBlob.fs.dirs.DocumentDir);
  
  	const logDatabase = new Database(new DatabaseDriverReactNative());
  	await logDatabase.open({ name: 'log.sqlite' });
  	await logDatabase.exec(Logger.databaseCreateTableSql());
  
  	const mainLogger = new Logger();
  	mainLogger.addTarget('database', { database: logDatabase, source: 'm' });
  	mainLogger.setLevel(Logger.LEVEL_INFO);
  	
  	if (Setting.value('env') == 'dev') {
  		mainLogger.addTarget('console');
  		mainLogger.setLevel(Logger.LEVEL_DEBUG);
  	}
  
  	reg.setLogger(mainLogger);
  	reg.setShowErrorMessageBoxHandler((message) => { alert(message) });
  
  	BaseService.logger_ = mainLogger;
  
  	reg.logger().info('====================================');
  	reg.logger().info('Starting application ' + Setting.value('appId') + ' (' + Setting.value('env') + ')');
  
  	const dbLogger = new Logger();
  	dbLogger.addTarget('database', { database: logDatabase, source: 'm' }); 
  	if (Setting.value('env') == 'dev') {
  		dbLogger.addTarget('console');
  		dbLogger.setLevel(Logger.LEVEL_INFO); // Set to LEVEL_DEBUG for full SQL queries
  	} else {
  		dbLogger.setLevel(Logger.LEVEL_INFO);
  	}
  
  	let db = new JoplinDatabase(new DatabaseDriverReactNative());
  	db.setLogger(dbLogger);
  	reg.setDb(db);
  
  	reg.dispatch = dispatch;
  	BaseModel.dispatch = dispatch;
  	FoldersScreenUtils.dispatch = dispatch;
  	BaseSyncTarget.dispatch = dispatch;
  	NavService.dispatch = dispatch;
  	BaseModel.db_ = db;
  
  	BaseItem.loadClass('Note', Note);
  	BaseItem.loadClass('Folder', Folder);
  	BaseItem.loadClass('Resource', Resource);
  	BaseItem.loadClass('Tag', Tag);
  	BaseItem.loadClass('NoteTag', NoteTag);
  	BaseItem.loadClass('MasterKey', MasterKey);
  	BaseItem.loadClass('Revision', Revision);
  
  	const fsDriver = new FsDriverRN();
  
  	Resource.fsDriver_ = fsDriver;
  	FileApiDriverLocal.fsDriver_ = fsDriver;
  
  	AlarmService.setDriver(new AlarmServiceDriver());
  	AlarmService.setLogger(mainLogger);
  
  	try {
  		if (Setting.value('env') == 'prod') {
  			await db.open({ name: 'joplin.sqlite' })
  		} else {
  			await db.open({ name: 'joplin-68.sqlite' })
  		}
  
  		reg.logger().info('Database is ready.');
  		reg.logger().info('Loading settings...');
  		await Setting.load();
  
  		if (Setting.value('firstStart')) {
  			let locale = NativeModules.I18nManager.localeIdentifier
  			if (!locale) locale = defaultLocale();
  			Setting.setValue('locale', closestSupportedLocale(locale));
  			if (Setting.value('env') === 'dev') Setting.setValue('sync.target', SyncTargetRegistry.nameToId('onedrive_dev'));
  			Setting.setValue('firstStart', 0);
  		}
  
  		if (Setting.value('db.ftsEnabled') === -1) {
  			const ftsEnabled = await db.ftsEnabled();
  			Setting.setValue('db.ftsEnabled', ftsEnabled ? 1 : 0);
  			reg.logger().info('db.ftsEnabled = ', Setting.value('db.ftsEnabled'));
  		}
  
  		BaseItem.revisionService_ = RevisionService.instance();
  
  		// Note: for now we hard-code the folder sort order as we need to 
  		// create a UI to allow customisation (started in branch mobile_add_sidebar_buttons)
  		Setting.setValue('folders.sortOrder.field', 'title');
  		Setting.setValue('folders.sortOrder.reverse', false);
  
  		reg.logger().info('Sync target: ' + Setting.value('sync.target'));
  
  		setLocale(Setting.value('locale'));
  
  		// ----------------------------------------------------------------
  		// E2EE SETUP
  		// ----------------------------------------------------------------
  
  		EncryptionService.fsDriver_ = fsDriver;
  		EncryptionService.instance().setLogger(mainLogger);
  		BaseItem.encryptionService_ = EncryptionService.instance();
  		DecryptionWorker.instance().dispatch = dispatch;
  		DecryptionWorker.instance().setLogger(mainLogger);
  		DecryptionWorker.instance().setEncryptionService(EncryptionService.instance());
  		await EncryptionService.instance().loadMasterKeysFromSettings();
  
  		// ----------------------------------------------------------------
  		// / E2EE SETUP
  		// ----------------------------------------------------------------
  
  		reg.logger().info('Loading folders...');
  
  		await FoldersScreenUtils.refreshFolders();
  
  		const tags = await Tag.allWithNotes();
  
  		dispatch({
  			type: 'TAG_UPDATE_ALL',
  			items: tags,
  		});
  
  		const masterKeys = await MasterKey.all();
  
  		dispatch({
  			type: 'MASTERKEY_UPDATE_ALL',
  			items: masterKeys,
  		});
  
  		let folderId = Setting.value('activeFolderId');
  		let folder = await Folder.load(folderId);
  
  		if (!folder) folder = await Folder.defaultFolder();
  
  		dispatch({
  			type: 'FOLDER_SET_COLLAPSED_ALL',
  			ids: Setting.value('collapsedFolderIds'),
  		});
  
  		if (!folder) {
  			dispatch({
  				type: 'NAV_GO',
  				routeName: 'Welcome',
  			});
  		} else {
  			dispatch({
  				type: 'NAV_GO',
  				routeName: 'Notes',
  				folderId: folder.id,
  			});
  		}
  	} catch (error) {
  		alert('Initialization error: ' + error.message);
  		reg.logger().error('Initialization error:', error);
  	}
  
  	reg.setupRecurrentSync();
  
  	PoorManIntervals.setTimeout(() => {
  		AlarmService.garbageCollect();
  	}, 1000 * 60 * 60);
  
  	ResourceService.runInBackground();
  
  	ResourceFetcher.instance().setFileApi(() => { return reg.syncTarget().fileApi() });
  	ResourceFetcher.instance().setLogger(reg.logger());
  	ResourceFetcher.instance().start();
  
  	SearchEngine.instance().setDb(reg.db());
  	SearchEngine.instance().setLogger(reg.logger());
  	SearchEngine.instance().scheduleSyncTables();
  
  	reg.scheduleSync().then(() => {
  		// Wait for the first sync before updating the notifications, since synchronisation
  		// might change the notifications.
  		AlarmService.updateAllNotifications();
  
  		DecryptionWorker.instance().scheduleStart();
  	});
  
  	await WelcomeUtils.install(dispatch);
  
  	// Collect revisions more frequently on mobile because it doesn't auto-save
  	// and it cannot collect anything when the app is not active.
  	RevisionService.instance().runInBackground(1000 * 30);
  
  	reg.logger().info('Application initialized');
  }