In-app Update with Github

Hello everyone :partying_face:

Thanks to @Bruno_Lima’s recent question about updating the app through GitHub releases, I found the topic very interesting.
I experimented a bit and came up with a project that’s fairly functional and not too complex, which I wanted to share with all of you.

We’ll mainly use the following components:

We’ll have two functions

  • githubGetLatestRelease
    Will be used in the Screen.Initialize event and will make a request to the GitHub API, providing us with information about the latest release.

  • githubVerifyRelease
    Will be used as soon as we receive the information.
    It will check the release name against the app version, and if they differ, it will download the updated version.

these functions were created just to be compressed and “forgotten” in the project without causing any clutter
In the same way, they can be used directly without having to call them through a function.

githubGetLatestRelease

Given the simplicity, I’ll just point out that Web.URL will be

https://api.github.com/repos/OWNER/REPO/releases/latest

where OWNER is the GitHub username and REPO is the repository name but later on, we’ll briefly see how to retrieve it.

githubVerifyRelease

This function will receive the responseContent from Web_GithubAppUpdate.GotText and format the response.

Below is an example that can be referred to.

response Content
{
	"assets": [
		{
			"browser_download_url": "https://github.com/OWNER/REPO/releases/download/NAME(VERSION)/APPNAME.apk",
			"content_type": "application/vnd.android.package-archive",
			"created_at": "2025-10-31T18:49:13Z",
			"digest": "sha256:bcb2df704fe29c2deeebeeb02603e91dra022e43ce414917abf511758b20525",
			"download_count": 1,
			"id": 309359408,
			"label": "null",
			"name": "APPNAME.apk",
			"node_id": "RA_kwDOQKPENM2ScHMw",
			"size": 10579386,
			"state": "uploaded",
			"updated_at": "2025-10-31T20:57:38Z",
			"uploader": {
				"avatar_url": "https://avatars.githubusercontent.com/u/104521385?v=4",
				"events_url": "https://api.github.com/users/OWNER/events{/privacy}",
				"followers_url": "https://api.github.com/users/OWNER/followers",
				"following_url": "https://api.github.com/users/OWNER/following{/other_user}",
				"gists_url": "https://api.github.com/users/OWNER/gists{/gist_id}",
				"gravatar_id": "",
				"html_url": "https://github.com/OWNER",
				"id": 108671385,
				"login": "OWNER",
				"node_id": "U_kgD4BJjSCQ",
				"organizations_url": "https://api.github.com/users/OWNER/orgs",
				"received_events_url": "https://api.github.com/users/OWNER/received_events",
				"repos_url": "https://api.github.com/users/OWNER/repos",
				"site_admin": false,
				"starred_url": "https://api.github.com/users/OWNER/starred{/owner}{/repo}",
				"subscriptions_url": "https://api.github.com/users/OWNER/subscriptions",
				"type": "User",
				"url": "https://api.github.com/users/OWNER",
				"user_view_type": "public"
			},
			"url": "https://api.github.com/repos/OWNER/REPO/releases/assets/309354408"
		},
		{
			"browser_download_url": "https://github.com/OWNER/REPO/releases/download/NAME(VERSION)/something.txt",
			"content_type": "text/plain",
			"created_at": "2025-10-31T21:04:36Z",
			"digest": "sha256:45aad2fe9636e92a19f6deeef9cb2392847ac33d6d3d4b1fb65eb9ef8c3d5a66",
			"download_count": 1,
			"id": 302295109,
			"label": "null",
			"name": "something.txt",
			"node_id": "RA_kwDOTKPENM4ScP6l",
			"size": 11,
			"state": "uploaded",
			"updated_at": "2025-10-31T21:04:37Z",
			"uploader": {
				"avatar_url": "https://avatars.githubusercontent.com/u/108584785?v=4",
				"events_url": "https://api.github.com/users/OWNER/events{/privacy}",
				"followers_url": "https://api.github.com/users/OWNER/followers",
				"following_url": "https://api.github.com/users/OWNER/following{/other_user}",
				"gists_url": "https://api.github.com/users/OWNER/gists{/gist_id}",
				"gravatar_id": "",
				"html_url": "https://github.com/OWNER",
				"id": 108582355,
				"login": "OWNER",
				"node_id": "U_kgDOBnjICQ",
				"organizations_url": "https://api.github.com/users/OWNER/orgs",
				"received_events_url": "https://api.github.com/users/OWNER/received_events",
				"repos_url": "https://api.github.com/users/OWNER/repos",
				"site_admin": false,
				"starred_url": "https://api.github.com/users/OWNER/starred{/owner}{/repo}",
				"subscriptions_url": "https://api.github.com/users/OWNER/subscriptions",
				"type": "User",
				"url": "https://api.github.com/users/OWNER",
				"user_view_type": "public"
			},
			"url": "https://api.github.com/repos/OWNER/REPO/releases/assets/309392909"
		}
	],
	"assets_url": "https://api.github.com/repos/OWNER/REPO/releases/257322570/assets",
	"author": {
		"avatar_url": "https://avatars.githubusercontent.com/u/108671385?v=4",
		"events_url": "https://api.github.com/users/OWNER/events{/privacy}",
		"followers_url": "https://api.github.com/users/OWNER/followers",
		"following_url": "https://api.github.com/users/OWNER/following{/other_user}",
		"gists_url": "https://api.github.com/users/OWNER/gists{/gist_id}",
		"gravatar_id": "",
		"html_url": "https://github.com/OWNER",
		"id": 108255385,
		"login": "OWNER",
		"node_id": "U_kgDHBnjSCQ",
		"organizations_url": "https://api.github.com/users/OWNER/orgs",
		"received_events_url": "https://api.github.com/users/OWNER/received_events",
		"repos_url": "https://api.github.com/users/OWNER/repos",
		"site_admin": false,
		"starred_url": "https://api.github.com/users/OWNER/starred{/owner}{/repo}",
		"subscriptions_url": "https://api.github.com/users/OWNER/subscriptions",
		"type": "User",
		"url": "https://api.github.com/users/OWNER",
		"user_view_type": "public"
	},
	"body": "- Fixed some bugs\r\n- Better UI\r\n- Removed malware\r\n- Added something.txt",
	"created_at": "2025-10-31T18:19:36Z",
	"draft": false,
	"html_url": "https://github.com/OWNER/REPO/releases/tag/NAME(VERSION)",
	"id": 257602570,
	"immutable": false,
	"name": "NAME(VERSION)",
	"node_id": "RE_kwDOQKPJNM4PWrQK",
	"prerelease": false,
	"published_at": "2025-10-31T18:20:51Z",
	"tag_name": "NAME(VERSION)",
	"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/NAME(VERSION)",
	"target_commitish": "main",
	"updated_at": "2025-10-31T21:04:50Z",
	"upload_url": "https://uploads.github.com/repos/OWNER/REPO/releases/257654570/assets{?name,label}",
	"url": "https://api.github.com/repos/OWNER/REPO/releases/257690570",
	"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/NAME(VERSION)"
}

