react-native-nitro-version-check 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/NitroVersionCheck.podspec +31 -0
  2. package/README.md +117 -0
  3. package/android/CMakeLists.txt +29 -0
  4. package/android/build.gradle +142 -0
  5. package/android/fix-prefab.gradle +51 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/cpp/cpp-adapter.cpp +9 -0
  9. package/android/src/main/java/com/margelo/nitro/nitroversioncheck/HybridVersionCheck.kt +74 -0
  10. package/android/src/main/java/com/margelo/nitro/nitroversioncheck/NitroVersionCheckPackage.kt +18 -0
  11. package/ios/Bridge.h +8 -0
  12. package/ios/HybridVersionCheck.swift +71 -0
  13. package/lib/index.d.ts +168 -0
  14. package/lib/index.js +174 -0
  15. package/lib/semver.d.ts +22 -0
  16. package/lib/semver.js +51 -0
  17. package/lib/specs/Version.nitro.d.ts +14 -0
  18. package/lib/specs/Version.nitro.js +1 -0
  19. package/lib/types/UpdateResult.d.ts +6 -0
  20. package/lib/types/UpdateResult.js +1 -0
  21. package/nitro.json +18 -0
  22. package/nitrogen/generated/.gitattributes +1 -0
  23. package/nitrogen/generated/android/NitroVersionCheck+autolinking.cmake +81 -0
  24. package/nitrogen/generated/android/NitroVersionCheck+autolinking.gradle +27 -0
  25. package/nitrogen/generated/android/NitroVersionCheckOnLoad.cpp +47 -0
  26. package/nitrogen/generated/android/NitroVersionCheckOnLoad.hpp +34 -0
  27. package/nitrogen/generated/android/c++/JHybridVersionCheckSpec.cpp +129 -0
  28. package/nitrogen/generated/android/c++/JHybridVersionCheckSpec.hpp +72 -0
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroversioncheck/HybridVersionCheckSpec.kt +84 -0
  30. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroversioncheck/NitroVersionCheckOnLoad.kt +35 -0
  31. package/nitrogen/generated/ios/NitroVersionCheck+autolinking.rb +60 -0
  32. package/nitrogen/generated/ios/NitroVersionCheck-Swift-Cxx-Bridge.cpp +57 -0
  33. package/nitrogen/generated/ios/NitroVersionCheck-Swift-Cxx-Bridge.hpp +179 -0
  34. package/nitrogen/generated/ios/NitroVersionCheck-Swift-Cxx-Umbrella.hpp +46 -0
  35. package/nitrogen/generated/ios/NitroVersionCheckAutolinking.mm +33 -0
  36. package/nitrogen/generated/ios/NitroVersionCheckAutolinking.swift +26 -0
  37. package/nitrogen/generated/ios/c++/HybridVersionCheckSpecSwift.cpp +11 -0
  38. package/nitrogen/generated/ios/c++/HybridVersionCheckSpecSwift.hpp +123 -0
  39. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  40. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  41. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  42. package/nitrogen/generated/ios/swift/HybridVersionCheckSpec.swift +61 -0
  43. package/nitrogen/generated/ios/swift/HybridVersionCheckSpec_cxx.swift +227 -0
  44. package/nitrogen/generated/shared/c++/HybridVersionCheckSpec.cpp +28 -0
  45. package/nitrogen/generated/shared/c++/HybridVersionCheckSpec.hpp +70 -0
  46. package/package.json +116 -0
  47. package/react-native.config.js +16 -0
  48. package/src/index.ts +185 -0
  49. package/src/semver.ts +54 -0
  50. package/src/specs/Version.nitro.ts +16 -0
