@momo-kits/native-kits 0.167.1-beta.15-debug → 0.167.1-beta.16-debug
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.
- package/CLAUDE.md +6 -4
- package/compose/build.gradle.kts +1 -1
- package/compose/compose.podspec +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +5 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +2 -2
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -38
- package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +32 -0
- package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +16 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +6 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +5 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +60 -0
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +13 -6
- package/ios/Application/DesignSystemKits.swift +100 -0
- package/ios/Application/DesignSystemKitsPayload.swift +68 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +39 -7
- package/ios/Typography/FontScale.swift +14 -11
- package/ios/Typography/Text.swift +19 -7
- package/ios/Typography/Typography.swift +2 -2
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/local.properties +8 -0
- package/package.json +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
- package/ios/Typography/FontScaleStore.swift +0 -58
- package/ios/Typography/KitDesignSystem.swift +0 -17
package/CLAUDE.md
CHANGED
|
@@ -41,7 +41,9 @@ Publishing is automated (`publish.sh` + GitLab CI on `[ci build]` / `main`). Do
|
|
|
41
41
|
|
|
42
42
|
- **Entry point:** wrap content in `NavigationContainer(...)` from `navigation/`. It builds the `Navigator`, takes a host-supplied `Localize` (falls back to an empty one), and provides the CompositionLocals below. **Do not use** `ApplicationContainer` (`application/NavigationContainer.kt`) — deprecated and does **not** wire `LocalLocalize` / `LocalNavigator` / `LocalMaxApi`.
|
|
43
43
|
- **Configuration flows via CompositionLocals**, not params. Public ones: `AppTheme`, `AppStatusBar`, `AppNavigationBar` (`const/Theme.kt`); `ApplicationContext`, `AppConfig`, `AppLanguage`, `LocalLocalize` (`application/`); `LocalNavigator`, `LocalMaxApi` (`navigation/`). Read with `X.current`.
|
|
44
|
-
-
|
|
44
|
+
- **`DesignSystemKits`** (`application/DesignSystemKits.kt`) is the single app-wide holder for the host-owned design-system inputs: font scale (`userScaleRate` / `useOSScaleRate`), whitelist (`isWhiteList`) and the remote `DesignSystemConfig`, bundled as `DesignSystemKitsConfigs`. The host calls `DesignSystemKits.init(source)` once at startup with its `DesignSystemKitsSource` (sync `getConfigs()` seed + `configsFlow`); `init` is idempotent. Without a source, call `setConfigs(...)` directly. Read with `getConfigs()` (snapshot state, recomposes); non-Compose observers use `configsFlow`.
|
|
45
|
+
- **SwiftUI twin:** `ios/Application/DesignSystemKits.swift` mirrors it field for field (`DesignSystemKitsConfigs` with `designSystemConfig: KitConfig?`, `setConfigs` / `getConfigs`), plus the OS Dynamic Type factor (`osFontScale`). There is no `init(source:)` (Swift reserves `init`): the host observes its own bridge and pushes. A host that can't share Swift types (the KMP bridge) calls `DesignSystemKits.shared.setConfigs(json:)` (`ios/Application/DesignSystemKitsPayload.swift`); the JSON field names are a contract with momo-app's serializer (`HostDesignSystemKits.observeConfigsJson`, payload classes in `DesignSystemData.kt`) — change both together. Invalid JSON is ignored. The SwiftUI kit can't link the Kotlin `kits` framework (a second Kotlin/Native runtime would hold a second, never-initialized singleton), so the render-time logic — `FontScale` formula, Dynamic Type observation — is intentionally duplicated in Swift; only values flow across.
|
|
46
|
+
- **Design-system config** (`trustBanner`, `headerBar`, `headerGradient`): `NavigationContainer` provides it as `LocalDesignSystemConfig` (param `designSystemConfig` → parent → `DesignSystemKits`) and applies `headerBar` as the header image when the theme has none.
|
|
45
47
|
- **Platform code** goes through `expect`/`actual` in `platform/` — never reference Android/iOS APIs from `commonMain` directly.
|
|
46
48
|
- **Native bridge:** `IMaxApi` (`vn.momo.maxapi`, exposed as `LocalMaxApi`) is the host-app bridge (dismiss, tracking, device info, language).
|
|
47
49
|
- Navigation uses a runtime `DynamicScreenRegistry`; overlays (modal/bottom-sheet/snackbar) go through `Navigator` + `OverplayComponentRegistry`.
|
|
@@ -69,9 +71,9 @@ The SwiftUI port differs from Compose in one important way: **it follows the hos
|
|
|
69
71
|
|
|
70
72
|
One formula, both platforms: the device-width scale (`width / 375`, capped at `+5pt`) and the OS Dynamic Type scale (capped at `1.5×`) are computed independently and the **larger wins**. When `useOSScaleRate` is false neither applies — the size is just multiplied by `userScaleRate`. Max font scale is fixed at 1.5; the old per-call/per-scope override is deprecated and ignored on both sides.
|
|
71
73
|
|
|
72
|
-
- **Compose** — `const/Typography.kt` (`scaleSize` overloads for `Float`/`Dp`/`TextUnit`/`TextStyle`). The `TextUnit` overload divides by `density.fontScale` so Compose's own `sp` scaling isn't applied twice. Inputs: `LocalContext.current` (`MiniAppContext.userScaleRate` / `useOSScaleRate`, per-field) falling back to
|
|
73
|
-
- **SwiftUI** — `ios/Typography/FontScale.swift`. `FontScale` is an immutable snapshot with `callAsFunction`, so a view reads `@Environment(\.momoFontScale) private var fontScale` and calls `fontScale(16)`. `
|
|
74
|
-
- **Never set `\.momoFontScale` directly** — same trap as `\.localize`. Inject via `.momoFontScale(_:context:)`, which holds
|
|
74
|
+
- **Compose** — `const/Typography.kt` (`scaleSize` overloads for `Float`/`Dp`/`TextUnit`/`TextStyle`). The `TextUnit` overload divides by `density.fontScale` so Compose's own `sp` scaling isn't applied twice. Inputs: `LocalContext.current` (`MiniAppContext.userScaleRate` / `useOSScaleRate`, per-field) falling back to `DesignSystemKits.getConfigs()`. On iOS, `platform/OsFontScale.ios.kt` observes `UIContentSizeCategoryDidChangeNotification` and overrides `LocalDensity` — Compose MP otherwise reads `fontScale` once at `ComposeUIViewController` creation. Wired at `NavigationContainer`.
|
|
75
|
+
- **SwiftUI** — `ios/Typography/FontScale.swift`. `FontScale` is an immutable snapshot with `callAsFunction`, so a view reads `@Environment(\.momoFontScale) private var fontScale` and calls `fontScale(16)`. `DesignSystemKits.shared` is the `ObservableObject` holding the host inputs and the OS factor (it observes `UIContentSizeCategory.didChangeNotification`); the host pushes them with `setConfigs(_:)`.
|
|
76
|
+
- **Never set `\.momoFontScale` directly** — same trap as `\.localize`. Inject via `.momoFontScale(_:context:)`, which holds `DesignSystemKits` as `@ObservedObject`; that is what re-publishes the snapshot. It also injects `\.designSystemKitsConfigs`, which `MomoText` (whitelist font) and `TrustBanner` (fallback after `ApplicationEnvironment.config`) read so a host push re-renders them. `NavigationContainer` does it (passing `MiniAppContext` so the per-mini-app override applies); a bare `UIHostingController` root must too. The environment default reads the live store, so an un-injected subtree still gets correct values — just not live ones.
|
|
75
77
|
- The global `scaleSize(_:)` and `Font.appFont(size:)` remain for call sites that can't read the environment (UIKit bridges, free functions). They are **not** reactive. `Font.header_default_bold` & friends are computed `static var`s for the same reason — as stored `static let`s they froze the scale at first access. `UIViewRepresentable`s should read `context.environment.momoFontScale` instead, and re-apply the font in `updateUIView`, not only in `makeUIView`.
|
|
76
78
|
|
|
77
79
|
## Code conventions
|
package/compose/build.gradle.kts
CHANGED
package/compose/compose.podspec
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
package vn.momo.kits.application
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.Immutable
|
|
4
|
-
import androidx.compose.runtime.getValue
|
|
5
|
-
import androidx.compose.runtime.mutableStateOf
|
|
6
|
-
import androidx.compose.runtime.setValue
|
|
7
4
|
import androidx.compose.runtime.staticCompositionLocalOf
|
|
8
5
|
import vn.momo.kits.components.TrustBannerData
|
|
9
6
|
|
|
@@ -19,17 +16,6 @@ data class DesignSystemConfig(
|
|
|
19
16
|
val headerGradient: String? = null,
|
|
20
17
|
)
|
|
21
18
|
|
|
22
|
-
// App-wide holder seeded by the host (same pattern as KitFontScale), so direct NavigationContainer
|
|
23
|
-
// callers get the config without passing it. Snapshot state: a config fetched after first frame recomposes.
|
|
24
|
-
object KitDesignSystemConfig {
|
|
25
|
-
var config by mutableStateOf<DesignSystemConfig?>(null)
|
|
26
|
-
private set
|
|
27
|
-
|
|
28
|
-
fun update(config: DesignSystemConfig?) {
|
|
29
|
-
this.config = config
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
19
|
@Immutable
|
|
34
20
|
data class MiniAppContext(
|
|
35
21
|
val appIcon: String = "",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package vn.momo.kits.application
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.Immutable
|
|
4
|
+
import androidx.compose.runtime.getValue
|
|
5
|
+
import androidx.compose.runtime.mutableStateOf
|
|
6
|
+
import androidx.compose.runtime.setValue
|
|
7
|
+
import kotlinx.coroutines.CoroutineScope
|
|
8
|
+
import kotlinx.coroutines.Dispatchers
|
|
9
|
+
import kotlinx.coroutines.Job
|
|
10
|
+
import kotlinx.coroutines.SupervisorJob
|
|
11
|
+
import kotlinx.coroutines.flow.Flow
|
|
12
|
+
import kotlinx.coroutines.flow.MutableStateFlow
|
|
13
|
+
import kotlinx.coroutines.flow.StateFlow
|
|
14
|
+
import kotlinx.coroutines.flow.asStateFlow
|
|
15
|
+
import kotlinx.coroutines.launch
|
|
16
|
+
|
|
17
|
+
@Immutable
|
|
18
|
+
data class DesignSystemKitsConfigs(
|
|
19
|
+
val userScaleRate: Float = 1f,
|
|
20
|
+
val useOSScaleRate: Boolean = true,
|
|
21
|
+
val isWhiteList: Boolean = false,
|
|
22
|
+
val designSystemConfig: DesignSystemConfig? = null,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// The kit is sandboxed from the host's storage/config, so the host implements this and hands it to init().
|
|
26
|
+
interface DesignSystemKitsSource {
|
|
27
|
+
// Sync read: seeds the kit before the first frame, so there is no default-then-real flicker.
|
|
28
|
+
fun getConfigs(): DesignSystemKitsConfigs
|
|
29
|
+
|
|
30
|
+
val configsFlow: Flow<DesignSystemKitsConfigs>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// App-wide design-system inputs (font scale, whitelist, remote config). scaleSize(), defaultTheme and
|
|
34
|
+
// NavigationContainer fall back to these when no MiniAppContext / LocalDesignSystemConfig is provided.
|
|
35
|
+
object DesignSystemKits {
|
|
36
|
+
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
|
37
|
+
private var observeJob: Job? = null
|
|
38
|
+
|
|
39
|
+
// Snapshot state so Compose readers recompose (including non-composable getters like defaultTheme);
|
|
40
|
+
// the StateFlow is for non-Compose observers, e.g. the host's SwiftUI bridge, where Compose's
|
|
41
|
+
// global snapshot apply loop may never have started.
|
|
42
|
+
private var currentConfigs by mutableStateOf(DesignSystemKitsConfigs())
|
|
43
|
+
private val mutableConfigsFlow = MutableStateFlow(currentConfigs)
|
|
44
|
+
val configsFlow: StateFlow<DesignSystemKitsConfigs> = mutableConfigsFlow.asStateFlow()
|
|
45
|
+
|
|
46
|
+
// Idempotent: a repeated call (e.g. Activity re-creation) replaces the previous observer.
|
|
47
|
+
fun init(source: DesignSystemKitsSource) {
|
|
48
|
+
observeJob?.cancel()
|
|
49
|
+
setConfigs(source.getConfigs())
|
|
50
|
+
observeJob = scope.launch {
|
|
51
|
+
source.configsFlow.collect { setConfigs(it) }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// While init() is observing a source, its next emission overrides a value set here.
|
|
56
|
+
fun setConfigs(configs: DesignSystemKitsConfigs) {
|
|
57
|
+
currentConfigs = configs
|
|
58
|
+
mutableConfigsFlow.value = configs
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fun getConfigs(): DesignSystemKitsConfigs = currentConfigs
|
|
62
|
+
}
|
|
@@ -10,10 +10,6 @@ import androidx.compose.ui.text.font.FontFamily
|
|
|
10
10
|
* - Android: `assets/fonts/<fileName>` (gradle task `syncMomoFonts`)
|
|
11
11
|
* - iOS: file rời ở bundle root (pbxproj fileRef được `post_install.rb` trỏ về font-source)
|
|
12
12
|
*
|
|
13
|
-
* AI-GENERATED START: note SF Pro -> iOS system font (ITMS-91182)
|
|
14
|
-
* Ngoại lệ: SF Pro trên iOS dùng system font (chính là SF Pro) — Apple cấm app nhúng font của Apple.
|
|
15
|
-
* AI-GENERATED END: note SF Pro -> iOS system font (ITMS-91182)
|
|
16
|
-
*
|
|
17
13
|
* Trả về `null` nếu host không có font đó — caller tự fallback [FontFamily.Default].
|
|
18
14
|
*/
|
|
19
15
|
@Composable
|
|
@@ -8,6 +8,7 @@ import androidx.compose.ui.text.font.FontFamily
|
|
|
8
8
|
import androidx.compose.ui.text.font.FontWeight
|
|
9
9
|
import androidx.compose.ui.text.style.TextDecoration
|
|
10
10
|
import androidx.compose.ui.unit.*
|
|
11
|
+
import vn.momo.kits.application.DesignSystemKits
|
|
11
12
|
import vn.momo.kits.application.LocalContext
|
|
12
13
|
import vn.momo.kits.platform.getScreenDimensions
|
|
13
14
|
import kotlin.math.max
|
|
@@ -20,10 +21,9 @@ const val MAX_DEVICE_SCALE = 5
|
|
|
20
21
|
@Composable
|
|
21
22
|
fun scaleSize(size: Float): Float {
|
|
22
23
|
val context = LocalContext.current
|
|
23
|
-
|
|
24
|
-
val useOsScale = context?.useOSScaleRate ?: KitFontScale.useOSScaleRate
|
|
24
|
+
val useOsScale = context?.useOSScaleRate ?: DesignSystemKits.getConfigs().useOSScaleRate
|
|
25
25
|
if (!useOsScale) {
|
|
26
|
-
return size * (context?.userScaleRate ?:
|
|
26
|
+
return size * (context?.userScaleRate ?: DesignSystemKits.getConfigs().userScaleRate)
|
|
27
27
|
}
|
|
28
28
|
val scaleSizeMaxRate: Float = MAX_FONT_SCALE
|
|
29
29
|
val deviceWidth = getScreenDimensions().width
|
|
@@ -83,12 +83,6 @@ fun scaleSize(textStyle: TextStyle): TextStyle {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
/**
|
|
87
|
-
* Tên file font trong tài nguyên native của app host (xem [hostFontFamily]).
|
|
88
|
-
*
|
|
89
|
-
* SDK không đóng gói font riêng nữa: app host đã bundle sẵn toàn bộ font từ `font-source`
|
|
90
|
-
* cho cả React Native lẫn native, nên bản copy trong `composeResources` chỉ làm phình app.
|
|
91
|
-
*/
|
|
92
86
|
private const val LEGACY_DEFAULT_FONT_FILE = "SFProText-Regular.ttf"
|
|
93
87
|
private const val NEW_DEFAULT_FONT_FILE = "MomoTrustSans-Regular.ttf"
|
|
94
88
|
|
|
@@ -124,11 +118,11 @@ private const val NEW_DEFAULT_FONT_FAMILY = "MomoTrustSans"
|
|
|
124
118
|
private const val LEGACY_DEFAULT_FONT_FAMILY = "SFProText"
|
|
125
119
|
|
|
126
120
|
internal val defaultFontFamily: String
|
|
127
|
-
get() = if (
|
|
121
|
+
get() = if (DesignSystemKits.getConfigs().isWhiteList) NEW_DEFAULT_FONT_FAMILY else LEGACY_DEFAULT_FONT_FAMILY
|
|
128
122
|
|
|
129
123
|
@Composable
|
|
130
124
|
fun getFontFamily(fontFamily: String, fontWeight: FontWeight? = FontWeight.Normal): FontFamily {
|
|
131
|
-
val isWhiteList =
|
|
125
|
+
val isWhiteList = DesignSystemKits.getConfigs().isWhiteList
|
|
132
126
|
val defaultFile = if (isWhiteList) NEW_DEFAULT_FONT_FILE else LEGACY_DEFAULT_FONT_FILE
|
|
133
127
|
val key = "$fontFamily-${fontWeight?.weight}"
|
|
134
128
|
val fileName = hostFontFiles[key] ?: defaultFile
|
|
@@ -20,7 +20,7 @@ import androidx.navigation.toRoute
|
|
|
20
20
|
import kotlinx.coroutines.delay
|
|
21
21
|
import kotlinx.coroutines.flow.collectLatest
|
|
22
22
|
import vn.momo.kits.application.DesignSystemConfig
|
|
23
|
-
import vn.momo.kits.application.
|
|
23
|
+
import vn.momo.kits.application.DesignSystemKits
|
|
24
24
|
import vn.momo.kits.application.LocalContext
|
|
25
25
|
import vn.momo.kits.application.LocalDesignSystemConfig
|
|
26
26
|
import vn.momo.kits.application.LocalLocalize
|
|
@@ -63,7 +63,7 @@ fun NavigationContainer(
|
|
|
63
63
|
val stackTransition = remember { StackTransitionCoordinator() }
|
|
64
64
|
val theme = remember { mutableStateOf(initialTheme) }
|
|
65
65
|
val resolvedDesignSystemConfig =
|
|
66
|
-
designSystemConfig ?: LocalDesignSystemConfig.current ?:
|
|
66
|
+
designSystemConfig ?: LocalDesignSystemConfig.current ?: DesignSystemKits.getConfigs().designSystemConfig
|
|
67
67
|
val headerBar = resolvedDesignSystemConfig?.headerBar
|
|
68
68
|
// RN parity: the theme's own header image wins; config headerBar is only the fallback.
|
|
69
69
|
val resolvedTheme = remember(theme.value, headerBar) {
|
|
@@ -8,13 +8,13 @@ import androidx.compose.ui.text.style.TextAlign
|
|
|
8
8
|
import androidx.compose.ui.text.style.TextOverflow
|
|
9
9
|
import androidx.compose.ui.unit.sp
|
|
10
10
|
import androidx.compose.ui.zIndex
|
|
11
|
+
import vn.momo.kits.application.DesignSystemKits
|
|
11
12
|
import vn.momo.kits.components.Text
|
|
12
|
-
import vn.momo.kits.const.KitDesignSystem
|
|
13
13
|
import vn.momo.kits.const.Typography
|
|
14
14
|
import vn.momo.kits.modifier.setAutomationId
|
|
15
15
|
|
|
16
16
|
internal val headerFontFamily: String?
|
|
17
|
-
get() = if (
|
|
17
|
+
get() = if (DesignSystemKits.getConfigs().isWhiteList) "MoMoTrustDisplay" else null
|
|
18
18
|
|
|
19
19
|
@Composable
|
|
20
20
|
internal fun HeaderTitle(
|
|
@@ -4,12 +4,6 @@ package vn.momo.kits.const
|
|
|
4
4
|
|
|
5
5
|
import androidx.compose.runtime.Composable
|
|
6
6
|
import androidx.compose.runtime.remember
|
|
7
|
-
// AI-GENERATED START: imports for the SF Pro -> iOS system font mapping (ITMS-91182)
|
|
8
|
-
import androidx.compose.ui.text.ExperimentalTextApi
|
|
9
|
-
import androidx.compose.ui.text.font.FontWeight
|
|
10
|
-
import androidx.compose.ui.text.platform.SystemFont
|
|
11
|
-
import platform.UIKit.UIFont
|
|
12
|
-
// AI-GENERATED END: imports for the SF Pro -> iOS system font mapping (ITMS-91182)
|
|
13
7
|
import androidx.compose.ui.text.font.FontFamily
|
|
14
8
|
import androidx.compose.ui.text.platform.Font
|
|
15
9
|
import kotlinx.cinterop.ExperimentalForeignApi
|
|
@@ -20,40 +14,8 @@ import platform.Foundation.NSData
|
|
|
20
14
|
import platform.Foundation.dataWithContentsOfFile
|
|
21
15
|
import platform.posix.memcpy
|
|
22
16
|
|
|
23
|
-
// AI-GENERATED START: SF Pro -> iOS system font mapping (ITMS-91182)
|
|
24
|
-
/**
|
|
25
|
-
* Apple cấm app nhúng font của Apple (ITMS-91182) và font hệ thống iOS chính là SF Pro, nên file SF Pro
|
|
26
|
-
* được thay bằng system font cùng weight thay vì đọc từ bundle — host không cần (và không được) ship nó.
|
|
27
|
-
*
|
|
28
|
-
* Weight đi theo tên face của Apple (Ultralight nhẹ hơn Thin), khớp thang weight CoreText mà Skia dùng
|
|
29
|
-
* (100 = UltraLight, 200 = Thin), để ra đúng face mà tên file chỉ định.
|
|
30
|
-
* Phải mang weight trong Font vì `Text` bỏ `fontWeight` khỏi style (weight nằm sẵn trong font).
|
|
31
|
-
*/
|
|
32
|
-
private val systemFontWeights: Map<String, FontWeight> = mapOf(
|
|
33
|
-
"SFProText-Ultralight.ttf" to FontWeight.Thin,
|
|
34
|
-
"SFProText-Thin.ttf" to FontWeight.ExtraLight,
|
|
35
|
-
"SFProText-Light.ttf" to FontWeight.Light,
|
|
36
|
-
"SFProText-Regular.ttf" to FontWeight.Normal,
|
|
37
|
-
"SFProText-Medium.ttf" to FontWeight.Medium,
|
|
38
|
-
"SFProText-Semibold.ttf" to FontWeight.SemiBold,
|
|
39
|
-
"SFProText-Bold.ttf" to FontWeight.Bold,
|
|
40
|
-
"SFProText-Heavy.ttf" to FontWeight.ExtraBold,
|
|
41
|
-
"SFProText-Black.ttf" to FontWeight.Black,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
/** Family của system font (`.AppleSystemUIFont`), lấy qua API chính thức thay vì hard-code tên ẩn. */
|
|
45
|
-
private val systemFontFamilyName: String by lazy { UIFont.systemFontOfSize(14.0).familyName }
|
|
46
|
-
|
|
47
|
-
@OptIn(ExperimentalTextApi::class)
|
|
48
|
-
private fun systemFontFamily(weight: FontWeight): FontFamily =
|
|
49
|
-
FontFamily(SystemFont(identity = systemFontFamilyName, weight = weight))
|
|
50
|
-
// AI-GENERATED END: SF Pro -> iOS system font mapping (ITMS-91182)
|
|
51
|
-
|
|
52
17
|
@Composable
|
|
53
18
|
internal actual fun hostFontFamily(fileName: String): FontFamily? = remember(fileName) {
|
|
54
|
-
// AI-GENERATED START: SF Pro -> iOS system font mapping (ITMS-91182)
|
|
55
|
-
systemFontWeights[fileName]?.let { weight -> return@remember systemFontFamily(weight) }
|
|
56
|
-
// AI-GENERATED END: SF Pro -> iOS system font mapping (ITMS-91182)
|
|
57
19
|
val name = fileName.substringBeforeLast('.')
|
|
58
20
|
val ext = fileName.substringAfterLast('.', "")
|
|
59
21
|
// Font từ font-source nằm rời ở bundle root, không nằm trong compose-resources.
|
package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Scheme
|
|
3
|
+
version = "1.3">
|
|
4
|
+
<BuildAction>
|
|
5
|
+
<BuildActionEntries>
|
|
6
|
+
<BuildActionEntry
|
|
7
|
+
buildForRunning = "YES">
|
|
8
|
+
<BuildableReference
|
|
9
|
+
BuildableIdentifier = "primary"
|
|
10
|
+
BlueprintIdentifier = "A6A920D22B2ADD1E00C1A11F"
|
|
11
|
+
BuildableName = "Example"
|
|
12
|
+
BlueprintName = "Example"
|
|
13
|
+
ReferencedContainer = "container:Example.xcodeproj">
|
|
14
|
+
</BuildableReference>
|
|
15
|
+
</BuildActionEntry>
|
|
16
|
+
</BuildActionEntries>
|
|
17
|
+
</BuildAction>
|
|
18
|
+
<LaunchAction
|
|
19
|
+
useCustomWorkingDirectory = "NO"
|
|
20
|
+
buildConfiguration = "Debug"
|
|
21
|
+
allowLocationSimulation = "YES">
|
|
22
|
+
<BuildableProductRunnable>
|
|
23
|
+
<BuildableReference
|
|
24
|
+
BuildableIdentifier = "primary"
|
|
25
|
+
BlueprintIdentifier = "A6A920D22B2ADD1E00C1A11F"
|
|
26
|
+
BuildableName = "Example"
|
|
27
|
+
BlueprintName = "Example"
|
|
28
|
+
ReferencedContainer = "container:Example.xcodeproj">
|
|
29
|
+
</BuildableReference>
|
|
30
|
+
</BuildableProductRunnable>
|
|
31
|
+
</LaunchAction>
|
|
32
|
+
</Scheme>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>Example.xcscheme</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>7</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>BuildLocationStyle</key>
|
|
6
|
+
<string>UseAppPreferences</string>
|
|
7
|
+
<key>CustomBuildLocationType</key>
|
|
8
|
+
<string>RelativeToDerivedData</string>
|
|
9
|
+
<key>DerivedDataCustomLocation</key>
|
|
10
|
+
<string>DerivedData</string>
|
|
11
|
+
<key>DerivedDataLocationStyle</key>
|
|
12
|
+
<string>WorkspaceRelativePath</string>
|
|
13
|
+
<key>ShowSharedSchemesAutomaticallyEnabled</key>
|
|
14
|
+
<true/>
|
|
15
|
+
</dict>
|
|
16
|
+
</plist>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Scheme
|
|
3
|
+
LastUpgradeVersion = "1600"
|
|
4
|
+
version = "1.3">
|
|
5
|
+
<BuildAction
|
|
6
|
+
parallelizeBuildables = "YES"
|
|
7
|
+
buildImplicitDependencies = "YES">
|
|
8
|
+
<BuildActionEntries>
|
|
9
|
+
<BuildActionEntry
|
|
10
|
+
buildForTesting = "YES"
|
|
11
|
+
buildForRunning = "YES"
|
|
12
|
+
buildForProfiling = "YES"
|
|
13
|
+
buildForArchiving = "YES"
|
|
14
|
+
buildForAnalyzing = "YES">
|
|
15
|
+
<BuildableReference
|
|
16
|
+
BuildableIdentifier = "primary"
|
|
17
|
+
BlueprintIdentifier = "3B6FB503A75BF5BC1FA6F30BC06B9D28"
|
|
18
|
+
BuildableName = "MoMoUIKits.framework"
|
|
19
|
+
BlueprintName = "MoMoUIKits"
|
|
20
|
+
ReferencedContainer = "container:Pods.xcodeproj">
|
|
21
|
+
</BuildableReference>
|
|
22
|
+
</BuildActionEntry>
|
|
23
|
+
</BuildActionEntries>
|
|
24
|
+
</BuildAction>
|
|
25
|
+
<TestAction
|
|
26
|
+
buildConfiguration = "Debug"
|
|
27
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
28
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
29
|
+
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
30
|
+
<Testables>
|
|
31
|
+
</Testables>
|
|
32
|
+
</TestAction>
|
|
33
|
+
<LaunchAction
|
|
34
|
+
buildConfiguration = "Debug"
|
|
35
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
36
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
37
|
+
launchStyle = "0"
|
|
38
|
+
useCustomWorkingDirectory = "NO"
|
|
39
|
+
ignoresPersistentStateOnLaunch = "NO"
|
|
40
|
+
debugDocumentVersioning = "YES"
|
|
41
|
+
debugServiceExtension = "internal"
|
|
42
|
+
allowLocationSimulation = "YES">
|
|
43
|
+
</LaunchAction>
|
|
44
|
+
<ProfileAction
|
|
45
|
+
buildConfiguration = "Release"
|
|
46
|
+
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
47
|
+
savedToolIdentifier = ""
|
|
48
|
+
useCustomWorkingDirectory = "NO"
|
|
49
|
+
debugDocumentVersioning = "YES">
|
|
50
|
+
</ProfileAction>
|
|
51
|
+
<AnalyzeAction
|
|
52
|
+
buildConfiguration = "Debug">
|
|
53
|
+
</AnalyzeAction>
|
|
54
|
+
<ArchiveAction
|
|
55
|
+
buildConfiguration = "Release"
|
|
56
|
+
revealArchiveInOrganizer = "YES">
|
|
57
|
+
</ArchiveAction>
|
|
58
|
+
</Scheme>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Scheme
|
|
3
|
+
LastUpgradeVersion = "1600"
|
|
4
|
+
version = "1.3">
|
|
5
|
+
<BuildAction
|
|
6
|
+
parallelizeBuildables = "YES"
|
|
7
|
+
buildImplicitDependencies = "YES">
|
|
8
|
+
<BuildActionEntries>
|
|
9
|
+
<BuildActionEntry
|
|
10
|
+
buildForTesting = "YES"
|
|
11
|
+
buildForRunning = "YES"
|
|
12
|
+
buildForProfiling = "YES"
|
|
13
|
+
buildForArchiving = "YES"
|
|
14
|
+
buildForAnalyzing = "YES">
|
|
15
|
+
<BuildableReference
|
|
16
|
+
BuildableIdentifier = "primary"
|
|
17
|
+
BlueprintIdentifier = "0AEE99A309977BD12A049FF48AF9BA4B"
|
|
18
|
+
BuildableName = "Pods_Example.framework"
|
|
19
|
+
BlueprintName = "Pods-Example"
|
|
20
|
+
ReferencedContainer = "container:Pods.xcodeproj">
|
|
21
|
+
</BuildableReference>
|
|
22
|
+
</BuildActionEntry>
|
|
23
|
+
</BuildActionEntries>
|
|
24
|
+
</BuildAction>
|
|
25
|
+
<TestAction
|
|
26
|
+
buildConfiguration = "Debug"
|
|
27
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
28
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
29
|
+
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
30
|
+
<Testables>
|
|
31
|
+
</Testables>
|
|
32
|
+
</TestAction>
|
|
33
|
+
<LaunchAction
|
|
34
|
+
buildConfiguration = "Debug"
|
|
35
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
36
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
37
|
+
launchStyle = "0"
|
|
38
|
+
useCustomWorkingDirectory = "NO"
|
|
39
|
+
ignoresPersistentStateOnLaunch = "NO"
|
|
40
|
+
debugDocumentVersioning = "YES"
|
|
41
|
+
debugServiceExtension = "internal"
|
|
42
|
+
allowLocationSimulation = "YES">
|
|
43
|
+
</LaunchAction>
|
|
44
|
+
<ProfileAction
|
|
45
|
+
buildConfiguration = "Release"
|
|
46
|
+
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
47
|
+
savedToolIdentifier = ""
|
|
48
|
+
useCustomWorkingDirectory = "NO"
|
|
49
|
+
debugDocumentVersioning = "YES">
|
|
50
|
+
</ProfileAction>
|
|
51
|
+
<AnalyzeAction
|
|
52
|
+
buildConfiguration = "Debug">
|
|
53
|
+
</AnalyzeAction>
|
|
54
|
+
<ArchiveAction
|
|
55
|
+
buildConfiguration = "Release"
|
|
56
|
+
revealArchiveInOrganizer = "YES">
|
|
57
|
+
</ArchiveAction>
|
|
58
|
+
</Scheme>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Scheme
|
|
3
|
+
LastUpgradeVersion = "1600"
|
|
4
|
+
version = "1.3">
|
|
5
|
+
<BuildAction
|
|
6
|
+
parallelizeBuildables = "YES"
|
|
7
|
+
buildImplicitDependencies = "YES">
|
|
8
|
+
<BuildActionEntries>
|
|
9
|
+
<BuildActionEntry
|
|
10
|
+
buildForTesting = "YES"
|
|
11
|
+
buildForRunning = "YES"
|
|
12
|
+
buildForProfiling = "YES"
|
|
13
|
+
buildForArchiving = "YES"
|
|
14
|
+
buildForAnalyzing = "YES">
|
|
15
|
+
<BuildableReference
|
|
16
|
+
BuildableIdentifier = "primary"
|
|
17
|
+
BlueprintIdentifier = "3847153A6E5EEFB86565BA840768F429"
|
|
18
|
+
BuildableName = "SDWebImage.framework"
|
|
19
|
+
BlueprintName = "SDWebImage"
|
|
20
|
+
ReferencedContainer = "container:Pods.xcodeproj">
|
|
21
|
+
</BuildableReference>
|
|
22
|
+
</BuildActionEntry>
|
|
23
|
+
</BuildActionEntries>
|
|
24
|
+
</BuildAction>
|
|
25
|
+
<TestAction
|
|
26
|
+
buildConfiguration = "Debug"
|
|
27
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
28
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
29
|
+
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
30
|
+
<Testables>
|
|
31
|
+
</Testables>
|
|
32
|
+
</TestAction>
|
|
33
|
+
<LaunchAction
|
|
34
|
+
buildConfiguration = "Debug"
|
|
35
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
36
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
37
|
+
launchStyle = "0"
|
|
38
|
+
useCustomWorkingDirectory = "NO"
|
|
39
|
+
ignoresPersistentStateOnLaunch = "NO"
|
|
40
|
+
debugDocumentVersioning = "YES"
|
|
41
|
+
debugServiceExtension = "internal"
|
|
42
|
+
allowLocationSimulation = "YES">
|
|
43
|
+
</LaunchAction>
|
|
44
|
+
<ProfileAction
|
|
45
|
+
buildConfiguration = "Release"
|
|
46
|
+
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
47
|
+
savedToolIdentifier = ""
|
|
48
|
+
useCustomWorkingDirectory = "NO"
|
|
49
|
+
debugDocumentVersioning = "YES">
|
|
50
|
+
</ProfileAction>
|
|
51
|
+
<AnalyzeAction
|
|
52
|
+
buildConfiguration = "Debug">
|
|
53
|
+
</AnalyzeAction>
|
|
54
|
+
<ArchiveAction
|
|
55
|
+
buildConfiguration = "Release"
|
|
56
|
+
revealArchiveInOrganizer = "YES">
|
|
57
|
+
</ArchiveAction>
|
|
58
|
+
</Scheme>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Scheme
|
|
3
|
+
LastUpgradeVersion = "1600"
|
|
4
|
+
version = "1.3">
|
|
5
|
+
<BuildAction
|
|
6
|
+
parallelizeBuildables = "YES"
|
|
7
|
+
buildImplicitDependencies = "YES">
|
|
8
|
+
<BuildActionEntries>
|
|
9
|
+
<BuildActionEntry
|
|
10
|
+
buildForTesting = "YES"
|
|
11
|
+
buildForRunning = "YES"
|
|
12
|
+
buildForProfiling = "YES"
|
|
13
|
+
buildForArchiving = "YES"
|
|
14
|
+
buildForAnalyzing = "YES">
|
|
15
|
+
<BuildableReference
|
|
16
|
+
BuildableIdentifier = "primary"
|
|
17
|
+
BlueprintIdentifier = "92EBFA3E7005B4C18A9C0B44324EB80F"
|
|
18
|
+
BuildableName = "SDWebImageSwiftUI.framework"
|
|
19
|
+
BlueprintName = "SDWebImageSwiftUI"
|
|
20
|
+
ReferencedContainer = "container:Pods.xcodeproj">
|
|
21
|
+
</BuildableReference>
|
|
22
|
+
</BuildActionEntry>
|
|
23
|
+
</BuildActionEntries>
|
|
24
|
+
</BuildAction>
|
|
25
|
+
<TestAction
|
|
26
|
+
buildConfiguration = "Debug"
|
|
27
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
28
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
29
|
+
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
30
|
+
<Testables>
|
|
31
|
+
</Testables>
|
|
32
|
+
</TestAction>
|
|
33
|
+
<LaunchAction
|
|
34
|
+
buildConfiguration = "Debug"
|
|
35
|
+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
36
|
+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
37
|
+
launchStyle = "0"
|
|
38
|
+
useCustomWorkingDirectory = "NO"
|
|
39
|
+
ignoresPersistentStateOnLaunch = "NO"
|
|
40
|
+
debugDocumentVersioning = "YES"
|
|
41
|
+
debugServiceExtension = "internal"
|
|
42
|
+
allowLocationSimulation = "YES">
|
|
43
|
+
</LaunchAction>
|
|
44
|
+
<ProfileAction
|
|
45
|
+
buildConfiguration = "Release"
|
|
46
|
+
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
47
|
+
savedToolIdentifier = ""
|
|
48
|
+
useCustomWorkingDirectory = "NO"
|
|
49
|
+
debugDocumentVersioning = "YES">
|
|
50
|
+
</ProfileAction>
|
|
51
|
+
<AnalyzeAction
|
|
52
|
+
buildConfiguration = "Debug">
|
|
53
|
+
</AnalyzeAction>
|
|
54
|
+
<ArchiveAction
|
|
55
|
+
buildConfiguration = "Release"
|
|
56
|
+
revealArchiveInOrganizer = "YES">
|
|
57
|
+
</ArchiveAction>
|
|
58
|
+
</Scheme>
|