I’ll try to explain it briefly

  • If the version of our app differs from the one listed in the latest release,

    • it saves the response containing all the information in TinyDB, adding a key toRead = true and a key infos containing the formattedResponse.
    • We then create the variable assets, which points to assets (the list containing all the files in the release) from the formattedResponse.
    • When looping through it, if an asset with an .apk extension is found, it downloads it to update/assetName and stops, using break (assuming there’s only one APK file)
  • If the app version is already up to date, the function deletes the APK file from the app’s folder

    • Here’s where toRead comes into play, since it’s set to true, it triggers an if statement that displays infos > body from the formattedResponse saved in TinyDB, which represents the update details.
    • It then sets toRead = false and updates updateInfos.
    • So, if the version is up to date and there’s nothing to read in updateInfos, we inform the user that there are no updates available.

Web_GithubAppUpdate.GotText

Since we’ll only be downloading one APK, this block will simply call APKInstaller, where we’ll specify the file name, which in this case will be update/assetName.apk.

Github

let’s move on to GitHub by quickly creating a project.

We’ll start by creating a new repository.

The repository name will be the value we need to replace in the app as REPO.

immagine

after creating a codespace, we’ll be able to create our first release


immagine

we fill in all the fields, add the APK file, and we’re done!

From now on, it will be enough to create a new release to have all our clients updated :partying_face:
For example, we could implement a block that forces the user to use the latest version.

AIA

GithubAppUpdate.aia (54.4 KB)

Notes

I’ve tested the extension on several devices and didn’t encounter any issues.
I haven’t been able to test it on Android 15 yet, but I hope it works.
Thanks again, Riad_Developer!

Take this project as a base to improve or modify,
it’s a starting point.
There are many other things you can add or do differently.

Happy :kodular:oding!

5 Likes