From e3d7ddb04a202f3dc0117f927a2b26a44bb34d3d Mon Sep 17 00:00:00 2001 From: tapframe <85391825+tapframe@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:40:12 +0530 Subject: [PATCH] changes --- .../commonMain/kotlin/com/nuvio/app/App.kt | 120 +++------ iosApp/iosApp.xcodeproj/project.pbxproj | 240 +++++++++--------- scripts/run-mobile.sh | 84 ++++-- 3 files changed, 217 insertions(+), 227 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt index e9f00d01..259cb3de 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt @@ -1,34 +1,29 @@ package com.nuvio.app +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawing import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Extension import androidx.compose.material.icons.rounded.Home import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import coil3.ImageLoader @@ -95,39 +90,36 @@ fun App() { } NuvioTheme { val navController = rememberNavController() - val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route var selectedTab by rememberSaveable { mutableStateOf(AppScreenTab.Home) } - // iOS-only: StreamsScreen is presented as a native modal sheet instead of - // a NavHost destination. On Android this stays null and navController is used. - var pendingStream by remember { mutableStateOf(null) } - val onPlay: (String, String, String, String?, String?, String?, Int?, Int?, String?, String?) -> Unit = { type, videoId, title, logo, poster, background, seasonNumber, episodeNumber, episodeTitle, episodeThumbnail -> - val route = StreamRoute( - type = type, - videoId = videoId, - title = title, - logo = logo, - poster = poster, - background = background, - seasonNumber = seasonNumber, - episodeNumber = episodeNumber, - episodeTitle = episodeTitle, - episodeThumbnail = episodeThumbnail, + navController.navigate( + StreamRoute( + type = type, + videoId = videoId, + title = title, + logo = logo, + poster = poster, + background = background, + seasonNumber = seasonNumber, + episodeNumber = episodeNumber, + episodeTitle = episodeTitle, + episodeThumbnail = episodeThumbnail, + ) ) - if (isIos) { - pendingStream = route - } else { - navController.navigate(route) - } } - Scaffold( - containerColor = MaterialTheme.colorScheme.background, - contentWindowInsets = WindowInsets(0), - bottomBar = { - if (currentRoute == TabsRoute::class.qualifiedName) { + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background), + ) { + Scaffold( + modifier = Modifier.fillMaxSize(), + containerColor = MaterialTheme.colorScheme.background, + contentWindowInsets = WindowInsets(0), + bottomBar = { NavigationBar( containerColor = MaterialTheme.colorScheme.surface, windowInsets = WindowInsets(0), @@ -145,21 +137,24 @@ fun App() { label = { Text("Addons") }, ) } - } - }, - ) { innerPadding -> + }, + ) { innerPadding -> + AppScreen( + tab = selectedTab, + modifier = Modifier.padding(innerPadding), + onPosterClick = { meta -> + navController.navigate(DetailRoute(type = meta.type, id = meta.id)) + }, + ) + } + NavHost( navController = navController, startDestination = TabsRoute, + modifier = Modifier.fillMaxSize(), ) { composable { - AppScreen( - tab = selectedTab, - modifier = Modifier.padding(innerPadding), - onPosterClick = { meta -> - navController.navigate(DetailRoute(type = meta.type, id = meta.id)) - }, - ) + Unit } composable { backStackEntry -> val route = backStackEntry.toRoute() @@ -171,10 +166,9 @@ fun App() { navController.popBackStack() }, onPlay = onPlay, - modifier = Modifier.padding(innerPadding), + modifier = Modifier.fillMaxSize(), ) } - // Android only: iOS uses the modal sheet below instead composable { backStackEntry -> val route = backStackEntry.toRoute() StreamsScreen( @@ -192,42 +186,10 @@ fun App() { StreamsRepository.clear() navController.popBackStack() }, - modifier = Modifier.padding(innerPadding), + modifier = Modifier.fillMaxSize(), ) } } } - - // iOS native modal sheet presentation for StreamsScreen - pendingStream?.let { route -> - ModalBottomSheet( - onDismissRequest = { - StreamsRepository.clear() - pendingStream = null - }, - sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), - containerColor = Color.Transparent, - contentColor = MaterialTheme.colorScheme.onBackground, - dragHandle = null, - contentWindowInsets = { WindowInsets.safeDrawing.only(WindowInsetsSides.Top) }, - ) { - StreamsScreen( - type = route.type, - videoId = route.videoId, - title = route.title, - logo = route.logo, - poster = route.poster, - background = route.background, - seasonNumber = route.seasonNumber, - episodeNumber = route.episodeNumber, - episodeTitle = route.episodeTitle, - episodeThumbnail = route.episodeThumbnail, - onBack = { - StreamsRepository.clear() - pendingStream = null - }, - ) - } - } } } diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 2bf09a40..1776f806 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -21,6 +21,11 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + 69649F6DF5D3AF53A24CA9C3 /* Configuration */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Configuration; + sourceTree = ""; + }; A2F97F59D21EB4D4B662C8D1 /* iosApp */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( @@ -29,11 +34,6 @@ path = iosApp; sourceTree = ""; }; - 69649F6DF5D3AF53A24CA9C3 /* Configuration */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = Configuration; - sourceTree = ""; - }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -167,6 +167,120 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 08C6158BC74ED5D18954BFD9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 69649F6DF5D3AF53A24CA9C3 /* Configuration */; + baseConfigurationReferenceRelativePath = Config.xcconfig; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3BEA88C53B91734AEB44313E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = 8QBDZ766S3; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = iosApp/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 4322E267F98B668D798FAEEE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = 8QBDZ766S3; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = iosApp/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; 7E47803C8E15431AEC9C9A71 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReferenceAnchor = 69649F6DF5D3AF53A24CA9C3 /* Configuration */; @@ -232,120 +346,6 @@ }; name = Debug; }; - 08C6158BC74ED5D18954BFD9 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 69649F6DF5D3AF53A24CA9C3 /* Configuration */; - baseConfigurationReferenceRelativePath = Config.xcconfig; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 4322E267F98B668D798FAEEE /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = arm64; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; - DEVELOPMENT_TEAM = "${TEAM_ID}"; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = iosApp/Info.plist; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 3BEA88C53B91734AEB44313E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = arm64; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; - DEVELOPMENT_TEAM = "${TEAM_ID}"; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = iosApp/Info.plist; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -370,4 +370,4 @@ /* End XCConfigurationList section */ }; rootObject = E819B502921EC7C68AC4965A /* Project object */; -} \ No newline at end of file +} diff --git a/scripts/run-mobile.sh b/scripts/run-mobile.sh index b30ea460..5b8bde5b 100755 --- a/scripts/run-mobile.sh +++ b/scripts/run-mobile.sh @@ -10,8 +10,9 @@ ANDROID_ACTIVITY=".MainActivity" IOS_PROJECT="$ROOT_DIR/iosApp/iosApp.xcodeproj" IOS_SCHEME="iosApp" IOS_DERIVED_DATA="$ROOT_DIR/build/ios-derived" -IOS_APP_PATH="$IOS_DERIVED_DATA/Build/Products/Debug-iphonesimulator/Nuvio.app" +IOS_APP_NAME="Nuvio.app" IOS_BUNDLE_ID="com.nuvio.app.Nuvio" +IOS_PREFERRED_DEVICE_MODEL="iPhone 14 Pro" usage() { cat <<'EOF' @@ -20,7 +21,7 @@ Usage: ./scripts/run-mobile.sh ios Builds the debug app for the selected platform, installs it on the first running -Android emulator or booted iOS simulator, and launches the app. +Android emulator or the configured iOS device target, and launches the app. EOF } @@ -39,6 +40,30 @@ first_booted_ios_simulator() { xcrun simctl list devices booted | awk -F '[()]' '/Booted/ { print $2; exit }' } +preferred_ios_device() { + xcrun xcdevice list --timeout 5 2>/dev/null | python3 -c ' +import json +import sys +import os + +try: + devices = json.load(sys.stdin) +except Exception: + sys.exit(0) + +physical = [ + device for device in devices + if device.get("platform") == "com.apple.platform.iphoneos" + and not device.get("simulator", False) + and device.get("available") is True + and device.get("modelName") == os.environ["IOS_PREFERRED_DEVICE_MODEL"] +] + +if physical: + print(physical[0].get("identifier", "")) +' +} + run_android() { require_command adb @@ -73,35 +98,38 @@ run_ios() { require_command xcodebuild require_command xcrun - local simulator_udid - simulator_udid="$(first_booted_ios_simulator)" + local physical_device_id + physical_device_id="$(IOS_PREFERRED_DEVICE_MODEL="$IOS_PREFERRED_DEVICE_MODEL" preferred_ios_device)" - if [[ -z "$simulator_udid" ]]; then - echo "No booted iOS simulator found." >&2 - echo "Start a simulator first, then rerun: ./scripts/run-mobile.sh ios" >&2 - exit 1 + if [[ -n "$physical_device_id" ]]; then + local device_app_path + device_app_path="$IOS_DERIVED_DATA/Build/Products/Debug-iphoneos/$IOS_APP_NAME" + + echo "Building iOS debug app for physical device $physical_device_id..." + xcodebuild \ + -project "$IOS_PROJECT" \ + -scheme "$IOS_SCHEME" \ + -configuration Debug \ + -destination "id=$physical_device_id" \ + -derivedDataPath "$IOS_DERIVED_DATA" \ + build + + if [[ ! -d "$device_app_path" ]]; then + echo "Expected iOS app not found at: $device_app_path" >&2 + exit 1 + fi + + echo "Installing on physical device $physical_device_id..." + xcrun devicectl device install app --device "$physical_device_id" "$device_app_path" + + echo "Launching app..." + xcrun devicectl device process launch --device "$physical_device_id" "$IOS_BUNDLE_ID" + return fi - echo "Building iOS debug app for simulator $simulator_udid..." - xcodebuild \ - -project "$IOS_PROJECT" \ - -scheme "$IOS_SCHEME" \ - -configuration Debug \ - -destination "id=$simulator_udid" \ - -derivedDataPath "$IOS_DERIVED_DATA" \ - CODE_SIGNING_ALLOWED=NO \ - build - - if [[ ! -d "$IOS_APP_PATH" ]]; then - echo "Expected iOS app not found at: $IOS_APP_PATH" >&2 - exit 1 - fi - - echo "Installing on simulator $simulator_udid..." - xcrun simctl install "$simulator_udid" "$IOS_APP_PATH" - - echo "Launching app..." - xcrun simctl launch "$simulator_udid" "$IOS_BUNDLE_ID" + echo "Preferred iOS device not available: $IOS_PREFERRED_DEVICE_MODEL" >&2 + echo "Connect and unlock that device, then rerun: ./scripts/run-mobile.sh ios" >&2 + exit 1 } main() {