Nanfeng

Notes on software development, code, and curious ideas

Fixing a Cocos Creator iOS Hot Update That Still Runs Old Code

This hot-update implementation was already working correctly on Android, so I reused it on iOS without much concern. The update reached 100%, and I was happy when the app restarted—until I realized that it was still running the old code.

Troubleshooting

Common Cocos 3.x issue: point the update path at the assets root

My source path was -s ${path.join(result.dest, resdir)}:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
exports.onAfterBuild = function (options, result) {
let resdir = 'assets';

if (fs.existsSync(path.join(result.dest, 'data'))) {
resdir = 'data';
}
let cmd = `node version_generator.js -v ${game_version} -u https://lengmo714/${game_version}/ -s ${path.join(result.dest, resdir)} -d ${path.join(Editor.Project.path, "assets\\resources")}`

console.warn("Hot update path:", cmd);

exec(cmd, { cwd: Editor.Project.path }, (err, stdout, stderr) => {
if (!err) return;
console.error(err);
});
}

On iOS, the build path is usually:

1
build/ios/data

The local path after an update usually looks like:

1
Documents/asset

In my case, an incorrect Objective-C path caused the mismatch:

Incorrect hot-update path

After correcting it, the path was right:

Correct hot-update path

The key issue

Correcting the path did not solve the problem, which meant it was not the root cause. The update-completion code was:

1
2
3
4
5
6
7
8
9
10
11
this._am.setEventCallback(null!);
this._updateListener = null;
var searchPaths = jsb.fileUtils.getSearchPaths();
var newPaths = this._am.getLocalManifest().getSearchPaths();
console.log("---- newPaths", JSON.stringify(newPaths));
Array.prototype.unshift.apply(searchPaths, newPaths);
localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
jsb.fileUtils.setSearchPaths(searchPaths);
setTimeout(() => {
game.restart();
}, 100)

I called setSearchPaths only when the update finished. After the restart, I did not restore those paths early in the game startup process, so iOS continued loading the old code.

The simplest fix is to update main.js as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
window.self = window;
require("src/system.bundle.js");

// Restore hot-update search paths before loading the application.
const jsb = window['jsb'];
if (jsb && jsb.fileUtils) {
const hotUpdateSearchPaths = localStorage.getItem('HotUpdateSearchPaths');
if (hotUpdateSearchPaths) {
const paths = JSON.parse(hotUpdateSearchPaths);
jsb.fileUtils.setSearchPaths(paths);
console.log("--- restore HotUpdateSearchPaths:", paths);
}
}

const importMapJson = jsb.fileUtils.getStringFromFile("src/import-map.json");
const importMap = JSON.parse(importMapJson);
System.warmup({
importMap,
importMapUrl: 'src/import-map.json',
defaultHandler: (urlNoSchema) => {
require(urlNoSchema.startsWith('/') ? urlNoSchema.substr(1) : urlNoSchema);
},
});

System.import('./src/application.js')
.then(({ Application }) => {
return new Application();
}).then((application) => {
return System.import('cc').then((cc) => {
require('jsb-adapter/engine-adapter.js');
return application.init(cc);
}).then(() => {
return application.start();
});
}).catch((err) => {
console.error(err.toString() + ', stack: ' + err.stack);
});

Correcting the native path and restoring the saved search paths at startup solved the problem.

+