@@ -0,0 +1,31 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "NitroVersionCheck"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0 }
14
+ s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = [
17
+ # Implementation (Swift)
18
+ "ios/**/*.{swift}",
19
+ # Autolinking/Registration (Objective-C++)
20
+ "ios/**/*.{m,mm}",
21
+ # Implementation (C++ objects)
22
+ "cpp/**/*.{hpp,cpp}",
23
+ ]
24
+
25
+ load 'nitrogen/generated/ios/NitroVersionCheck+autolinking.rb'
26
+ add_nitrogen_files(s)
27
+
28
+ s.dependency 'React-jsi'
29
+ s.dependency 'React-callinvoker'
30
+ install_modules_dependencies(s)
31
+ end
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ <h1 align="center">
2
+ react-native-nitro-version-check
3
+ </h1>
4
+
5
+ <p align="center">
6
+ <b>A fast, modern version-checking library for React Native, powered by <a href="https://github.com/mrousavy/nitro">Nitro Modules</a>.</b>
7
+ </p>
8
+
9
+ <p align="center">
10
+ A drop-in replacement for the unmaintained <a href="https://github.com/kimxogus/react-native-version-check"><code>react-native-version-check</code></a> — rewritten from scratch with <a href="https://github.com/mrousavy/nitro">Nitro Modules</a>.
11
+ </p>
12
+
13
+ <br />
14
+
15
+ ## Features
16
+
17
+ - ⚡ **Synchronous access** to version, build number, package name, and country
18
+ - 🏊 **Store version lookup** from the App Store or Play Store
19
+ - 🔍 **Granular update checks** — major, minor, or patch
20
+ - ðŸ“Ķ **Install source detection** — TestFlight, App Store, Play Store, or sideloaded
21
+ - ðŸŠķ **Tiny footprint** — pure Swift on iOS, Kotlin on Android
22
+
23
+ ## Performance
24
+
25
+ Benchmarked against [`react-native-version-check`](https://github.com/kimxogus/react-native-version-check). 100,000 iterations averaged over 5 runs on an iPhone 12:
26
+
27
+ | Method | Speedup |
28
+ |--------|---------|
29
+ | `getAllInfo` | **~3.1x faster** |
30
+ | `packageName` | **~1.6x faster** |
31
+ | `version` | **~1.6x faster** |
32
+ | `buildNumber` | **~1.6x faster** |
33
+ | `getCountry` | **~3.1x faster** |
34
+
35
+ ## Installation
36
+
37
+ ```sh
38
+ bun add react-native-nitro-version-check react-native-nitro-modules
39
+ ```
40
+
41
+ For Expo projects, run prebuild:
42
+ ```sh
43
+ npx expo prebuild
44
+ ```
45
+
46
+ For bare React Native projects:
47
+ ```sh
48
+ cd ios && pod install
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ```ts
54
+ import { VersionCheck, getCountry, getStoreUrl, getLatestVersion, needsUpdate } from 'react-native-nitro-version-check'
55
+
56
+ // Sync properties
57
+ VersionCheck.version // "1.2.0"
58
+ VersionCheck.buildNumber // "42"
59
+ VersionCheck.packageName // "com.example.app"
60
+ VersionCheck.installSource // "appstore" | "testflight" | "playstore" | undefined
61
+ getCountry() // "US"
62
+
63
+ // Async
64
+ const url = await getStoreUrl() // App Store / Play Store URL
65
+ const latest = await getLatestVersion() // "1.3.0"
66
+
67
+ if (await needsUpdate()) {
68
+ Linking.openURL(await getStoreUrl())
69
+ }
70
+
71
+ // Only prompt for major updates
72
+ if (await needsUpdate({ level: 'major' })) {
73
+ // 1.x → 2.x
74
+ }
75
+ ```
76
+
77
+ ## API
78
+
79
+ ### `VersionCheck`
80
+
81
+ | Property | Type | Description |
82
+ |----------|------|-------------|
83
+ | `version` | `string` | App version |
84
+ | `buildNumber` | `string` | Build number |
85
+ | `packageName` | `string` | Bundle ID / Application ID |
86
+ | `installSource` | `string \| undefined` | `"appstore"` `"testflight"` `"playstore"` or `undefined` |
87
+ | `getCountry()` | `string` | Device's 2-letter ISO country code |
88
+ | `getStoreUrl()` | `Promise<string>` | App Store / Play Store URL |
89
+ | `getLatestVersion()` | `Promise<string>` | Latest version in the store |
90
+ | `needsUpdate()` | `Promise<boolean>` | Whether an update is available |
91
+
92
+ ### Standalone exports
93
+
94
+ Also available as individual named exports:
95
+
96
+ | Export | Returns | Description |
97
+ |--------|---------|-------------|
98
+ | `getCountry()` | `string` | Device's 2-letter ISO country code |
99
+ | `getStoreUrl()` | `Promise<string>` | App Store / Play Store URL |
100
+ | `getLatestVersion()` | `Promise<string>` | Latest version in the store |
101
+ | `needsUpdate(options?)` | `Promise<boolean>` | Whether an update is available |
102
+
103
+ #### `needsUpdate` options
104
+
105
+ | Option | Type | Default | Description |
106
+ |--------|------|---------|-------------|
107
+ | `level` | `"major" \| "minor" \| "patch"` | `"patch"` | Minimum version bump to trigger `true` |
108
+
109
+ ### Utilities
110
+
111
+ | Function | Returns | Description |
112
+ |----------|---------|-------------|
113
+ | `compareVersions(v1, v2)` | `-1 \| 0 \| 1` | Compare two semver strings |
114
+
115
+ ## License
116
+
117
+ MIT
@@ -0,0 +1,29 @@
1
+ project(NitroVersionCheck)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set (PACKAGE_NAME NitroVersionCheck)
5
+ set (CMAKE_VERBOSE_MAKEFILE ON)
6
+ set (CMAKE_CXX_STANDARD 20)
7
+
8
+ # Define C++ library and add all sources
9
+ add_library(${PACKAGE_NAME} SHARED
10
+ src/main/cpp/cpp-adapter.cpp
11
+ )
12
+
13
+ # Add Nitrogen specs :)
14
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroVersionCheck+autolinking.cmake)
15
+
16
+ # Set up local includes
17
+ include_directories(
18
+ "src/main/cpp"
19
+ "../cpp"
20
+ )
21
+
22
+ find_library(LOG_LIB log)
23
+
24
+ # Link all libraries together
25
+ target_link_libraries(
26
+ ${PACKAGE_NAME}
27
+ ${LOG_LIB}
28
+ android # <-- Android core
29
+ )
@@ -0,0 +1,142 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:9.0.1"
9
+ }
10
+ }
11
+
12
+ def reactNativeArchitectures() {
13
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
14
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
15
+ }
16
+
17
+ def isNewArchitectureEnabled() {
18
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
19
+ }
20
+
21
+ apply plugin: "com.android.library"
22
+ apply plugin: 'org.jetbrains.kotlin.android'
23
+ apply from: '../nitrogen/generated/android/NitroVersionCheck+autolinking.gradle'
24
+ apply from: "./fix-prefab.gradle"
25
+
26
+ if (isNewArchitectureEnabled()) {
27
+ apply plugin: "com.facebook.react"
28
+ }
29
+
30
+ def getExtOrDefault(name) {
31
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroVersionCheck_" + name]
32
+ }
33
+
34
+ def getExtOrIntegerDefault(name) {
35
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroVersionCheck_" + name]).toInteger()
36
+ }
37
+
38
+ android {
39
+ namespace "com.margelo.nitro.nitroversioncheck"
40
+
41
+ ndkVersion getExtOrDefault("ndkVersion")
42
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
43
+
44
+ defaultConfig {
45
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
46
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
47
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
48
+
49
+ externalNativeBuild {
50
+ cmake {
51
+ cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
52
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
53
+ abiFilters (*reactNativeArchitectures())
54
+
55
+ buildTypes {
56
+ debug {
57
+ cppFlags "-O1 -g"
58
+ }
59
+ release {
60
+ cppFlags "-O2"
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ externalNativeBuild {
68
+ cmake {
69
+ path "CMakeLists.txt"
70
+ }
71
+ }
72
+
73
+ packagingOptions {
74
+ excludes = [
75
+ "META-INF",
76
+ "META-INF/**",
77
+ "**/libc++_shared.so",
78
+ "**/libNitroModules.so",
79
+ "**/libfbjni.so",
80
+ "**/libjsi.so",
81
+ "**/libfolly_json.so",
82
+ "**/libfolly_runtime.so",
83
+ "**/libglog.so",
84
+ "**/libhermes.so",
85
+ "**/libhermes-executor-debug.so",
86
+ "**/libhermes_executor.so",
87
+ "**/libreactnative.so",
88
+ "**/libreactnativejni.so",
89
+ "**/libturbomodulejsijni.so",
90
+ "**/libreact_nativemodule_core.so",
91
+ "**/libjscexecutor.so"
92
+ ]
93
+ }
94
+
95
+ buildFeatures {
96
+ buildConfig true
97
+ prefab true
98
+ }
99
+
100
+ buildTypes {
101
+ release {
102
+ minifyEnabled false
103
+ }
104
+ }
105
+
106
+ lintOptions {
107
+ disable "GradleCompatible"
108
+ }
109
+
110
+ compileOptions {
111
+ sourceCompatibility JavaVersion.VERSION_1_8
112
+ targetCompatibility JavaVersion.VERSION_1_8
113
+ }
114
+
115
+ sourceSets {
116
+ main {
117
+ if (isNewArchitectureEnabled()) {
118
+ java.srcDirs += [
119
+ // React Codegen files
120
+ "${project.buildDir}/generated/source/codegen/java"
121
+ ]
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ repositories {
128
+ mavenCentral()
129
+ google()
130
+ }
131
+
132
+
133
+ dependencies {
134
+ // For < 0.71, this will be from the local maven repo
135
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
136
+ //noinspection GradleDynamicVersion
137
+ implementation "com.facebook.react:react-native:+"
138
+
139
+ // Add a dependency on NitroModules
140
+ implementation project(":react-native-nitro-modules")
141
+ }
142
+
@@ -0,0 +1,51 @@
1
+ tasks.configureEach { task ->
2
+ // Make sure that we generate our prefab publication file only after having built the native library
3
+ // so that not a header publication file, but a full configuration publication will be generated, which
4
+ // will include the .so file
5
+
6
+ def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/
7
+ def matcher = task.name =~ prefabConfigurePattern
8
+ if (matcher.matches()) {
9
+ def variantName = matcher[0][1]
10
+ task.outputs.upToDateWhen { false }
11
+ task.dependsOn("externalNativeBuild${variantName}")
12
+ }
13
+ }
14
+
15
+ afterEvaluate {
16
+ def abis = reactNativeArchitectures()
17
+ rootProject.allprojects.each { proj ->
18
+ if (proj === rootProject) return
19
+
20
+ def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config ->
21
+ config.dependencies.any { dep ->
22
+ dep.group == project.group && dep.name == project.name
23
+ }
24
+ }
25
+ if (!dependsOnThisLib && proj != project) return
26
+
27
+ if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) {
28
+ return
29
+ }
30
+
31
+ def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants
32
+ // Touch the prefab_config.json files to ensure that in ExternalNativeJsonGenerator.kt we will re-trigger the prefab CLI to
33
+ // generate a libnameConfig.cmake file that will contain our native library (.so).
34
+ // See this condition: https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/ExternalNativeJsonGenerator.kt;l=207-219?q=createPrefabBuildSystemGlue
35
+ variants.all { variant ->
36
+ def variantName = variant.name
37
+ abis.each { abi ->
38
+ def searchDir = new File(proj.projectDir, ".cxx/${variantName}")
39
+ if (!searchDir.exists()) return
40
+ def matches = []
41
+ searchDir.eachDir { randomDir ->
42
+ def prefabFile = new File(randomDir, "${abi}/prefab_config.json")
43
+ if (prefabFile.exists()) matches << prefabFile
44
+ }
45
+ matches.each { prefabConfig ->
46
+ prefabConfig.setLastModified(System.currentTimeMillis())
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,5 @@
1
+ NitroVersionCheck_kotlinVersion=2.1.20
2
+ NitroVersionCheck_minSdkVersion=23
3
+ NitroVersionCheck_targetSdkVersion=36
4
+ NitroVersionCheck_compileSdkVersion=36
5
+ NitroVersionCheck_ndkVersion=27.1.12297006
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,9 @@
1
+ #include <jni.h>
2
+ #include <fbjni/fbjni.h>
3
+ #include "NitroVersionCheckOnLoad.hpp"
4
+
5
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
6
+ return facebook::jni::initialize(vm, []() {
7
+ margelo::nitro::nitroversioncheck::registerAllNatives();
8
+ });
9
+ }
@@ -0,0 +1,74 @@
1
+ package com.margelo.nitro.nitroversioncheck
2
+
3
+ import com.margelo.nitro.NitroModules
4
+ import com.margelo.nitro.core.Promise
5
+ import java.net.HttpURLConnection
6
+ import java.net.URL
7
+
8
+ class HybridVersionCheck : HybridVersionCheckSpec() {
9
+ companion object {
10
+ private const val TIMEOUT_MS = 15_000
11
+ private val context = NitroModules.applicationContext
12
+ private val packageInfo = context?.packageManager?.getPackageInfo(context.packageName, 0)
13
+ }
14
+
15
+ public override val version = packageInfo?.versionName ?: "unknown"
16
+
17
+ public override val buildNumber = if (android.os.Build.VERSION.SDK_INT >= 28) {
18
+ if (packageInfo?.longVersionCode != null) packageInfo.longVersionCode.toString() else "unknown"
19
+ } else {
20
+ @Suppress("DEPRECATION")
21
+ if (packageInfo?.versionCode != null) packageInfo.versionCode.toString() else "unknown"
22
+ }
23
+
24
+ public override val packageName = packageInfo?.packageName ?: "unknown"
25
+ public override val installSource: String? = run {
26
+ val installer = if (android.os.Build.VERSION.SDK_INT >= 30) {
27
+ context?.packageManager?.getInstallSourceInfo(context.packageName)?.installingPackageName
28
+ } else {
29
+ @Suppress("DEPRECATION")
30
+ context?.packageManager?.getInstallerPackageName(context.packageName)
31
+ }
32
+ if (installer != null) "playstore" else null
33
+ }
34
+
35
+ override fun getCountry(): String {
36
+ return java.util.Locale.getDefault().country ?: "unknown"
37
+ }
38
+
39
+ override fun getStoreUrl(): Promise<String> {
40
+ return Promise.async {
41
+ "https://play.google.com/store/apps/details?id=$packageName"
42
+ }
43
+ }
44
+
45
+ override fun getLatestVersion(): Promise<String> {
46
+ return Promise.async {
47
+ try {
48
+ val url = URL("https://play.google.com/store/apps/details?id=$packageName&hl=en")
49
+ val connection = url.openConnection() as HttpURLConnection
50
+ connection.connectTimeout = TIMEOUT_MS
51
+ connection.readTimeout = TIMEOUT_MS
52
+ connection.setRequestProperty("User-Agent", "Mozilla/5.0")
53
+ val html = connection.inputStream.bufferedReader().use { it.readText() }
54
+ val regex = Regex("""\]\]\],\s*"(\d+\.\d+[\d.]*\d)"""")
55
+ val match = regex.find(html)
56
+ match?.groupValues?.get(1)
57
+ ?: throw Exception("Could not parse latest version from Play Store page")
58
+ } catch (e: Exception) {
59
+ throw Exception("Failed to fetch latest version: ${e.message}", e)
60
+ }
61
+ }
62
+ }
63
+
64
+ override fun needsUpdate(): Promise<Boolean> {
65
+ return Promise.async {
66
+ try {
67
+ val latest = getLatestVersion().await()
68
+ version != latest
69
+ } catch (e: Exception) {
70
+ throw Exception("Failed to check for updates: ${e.message}", e)
71
+ }
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,18 @@
1
+ package com.margelo.nitro.nitroversioncheck
2
+
3
+ import com.facebook.react.bridge.NativeModule
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.module.model.ReactModuleInfoProvider
6
+ import com.facebook.react.BaseReactPackage
7
+
8
+ class NitroVersionCheckPackage : BaseReactPackage() {
9
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = null
10
+
11
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { HashMap() }
12
+
13
+ companion object {
14
+ init {
15
+ NitroVersionCheckOnLoad.initializeNative()
16
+ }
17
+ }
18
+ }
package/ios/Bridge.h ADDED
@@ -0,0 +1,8 @@
1
+ //
2
+ // Bridge.h
3
+ // NitroVersionCheck
4
+ //
5
+ // Created by Marc Rousavy on 22.07.24.
6
+ //
7
+
8
+ #pragma once
@@ -0,0 +1,71 @@
1
+ import Foundation
2
+ import NitroModules
3
+
4
+ class HybridVersionCheck: HybridVersionCheckSpec {
5
+ private static let session: URLSession = {
6
+ let config = URLSessionConfiguration.default
7
+ config.timeoutIntervalForRequest = 15
8
+ config.timeoutIntervalForResource = 30
9
+ return URLSession(configuration: config)
10
+ }()
11
+
12
+ var version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown"
13
+ var buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown"
14
+ var packageName = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? "unknown"
15
+ var installSource: String? = {
16
+ guard let receiptURL = Bundle.main.appStoreReceiptURL,
17
+ FileManager.default.fileExists(atPath: receiptURL.path) else {
18
+ return nil
19
+ }
20
+ if receiptURL.lastPathComponent == "sandboxReceipt" {
21
+ return "testflight"
22
+ }
23
+ return "appstore"
24
+ }()
25
+
26
+ func getCountry() throws -> String {
27
+ if #available(iOS 16, *) {
28
+ return Locale.current.region?.identifier ?? "unknown"
29
+ }
30
+ return Locale.current.regionCode ?? "unknown"
31
+ }
32
+
33
+ func getStoreUrl() throws -> Promise<String> {
34
+ return Promise.async {
35
+ let bundleId = Bundle.main.bundleIdentifier ?? ""
36
+ let url = URL(string: "https://itunes.apple.com/lookup?bundleId=\(bundleId)")!
37
+ let (data, _) = try await HybridVersionCheck.session.data(from: url)
38
+ guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
39
+ let results = json["results"] as? [[String: Any]],
40
+ let trackViewUrl = results.first?["trackViewUrl"] as? String else {
41
+ throw NSError(domain: "VersionCheck", code: 1, userInfo: [NSLocalizedDescriptionKey: "App not found on App Store"])
42
+ }
43
+ return trackViewUrl
44
+ }
45
+ }
46
+
47
+ func getLatestVersion() throws -> Promise<String> {
48
+ return Promise.async {
49
+ let bundleId = Bundle.main.bundleIdentifier ?? ""
50
+ let url = URL(string: "https://itunes.apple.com/lookup?bundleId=\(bundleId)")!
51
+ let (data, _) = try await HybridVersionCheck.session.data(from: url)
52
+ guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
53
+ let results = json["results"] as? [[String: Any]],
54
+ let latestVersion = results.first?["version"] as? String else {
55
+ throw NSError(domain: "VersionCheck", code: 2, userInfo: [NSLocalizedDescriptionKey: "App not found on App Store"])
56
+ }
57
+ return latestVersion
58
+ }
59
+ }
60
+
61
+ func needsUpdate() throws -> Promise<Bool> {
62
+ return Promise.async { [self] in
63
+ do {
64
+ let latest = try await self.getLatestVersion().await()
65
+ return self.version != latest
66
+ } catch {
67
+ throw NSError(domain: "VersionCheck", code: 3, userInfo: [NSLocalizedDescriptionKey: "Failed to check for updates: \(error.localizedDescription)"])
68
+ }
69
+ }
70
+ }
71
+ }