
- TOP
- ã¿ã°äžèЧ
- Android
Android
ã€ãã³ã
ãã¬ãžã³
æè¡ããã°
ã¯ããã« ããã«ã¡ã¯ããã©ã³ããœãªã¥ãŒã·ã§ã³éçºæ¬éšFAANSéšã®ç°äžã§ããã·ã§ããã¹ã¿ããã®è²©å£²ãµããŒãããŒã«ãFAANSãã®Androidã¢ããªãæ
åœããŠããŸããæ¬èšäºã§ã¯ãFAANSã¢ããªã§FragmentããŒã¹ã®ããã²ãŒã·ã§ã³ããNavigation Composeãžã®ç§»è¡ããæ©èœéçºãšäžŠè¡ããŠã¢ãžã¥ãŒã«åäœã§é²ããåãçµã¿ã玹ä»ããŸãã çŽå幎ã§ã8ã€ã®æ©èœã¢ãžã¥ãŒã«ã®ãã¡5ã€ã®ç§»è¡ãå®äºããŸããããŽãŒã«ã¯ã ç»é¢é·ç§»ã®ç®¡çãActivityã«1ã€çœ®ããNavHostïŒNavigation Composeã§ç»é¢é·ç§»ã管çããComposableïŒãžéçŽãã ããšã§ãããã ãæ©èœéçºãšãªãªãŒã¹ãæ¢ããããã«ã¯ãããªããããFragmentãæ®ãããŸãŸ ã¢ãžã¥ãŒã«åäœã§ç§»è¡ãç©ã¿äžãã 圢ãåã£ãŠããŸãã以äžããã®é²ãæ¹ãšç§»è¡ã®äžã§èŠããŠãã課é¡ãä»åŸã®èŠéããæžããŸãã ç®æ¬¡ ã¯ããã« ç®æ¬¡ èæ¯ã»èª²é¡ ãã«ãã¢ãžã¥ãŒã«æ§æãšç»é¢é·ç§» UIã®Composeåãšäºé管ç ç§»è¡å¯Ÿè±¡ã®èŠæš¡ ç§»è¡æ¹é ãŽãŒã«ã𿮵é å
±åãããããã®éžæè¢ Navigation 3ã§ã¯ãªãNavigation Composeãéžãã çç± 1. ã¢ãžã¥ãŒã«åäœã®ç§»è¡ 1-1. æ¡çšããæ§é æªç§»è¡ã®Fragmentãžã®æ©æž¡ã 1-2. é²ãæ¹ã®æŽå 1-3. ç§»è¡åã®é·ç§»ã®èšé² 1-4. ç»é¢ã®ç§»è¡ ãã€ã¢ãã°ã®ç§»è¡ é·ç§»ãã€ãã³ãã§éç¥ããŠããViewModelã®ç§»è¡åå¯Ÿå¿ Fragmentã®ã©ã€ããµã€ã¯ã«ãšã®å¯Ÿå¿ 1-5. PRã®åãæ¹ 2. 芪åé¢ä¿ã«ããã¢ãžã¥ãŒã«ã®çµ±å 2-1. NavGraphBuilderæ¡åŒµãšããŠã®ã°ã©ãåé¢ 2-2. äžæçãªã¢ãžã¥ãŒã«éäŸåã®æ±ã 3. ActivityçŽäžã®åäžNavHostãžã®çµ±å 3-1. ã«ãŒãåŽã®çŸç¶ 3-2. çµ±åã®èŠéã ç§»è¡ã®äžã§èŠããŠããèª²é¡ èª²é¡1ïŒæ»ããã¿ã³ã®é£æã§ç»é¢ããã¯ã€ãã¢ãŠããã 課é¡2ïŒåçããã«ãŒããæ»ã£ãçŽåŸã®é·ç§»ãçºç«ããªã 課é¡3ïŒæªç§»è¡ã®ç»é¢ãæãã§ãããã¯ã¹ã¿ãã¯ã¯å€±ãããªãã£ã 課é¡4ïŒããã ã·ãŒããéãçŽããŠãåã®å
容ã衚瀺ããã 課é¡5ïŒç»é¢é·ç§»ãã¹ããæžããã«ã©ãæ
ä¿ããã ç§»è¡ã®é²æãšçŸæç¹ã®å¹æ æåŸã« èæ¯ã»èª²é¡ ãã«ãã¢ãžã¥ãŒã«æ§æãšç»é¢é·ç§» FAANSã¢ããªã¯ãã¢ãžã¥ãŒã«ããšã«ããã²ãŒã·ã§ã³ã°ã©ããæã€ãã«ãã¢ãžã¥ãŒã«æ§æã§ããåã¢ãžã¥ãŒã«ã®ã°ã©ããã«ãŒãã®ã°ã©ããã <include> ã§åã蟌ã¿ãé·ç§»å
ã¯ãã¹ãŠFragmentãšããŠå®çŸ©ããŠããŸããã¢ãžã¥ãŒã«ã¯æ©èœåäœã§åããããšãæå³ããŠããŸãããæŽå²çãªçµç·¯ã§ã¢ãžã¥ãŒã«ã®å¢çãšæ©èœã®å¢çãäžèŽããŠããªãç®æããããããèªäœãæè¡çè² åµã®1ã€ã«ãªã£ãŠããŸããããã§ããããã²ãŒã·ã§ã³ã°ã©ããšç»é¢é·ç§»ã®ãŸãšãŸããšããŠã¯ã¢ãžã¥ãŒã«ãåäœãšããŠæ©èœããŠãããããä»åã®ç§»è¡ã¯ ãã®ã¢ãžã¥ãŒã«ãåäœãšã㊠é²ããæ¹éãåããŸããã ããã²ãŒã·ã§ã³ã°ã©ãïŒXMLïŒã¯æ¬¡ã®ããã«å®çŸ©ããŠããŸããã«ãŒãã®ã°ã©ããåã¢ãžã¥ãŒã«ã®ã°ã©ãã <include> ã§åã蟌ã¿ãã¢ãžã¥ãŒã«ã®ã°ã©ãã«ã¯ç»é¢ã® <fragment> ãšé·ç§»ã® <action> ã䞊ã³ãŸãã <!-- ã«ãŒãã®ã°ã©ã: åã¢ãžã¥ãŒã«ã®ã°ã©ããåã蟌ã --> <navigation android : id = "@+id/nav_root" app : startDestination = "@id/SplashFragment" > <fragment android : id = "@+id/SplashFragment" android : name = "...SplashFragment" /> <include app : graph = "@navigation/module_a" /> <include app : graph = "@navigation/module_b" /> </navigation> <!-- ã¢ãžã¥ãŒã«Aã®ã°ã©ã: ç»é¢ããšã« fragment ãš action ãå®çŸ©ãã --> <navigation android : id = "@+id/nav_module_a" app : startDestination = "@id/SettingsFragment" > <fragment android : id = "@+id/SettingsFragment" android : name = "...SettingsFragment" > <action android : id = "@+id/action_Settings_to_Account" app : destination = "@id/AccountFragment" /> </fragment> <fragment android : id = "@+id/AccountFragment" android : name = "...AccountFragment" > <action android : id = "@+id/action_Account_to_ChangeEmail" app : destination = "@id/ChangeEmailFragment" /> </fragment> <fragment android : id = "@+id/ChangeEmailFragment" android : name = "...ChangeEmailFragment" /> </navigation> UIã®Composeåãšäºé管ç XMLã®ã¬ã€ã¢ãŠããJetpack Composeã«æžãæããäœæ¥ãç¶ããæ©èœã¢ãžã¥ãŒã«ã®ç»é¢ã¯ãã¹ãŠComposeåãå®äºããŸãããæ®ã£ãŠããã®ãç»é¢é·ç§»ã®å±€ã§ããç»é¢é·ç§»ãFragmentããŒã¹ã®ãŸãŸãªã®ã§ãComposeåããç»é¢ãé·ç§»å
ãšããŠç»é²ããå¿
èŠããããŸããããã®ããã«ã ComposeView ã setContent ããã ãã®Fragmentãã1ç»é¢ããšã«çšæããŠããŸãããããããŠãããã²ãŒã·ã§ã³ã°ã©ãã« <fragment> ãš <action> ãæžãç¶ããå¿
èŠããããŸããã // ç§»è¡å: äžèº«ã¯Composeãªã®ã«ãé·ç§»ã®ããã ãã«æ®ã£ãŠããFragment @AndroidEntryPoint class ExampleFragment : Fragment() { private val viewModel: ExampleViewModel by viewModels() private val args: ExampleFragmentArgs by navArgs() // åŒæ°ã¯XMLã® <argument> ããçæãããã¯ã©ã¹ã§åãåã override fun onCreateView(...): View = ComposeView(requireContext()).apply { setContent { FaansTheme { ExampleRoute( id = args.id, viewModel = viewModel, onNavigateBack = { findNavController().popBackStack() }, ) } } } } ç»é¢ã®å®è£
ã¯Composeã«ç§»ã£ãŠããã®ã«ãé·ç§»ãŸããã¯3ãæã«åããããŸãŸã§ããé·ç§»å
ã®å®çŸ©ã¯ããã²ãŒã·ã§ã³ã°ã©ãã«ãé·ç§»ã®å®è¡ã¯Fragmentã® findNavController() ã«æžããŸããç»é¢éã§æž¡ãåŒæ°ã¯ãXMLã® <argument> ããçæãããSafe Argsã®ã¯ã©ã¹ã§åãåããŸããComposeã®ç»é¢ã1ã€è¶³ããã³ã«FragmentãšXMLã®äž¡æ¹ãè§Šãã äºé管ç ããç©ã¿äžãã£ãŠããŸããã ç§»è¡å¯Ÿè±¡ã®èŠæš¡ é
ç® ç§»è¡å ããã²ãŒã·ã§ã³ã°ã©ãïŒXMLïŒ 12ãã¡ã€ã«ïŒæ©èœã¢ãžã¥ãŒã«å9ãã¢ããªå
±é3ïŒ é·ç§»å
ïŒ <fragment> ïŒ 90 é·ç§»å
ïŒ <dialog> ïŒ 50 ç»é¢ã®Composeåãããåããããããç»é¢é·ç§»ã®å±€ã«æãä»ããããç¶æ
ã«ãªããŸãããå
¬åŒã®ç§»è¡ã¬ã€ãããNavigation Composeãžåãæ¿ããåæãšããŠã é·ç§»å
ããã¹ãŠComposableã§ããããš ããæããŠããŸãã You can migrate to Navigation Compose once you're able to replace all of your Fragments with corresponding screen composables . Screen composables can contain a mix of Compose and View content, but all navigation destinations must be composables to enable Navigation Compose migration. Until then, you should continue using Fragment-based Navigation component in your interop View and Compose codebase. â Migrate Jetpack Navigation to Navigation Compose â Android Developers èŠçŽïŒãã¹ãŠã®é·ç§»å
ãComposableã«ãªããŸã§ã¯ãFragmentããŒã¹ã®ããã²ãŒã·ã§ã³ã䜿ãç¶ããå¿
èŠãããã äžæ¹ã§ãã¢ããªå
šäœãäžåºŠã«åãæ¿ããéžæè¢ã¯ãããŸããã§ããã æ©èœéçºãšäžŠè¡ããŠå°ããã€ããããåŸæ»ããªãé²ããæ¹æ³ ãå¿
èŠã§ããã ç§»è¡æ¹é ãŽãŒã«ã𿮵é Navigation Composeã§ã¯ã NavHost ãšãã1ã€ã®Composableãç»é¢ã®åãæ¿ããšããã¯ã¹ã¿ãã¯ã管çããŸããå
¬åŒã®åºæ¬æ§æã§ã¯ããããActivityã«1ã€çœ®ããŸãã ãŽãŒã«ã¯ãActivityçŽäžã«1ã€ã®Compose NavHostã眮ããXMLã®ããã²ãŒã·ã§ã³ã°ã©ããš NavHostFragment ããªããããšã§ãã ãããŸã§ãäžæ°ã«ã¯é²ããããªãã®ã§ã3段éã«åããŠããŸãã ã¢ãžã¥ãŒã«åäœã®ç§»è¡ ïŒèµ·ç¹ã®Fragmentãæ®ãããã®å
éšã«NavHostã眮ããŠãã¢ãžã¥ãŒã«å
ã®ç»é¢ãComposeãžç§»ãïŒã³ãŒãã¯1-1ïŒ èŠªåé¢ä¿ã«ããã¢ãžã¥ãŒã«ã®çµ±å ïŒç¹å®ã®1ã€ã®ã¢ãžã¥ãŒã«ããããé·ç§»ãããªãã¢ãžã¥ãŒã«ïŒãã®èšäºã§ã¯èŠªåé¢ä¿ãšåŒã³ãŸãïŒã®ç§»è¡ãå®äºãããããã®ã°ã©ãã芪åŽã®NavHostãžçµã¿èŸŒãïŒã³ãŒãã¯2-1ïŒ ActivityçŽäžã®åäžNavHostãžã®çµ±å ïŒãã¹ãŠã®ã¢ãžã¥ãŒã«ãæã£ã段éã§ãActivityã«1ã€ã ã眮ããNavHostãžäžæ¬ã§çµã¿èŸŒãïŒã³ãŒãã¯3-2ïŒ ä»¥äžããã®3段éãé ã«èª¬æããŸããã³ãŒãã¯èšå®ã¢ãžã¥ãŒã«ãéãã®äŸã«ããŠãåãã³ãŒãããã®3段éã§ã©ãå€ãããã远ãã圢ã«ããŠããŸãã3ã¯æ¬¡ã®ãã§ãŒãºãšããŠèšç»ããŠããæ®µéã§ãå®éã®ç§»è¡äœæ¥ã¯æ¬èšäºã®ã¹ã³ãŒãå€ã®ãããçŸç¶ãšèŠéãã ããæžããŸãã å
±åãããããã®éžæè¢ FragmentããŒã¹ã®ããã²ãŒã·ã§ã³ãšNavigation Composeãå
±åãããæ¹æ³ã¯ãå
¬åŒã®çžäºéçšAPIãå«ããŠããã€ããããŸãã æ¹æ³ æŠèŠ åããŠããå Žé¢ ããã²ãŒã·ã§ã³ã°ã©ãïŒXMLïŒã« composable ã眮ã Navigation 2.8.0 ã® navigation-fragment-compose ã ComposableNavHostFragment ã䜿ããšãã°ã©ãã« <composable> ãæžãã æ¢åã®ã°ã©ãæ§é ãä¿ã£ããŸãŸãç»é¢åäœã§Composeã«çœ®ãæããŠããããå Žå Composeã®NavHostã®äžã«Fragmentã眮ã Fragment 1.8.0 ã® fragment-compose ã AndroidFragment ã§Composeéå±€ã®äžã«Fragmentãé
眮ã§ããç¶æ
ã®ä¿åã»åŸ©å
ã«ã察å¿ããŠãã NavHostãå
ã«ComposeåŽãžç§»ããæ®ã£ãFragmentç»é¢ãåŸãã眮ãæãããå Žå èµ·ç¹ã®Fragmentãæ®ãããã®å
éšã«NavHostã眮ã å€åŽã¯FragmentããŒã¹ã®ããã²ãŒã·ã§ã³ã®ãŸãŸãã¢ãžã¥ãŒã«å
ã®ç»é¢ã ãã composable<T> ãžç§»ããŠãã ã¢ãžã¥ãŒã«åäœã§ç§»è¡ãå®çµããããå ŽåïŒä»åæ¡çšïŒ ç§ãã¡ã¯ã 1ã€ã®ã¢ãžã¥ãŒã«ãæåŸãŸã§ããåã£ãŠããæ¬¡ãžé²ã ãé²ãæ¹ã培åºããŠããããã3ã€ç®ãéžã³ãŸãããã¢ãžã¥ãŒã«ã®å
åŽã¯ãé·ç§»å
ããã¹ãŠComposableããšããå
¬åŒã®åæãæºãããå€åŽã¯æ¢åã®FragmentããŒã¹ã®ããã²ãŒã·ã§ã³ããã®ãŸãŸåããŸããç»é¢åäœã§å°ããã€æ··ãããå Žåã¯1ã€ç®ãNavHostãå
ã«ç§»ãããå Žåã¯2ã€ç®ãéžæè¢ã«ãªããŸãã Navigation 3ã§ã¯ãªãNavigation Composeãéžãã çç± å·çæç¹ã§ã¯ Navigation 3 ãå®å®çãšããŠå
¬éãããŠããŸãããããªãæåããNavigation 3ãžç§»è¡ããã°ããã®ã§ã¯ããšæããããããããŸãããããã Navigation 3ã¯Composeå°çšã§ãFragmentãé·ç§»å
ã«ã§ããŸãã ããã®ããFragmentãæ®ãæ®µéã§ã¯ãŸãNavigation Composeãžç§»è¡ããNavigation 3ãžã®ç§»è¡ã¯ãã®åŸã®æ®µéãšããŸãããåå®å
šã«ãŒããæ¡çšããŠããã®ã§ã å
¬åŒã®ç§»è¡ã¬ã€ã ãåæãšãã圢ã¯ãã®ãŸãŸæºãããŸãã 1. ã¢ãžã¥ãŒã«åäœã®ç§»è¡ 1-1. æ¡çšããæ§é 1ã€ã®ã¢ãžã¥ãŒã«ã«æ³šç®ãããšãç§»è¡åŸã®ã°ã©ãã¯Composeã§ããæžããŸããé·ç§»å
㯠@Serializable ãªã¯ã©ã¹ã§è¡šãã NavHost ã« composable<T> ãšããŠç»é²ããŸãã // é·ç§»å
ã®å®çŸ©: XMLã® <fragment> ã®ä»£ãã sealed interface SettingsDestination { @Serializable data object Settings : SettingsDestination @Serializable data object Account : SettingsDestination @Serializable data class ChangeEmail( val currentEmail: String ) : SettingsDestination } // ã¢ãžã¥ãŒã«ã®NavHost: XMLã® <action> ã®ä»£ããã«ã©ã ãã§é·ç§»ãã @Composable fun SettingsNavHost( navController: NavHostController, onClickBackButton: () -> Unit , ) { NavHost(navController, startDestination = SettingsDestination.Settings) { composable<SettingsDestination.Settings> { SettingsRoute( onClickBackButton = onClickBackButton, onClickAccount = { navController.navigate(SettingsDestination.Account) }, ) } composable<SettingsDestination.Account> { AccountRoute(onClickChangeEmail = { email -> navController.navigate(SettingsDestination.ChangeEmail(email)) }) } composable<SettingsDestination.ChangeEmail> { backStackEntry -> val args = backStackEntry.toRoute<SettingsDestination.ChangeEmail>() ChangeEmailRoute(currentEmail = args.currentEmail) } } } ãã®NavHostãã©ãã«çœ®ãããå
±åã®èŠç¹ã§ããã¢ãžã¥ãŒã«Aã®ã°ã©ãïŒXMLïŒã«å±ããŠããFragmentã®ãã¡ã èµ·ç¹ã®1ã€ã ããæ®ãããã®äžã«NavHostã眮ããŸã ãã¢ãžã¥ãŒã«å
ã®ç»é¢ã¯ãã¹ãŠNavHostã®äžã® composable<T> ã«ãªããŸãã å
¬åŒã¬ã€ãã®æé ã§ã¯NavHostãActivityã«çœ®ããŸãããå€åŽã®ã°ã©ãïŒXMLïŒãæ®ãå¿
èŠããã£ããããã¢ãžã¥ãŒã«ã®èµ·ç¹ãšãªãFragmentã«NavHostã眮ããŸããã 1ã¢ãžã¥ãŒã«=1NavHost ãšããåäœã§ãèæ¯ã§æãããé·ç§»å
ããã¹ãŠComposableããšããåæãã¢ãžã¥ãŒã«ã®å
åŽã§æºãããŸãã èµ·ç¹ã®Fragmentã®è²¬åã¯ãNavHostã眮ããããšãšãå€åŽãšã®æ¥ç¶ãã ãã«ãªããŸãã @AndroidEntryPoint class SettingsFragment : Fragment() { override fun onCreateView(...): View = ComposeView(requireContext()).apply { setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) setContent { FaansTheme { val navController = rememberNavController() SettingsNavHost( navController = navController, onClickBackButton = { findNavController().popBackStack() }, ) } } } } NavHost ã¯ãã®ãŸãŸäœ¿ãããç»é¢é·ç§»ã¢ãã¡ãŒã·ã§ã³ã None ã«çµ±äžããèãã©ãããŒãå
±éã¢ãžã¥ãŒã«ã«çœ®ããŠãåã¢ãžã¥ãŒã«ã¯ããã䜿ããŸããFragmentæä»£ã®é·ç§»ãšèŠãç®ã®å·®ãåºããªãããã§ãã ãªããäžã® SettingsNavHost ã®äŸã§ã¯ç°¡ç¥åã®ãã NavHost ãçŽæ¥åŒãã§ããŸãããå®éã«ã¯ãã®ã©ãããŒïŒ FaansNavHost ïŒã䜿ã£ãŠããŸãã @Composable fun FaansNavHost( navController: NavHostController, startDestination: Any , modifier: Modifier = Modifier, builder: NavGraphBuilder.() -> Unit , ) { NavHost( navController = navController, startDestination = startDestination, modifier = modifier, enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None }, popEnterTransition = { EnterTransition.None }, popExitTransition = { ExitTransition.None }, builder = builder, ) } æªç§»è¡ã®Fragmentãžã®æ©æž¡ã å
±åæã«ã¯ãã¢ãžã¥ãŒã«å
ã§ã¯ç§»è¡æžã¿ã ããé·ç§»å
ããŸã Fragmentããšããçµã¿åãããå¿
ãåºãŸãã å
åŽã®NavHostãããã£ããæããå€åŽã® findNavController() ã§é·ç§»ãããŸã ãæ¬¡ã®ç§»è¡å¯Ÿè±¡ãTODOã§æç€ºããŠãããšãåŸããæ¢ãæéãçããŸãã onNavigateToLegacyScreen = { args -> // TODO LegacyFragment ãScreenåããã compose ã® navController ã§é·ç§»ãã findNavController().navigate(R.id.LegacyFragment, args) } 1-2. é²ãæ¹ã®æŽå æåã®ã¢ãžã¥ãŒã«ã«ã¯ãæ¥åã®äžå¿ã§ã¯ãªããã®ã®äžå®ã®å©çšããããèšå®ç»é¢ã®ã¢ãžã¥ãŒã«ãéžã³ãŸãããå©çšãå°ãªããããšåé¡ã衚ã«åºããå€ããããšåé¡ãåºããšãã®åœ±é¿ã倧ããããã§ããç»é¢æ°ãå°ãªããé·ç§»ãäžçŽç·ã§ããããšãçç±ã®1ã€ã§ãã ãã®ã¢ãžã¥ãŒã«ã¯ç§ã1人ã§ç§»è¡ãã倿ã«è¿·ããšããã¯ããŒã ã«çžè«ããªããé²ããŸãããç§»è¡åŸã¯ã ç§»è¡æžã¿ãšæªç§»è¡ã®ã¢ãžã¥ãŒã«ãæ··åšããç¶æ
ã®ãŸãŸéåžžã®ãªãªãŒã¹ã«èŒã ãåé¡ãåºãªãããšã確ãããŸããã ãã®ããã§æé ãšæ³šæç¹ãClaude Codeã®ã¹ãã«ïŒæé æžãç»é²ããåŒã³åºããŠå®è¡ãããä»çµã¿ïŒã«èœãšã蟌ã¿ã2ã¢ãžã¥ãŒã«ç®ä»¥éã¯ããŒã ã§åæ
ããŠããŸããã¹ãã«ã¯ç§»è¡æé ã®å®è¡ã ãã§ãªãããã®ååŸã®æ®µåããèªååããŠããŠã䜿ããªããæçŽããç¶ããŠããŸãã åæ
ããŠé²ããããã«ã次ã®ãã®ãæŽããŸããã 鲿ãäžèЧã§ãã管çããŒãž ïŒã©ã®ã¢ãžã¥ãŒã«ãã©ããŸã§é²ãã§ãããã1ããŒãžã§èŠããåãäœæ¥ãã±ããïŒèª²é¡ç®¡çã¯JiraïŒã«ã¢ãžã¥ãŒã«åã®ã©ãã«ãä»ãããã®ããŒãžããã¢ãžã¥ãŒã«ããšã«çµã蟌ãã ã¢ãžã¥ãŒã«ããšã®éçŽãã©ã³ã ïŒåç»é¢ã®PRãéçŽãã©ã³ãã«ç©ã¿ãã¢ãžã¥ãŒã«å
šäœã®åäœç¢ºèªãçµãã£ãŠããéçºã®ã¡ã€ã³ãã©ã³ããžåã蟌ããæ©èœéçºåŽã®ãªãªãŒã¹ãšè¡çªãã«ãããªã 骚çµã¿ã®PRãå
ã«å
¥ãã ïŒèµ·ç¹ã®Fragmentã«NavHostãå°å
¥ããPRãæåã«å
¥ããŠãããšãåç»é¢ã®ç§»è¡ã¯ããããç¬ç«ããPRã«ãªããè€æ°äººã§äžŠè¡ããŠé²ãããã 鲿ã®ç®¡çããŒãžã¯ãã®ãããªåœ¢ã§ãã 1-3. ç§»è¡åã®é·ç§»ã®èšé² ç»é¢é·ç§»ã®ãã¹ãã¯æžããªã倿ãããŸãã ãNavigation Composeã«ã¯ãã¹ãçšã®APIãçšæãããŠããŸãããããã§ãæžããªããšæ±ºãããŸã§ã®çµç·¯ã¯èª²é¡5ã§æžããŸãããšã¯ãããç§»è¡åãšåãé·ç§»ãã§ããããšã®ç¢ºèªã¯å¿
èŠã§ããããã§éäžãã Maestro ã§ç»é¢é·ç§»ã®ã·ããªãªãäœããç§»è¡ã®ååŸã§åãã·ããªãªãæµããšãã軜ã確èªã詊ããŠããŸãã Maestroã¯ãUIæäœã®ã·ããªãªïŒMaestroã§ã¯ãããŒãšåŒã³ãŸãïŒãYAMLã§æžããšã宿©ã§åãæäœãåçããããŒã«ã§ãããã¹ãã³ãŒããæžãããæè»œã§ãç§»è¡ã®ãã³ã«ç¹°ãè¿ããç¹ãä»åã®çšéã«åããŠããŸããã ã·ããªãªã®YAMLã¯ã mobile-mcp çµç±ã§Claude Codeã«å®æ©ã®ç»é¢ãæäœãããªããçæããŠããŸãã AIã«ä»»ããã®ã¯äœæãŸã§ã§ãã§ãããã£ãã·ããªãªã®å®è¡ã¯Maestro CLIã ãã§è¡ããŸã ãå®è¡ã®ãã³ã«AIã®å€æãå
¥ããªãã®ã§ãåãæäœãåãçµæã§ç¹°ãè¿ããŸãã ã·ããªãªã§ç¢ºèªããŠããã®ã¯ãç»é¢ãéããŠè¡šç€ºã確èªããèµ·ç¹ã«æ»ããšãã åçŽãªç»é¢é·ç§»ã ã ã§ããåé€ãæçš¿ã®ããã«ããŒã¿ã倿Žããæäœããè€æ°ã®å
¥åã䌎ããããŒã¯å«ããŠããŸãããããã¯åŸæ¥ã©ããQAãšæå確èªã§æ
ä¿ããŠããŸãã ã·ããªãªã¯ç»é¢ããšã«ãèµ·ç¹ç»é¢ã§å§ãŸããèµ·ç¹ç»é¢ã«åž°çãããããã«æžããããã runFlow ã§æãã1æ¬ãçšæããŸããåäœã§ãæããŠãå®è¡ã§ããã¹ããããäºéã«æã¡ãŸããã å®è¡ãããšãå·Šã®ãã°ã1ã¹ããããã€é²ã¿ãå³ã®ç«¯æ«ã§åãæäœãåçãããŸãã Maestroã¯ç»é¢äžã®èŠçŽ ãã衚瀺ããã¹ããIDã§æå®ããŠæäœããŸããViewã®ç»é¢ãªã android:id ããã®ãŸãŸIDã«ãªããŸãããComposeã®èŠçŽ ã«ã¯IDã«åœãããã®ããããŸãããããã§æäœãããèŠçŽ ã« Modifier.testTag("...") ãä»ããŸããããã«ãã¢ããªå
šäœãå
ãããŒãã®ComposableïŒãã¹ãŠã®ç»é¢ãå¿
ãéãããã¡ã°ãå€åŽã®ComposableïŒã§ testTagsAsResourceId = true ãèšå®ããŸããããã§ testTag ã®æååãAndroidã® resource-id ãšããŠå€ããèŠããããã«ãªããMaestroã® id: ã§æå®ã§ããŸãã // ã¢ããªå
šäœãå
ãããŒãã®Composableã§äžåºŠã ãèšå®ãã(é
äžã®ãã¹ãŠã®ç»é¢ã«å¹ã) Modifier.semantics { testTagsAsResourceId = true } // æäœãããèŠçŽ ã« testTag ãä»ãã Button(modifier = Modifier.testTag( "settings_change_password_button" ), ...) # Maestro ã®ã·ããªãªãã㯠id ã§æå®ã§ãã - tapOn : id : "settings_change_password_button" Android Compose: Use Modifier.semantics { testTagsAsResourceId = true } to ensure your test tags are discoverable as IDs. â Core Selectors â Maestro Documentation èŠçŽïŒComposeã§ã¯ testTagsAsResourceId ãæå¹ã«ããããšã§ã testTag ãIDã»ã¬ã¯ã¿ãšããŠäœ¿ããã maestro record ã§ååŸããåç»ãPRã«æ·»ä»ããã¬ãã¥ã¢ãŒãé·ç§»ã®ç¶æã確èªã§ããããã«ããŠããŸããCIã«ã¯çµã¿èŸŒãŸããç§»è¡PRããšã«ããŒã«ã«ã§å®è¡ããŠããŸããæžããã·ããªãªã¯ç§»è¡åŸãæ®ãã®ã§ãç»é¢ã®è¿œå ãå
±éã³ã³ããŒãã³ãã®å·®ãæ¿ããã©ã€ãã©ãªæŽæ°ã®ãšãã«ãåãã·ããªãªã§å垰確èªãã§ããŸãã 1-4. ç»é¢ã®ç§»è¡ æ€èšŒçšã®ã·ããªãªãæã£ãããããããç§»è¡ã«åãããããŸããé²ããé çªã¯æ¬¡ã®ãšããã§ãã èµ·ç¹ã®Fragmentã«ç©ºã®NavHostãå°å
¥ããèµ·ç¹ç»é¢ã ãã composable<T> ã§é
ç·ãã骚çµã¿ã®PRãå
¥ãã åç»é¢ã1ã€ãã€ç§»è¡ããã Fragmentãåé€ããç»é¢ã®Composableã composable<T> ãšããŠNavHostã«ç»é²ãã äžèŠã«ãªã£ãå®çŸ©ãããã²ãŒã·ã§ã³ã°ã©ãããåé€ããããã®ç»é¢ã® <fragment> ãšããããžåãã <action> ãæ¶ãã <!-- ç§»è¡å: ããã²ãŒã·ã§ã³ã°ã©ãã«æžããŠããå®çŸ©ãç§»è¡åŸã¯ãã®2ã€ãåé€ãã --> <fragment android : id = "@+id/ChangeEmailFragment" android : name = "...ChangeEmailFragment" /> <action android : id = "@+id/action_Account_to_ChangeEmail" app : destination = "@id/ChangeEmailFragment" /> // ç§»è¡åŸ: NavHoståŽã« composable<T> ãç»é²ããé·ç§»ã¯ã©ã ãã§æž¡ã composable<SettingsDestination.ChangeEmail> { backStackEntry -> val args = backStackEntry.toRoute<SettingsDestination.ChangeEmail>() ChangeEmailRoute( currentEmail = args.currentEmail, onNavigateBack = dropUnlessResumed { navController.navigateUp() }, ) } FAANSã§ã¯ç»é¢ã®ComposableããViewModelãåãåã£ãŠç¶æ
ã賌èªãã XxxRoute ãšãç¶æ
ãšã³ãŒã«ããã¯ã ããåãåãçŽç²ãªUIã® XxxScreen ã«åããŠããŸãã Fragmentãæ
ã£ãŠããViewModelã®çæã»åŒæ°ã®åãåãã»é·ç§»ã³ãŒã«ããã¯ã®æäŸã¯ãNavHoståŽã® composable<T> ãããã¯ãš Route ãžç§»ããŸãã // ç»é¢åŽ: ViewModelãåãåã£ãŠç¶æ
ã賌èªããScreenã«æž¡ã @Composable fun ChangeEmailRoute( currentEmail: String , onNavigateBack: () -> Unit , viewModel: ChangeEmailViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsStateWithLifecycle() ChangeEmailScreen( state = state, onAction = viewModel :: dispatchAction, onNavigateBack = onNavigateBack, ) } ViewModel ïŒ by viewModels() ã®ä»£ããã« hiltViewModel() ã䜿ããé·ç§»å
ïŒ NavBackStackEntry ïŒã«ã¹ã³ãŒããããã®ã§ãç»é¢ãæããã°ç Žæ£ããã åŒæ° ïŒ Bundle ãSafe Argsã®ä»£ããã« backStackEntry.toRoute<T>() ã§åä»ãã®åŒæ°ãåãåã é·ç§» ïŒ findNavController() ã®ä»£ããã«ãNavHoståŽã§ navController ã䜿ãã©ã ããæž¡ã ãã€ã¢ãã°ã®ç§»è¡ ããã²ãŒã·ã§ã³ã°ã©ãã«ã¯é·ç§»å
ã® <dialog> ã50ãããŸãããç§»è¡åŸã¯çš®é¡ããšã«æ±ããå€ãããŸãã 確èªãã€ã¢ãã°ïŒ AlertDialog ïŒ ïŒå
±éã®Composeãã€ã¢ãã°ã«çœ®ãæããã芪ç»é¢ã®ç¶æ
ïŒ showDeleteConfirmDialog ã®ãããªãã©ã°ïŒã§è¡šç€ºãåãæ¿ããé·ç§»å
ãšããŠã¯ç»é²ããªã BottomSheetDialogFragment ïŒ ModalBottomSheet ã«çœ®ãæããã確èªãã€ã¢ãã°ãšåããã芪ç»é¢ã®ç¶æ
ã§è¡šç€ºãåãæ¿ãã å
šç»é¢ã®DialogFragment ïŒéåžžã®ç»é¢ãšåãã composable<T> ãšããŠç»é²ãã åŒæ°ãåãåããç¬ç«ããé·ç§»å
ãšããŠæ±ãå¿
èŠããããã® ïŒ dialog<T> ã§NavHostã«ç»é²ãã é·ç§»å
ãšããŠç»é²ããªããã€ã¢ãã°ã¯ãããã²ãŒã·ã§ã³ã°ã©ãã® <dialog> ãšãããžåãã <action> ãæ¶ããŸãã // 芪ç»é¢ã®ç¶æ
ã§ãã€ã¢ãã°ãåºãåãããé·ç§»å
ãšããŠã¯ç»é²ããªã if (state.showDeleteConfirmDialog) { FaansAlertDialog( message = stringResource(R.string.delete_account_confirm), onConfirm = { onAction(AccountAction.OnClickDeleteConfirm) }, onDismiss = { onAction(AccountAction.OnDismissDeleteConfirmDialog) }, ) } é·ç§»ãã€ãã³ãã§éç¥ããŠããViewModelã®ç§»è¡åå¯Ÿå¿ FAANSã®æ°ããç»é¢ã¯ãUIç¶æ
ã StateFlow ã«éçŽãã圢ã§ãããããå€ãç»é¢ã«ã¯ãç»é¢é·ç§»ããã€ã¢ãã°è¡šç€ºã LiveData<Event> ã®ãããª1åéãã®ã€ãã³ãã§éç¥ããŠããViewModelãæ®ã£ãŠããŸããã å
¬åŒã®ã¢ãŒããã¯ãã£ã¬ã€ã ã¯ãViewModelã§çºçããã€ãã³ãã UIç¶æ
ã®æŽæ°ãšããŠè¡šçŸãã ããšãæšå¥šããŠããŸããããããç»é¢ã¯ãé·ç§»ã®èŠåŠãç¶æ
ïŒ shouldNavigateBack ã®ãããªãã©ã°ïŒãšããŠæã€åœ¢ã«å€æããŠããç§»è¡ããŸããç»é¢åŽã§ã¯ç¶æ
ãããŒã«ãã LaunchedEffect ã§é·ç§»ãåŒã³ãåŒãã ããšãViewModelã«æ»ããŠç¶æ
ããªã»ããããŸãã LaunchedEffect(state.shouldNavigateBack) { if (state.shouldNavigateBack) { onNavigateBack() onAction(DetailAction.ConsumeNavigateBack) } } ãã®å¯Ÿå¿ã¯æåã®å€æŽãå«ãã®ã§ãç§»è¡PRãšã¯åããŠãç§»è¡å察å¿ããšããŠã¬ãã¥ãŒããŠããŸãã Fragmentã®ã©ã€ããµã€ã¯ã«ãšã®å¯Ÿå¿ Fragmentãåé€ãããšã onViewCreated ã onResume ã§è¡ã£ãŠããåæåããã°éä¿¡ã®çœ®ãå ŽæããªããªããŸããåŠçã®çš®é¡ããšã«ãFragmentã§ã®æžãæ¹ãšComposeã§ã®æžãæ¹ã䞊ã¹ããšæ¬¡ã®ããã«ãªããŸãã ç»é¢ã衚瀺ããããšãã®åæå // Fragment override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super .onViewCreated(view, savedInstanceState) viewModel.dispatchAction(ChangeEmailAction.Initialize) } // Compose: Compositionã«å
¥ã£ããšãã«1åå®è¡ããã LaunchedEffect( Unit ) { onAction(ChangeEmailAction.Initialize) } åé¢ã«æ»ããã³ã®ããŒã¿åååŸãšãç»é¢è¡šç€ºã®ãã°éä¿¡ // Fragment override fun onResume() { super .onResume() viewModel.dispatchAction(ChangeEmailAction.Refresh) analytics.logScreenView(SCREEN_NAME) } // Compose: ON_RESUME ããšã«å®è¡ããããåŸå§æ«ãèŠããã®ã¯ LifecycleResumeEffectã // ãã°ã®ããã«æããã ãã®ãã®ã¯ LifecycleEventEffect LifecycleResumeEffect( Unit ) { onAction(ChangeEmailAction.Refresh) onPauseOrDispose { } } LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { analytics.logScreenView(SCREEN_NAME) } LifecycleResumeEffect ãš LifecycleEventEffect 㯠å¥ã®API ã§ããã©ã¡ãã ON_RESUME ã§ãããã¯ãåããŸãããåè
㯠onPauseOrDispose ãšãã¢ã§ãåéã§å§ããŠäžæã§æ¢ãããåŠçãæ±ããåŸè
ã¯ã€ãã³ãã®ãã³ã«åŠçãå®è¡ããã ãã§ãã Fragmentã®ã©ã€ããµã€ã¯ã«ãšã®å¯Ÿå¿è¡šã¯å
¬åŒããã¥ã¡ã³ãã«ã¯èŒã£ãŠããªãããã Composeã®ã©ã€ããµã€ã¯ã«å¯Ÿå¿APIã®èª¬æ ãããšã«èªåãã¡ã§æŽçããç§»è¡åã«æ±ºããŠãããŸããã Fragmentã§æžããŠããããš Composeã§ã®çœ®ãæã è£è¶³ onViewCreated ã§ã®åæåã»è³Œèª LaunchedEffect(Unit) Compositionã«å
¥ããã³ã«å®è¡ãNavHostå
ã§æ»ã£ãŠãããšããåå®è¡ããã onResume ã§ã®ããŒã¿åååŸ LifecycleResumeEffect ON_RESUME ã«çµã³ä»ãã onPauseOrDispose ã§åŸå§æ« onResume ã§ã®ç»é¢è¡šç€ºãã°éä¿¡ LifecycleEventEffect(ON_RESUME) å
¬åŒããã¥ã¡ã³ãããã°ãåæãªã©ã®ã¯ã³ã·ã§ããã€ãã³ãåããšããŠããAPI äžåºŠã ãè¡ãåæå ViewModelã® init Composableã®ã©ã€ããµã€ã¯ã«ã«äŸåãããªã 1-5. PRã®åãæ¹ PRã¯åœ¹å²ã§åããŠããŸãã Fragmentã®åé€ã composable<T> ãžã®ç»é²ãäžèŠã«ãªã£ãããã²ãŒã·ã§ã³ã°ã©ãã®å®çŸ©ã®åé€ã ãããç§»è¡PRãã«å
¥ããŸã ãViewModelãUIç¶æ
ã®åœ¢ã«å¯ããäœæ¥ã¯ãç§»è¡å察å¿ããæ®ã£ãã¬ã€ã¢ãŠãXMLãäžèŠimportã®æŽçã¯ãç§»è¡åŸå¯Ÿå¿ããšããŠå¥PRã«åãåºããŸããã¬ãã¥ã¢ãŒããããã¯ç§»è¡ã®å·®åããæå倿Žãããè¿·ããªãããã§ãã æåã®ã¢ãžã¥ãŒã«ã¯æé ã確ç«ããããã«éžã³ãŸããããæé ãå®ãŸã£ãŠããã¯ã æ©èœéçºã®æ¡ä»¶ã§æãå
¥ããã¢ãžã¥ãŒã«ãåªå
ããŠç§»è¡ ããŠããŸããç§»è¡ã ããç®çã«å·¥æ°ã確ä¿ããã®ã¯é£ãããæ¡ä»¶ã§è§Šãã¢ãžã¥ãŒã«ã§ããã°åäœç¢ºèªã®æ©äŒãèªç¶ã«åŸãããããã§ãããã ãPRãšãã±ããã¯æ¡ä»¶ãšã¯å¿
ãåããŸãã 2. 芪åé¢ä¿ã«ããã¢ãžã¥ãŒã«ã®çµ±å ã¢ãžã¥ãŒã«ã®ç§»è¡ãå®äºãããããã®ã¢ãžã¥ãŒã«ã®NavHostãä»ã®NavHostãžçµã¿èŸŒããŸããçµ±åã«ã¯2çš®é¡ãããå€æåºæºãç°ãªããŸãã çµ±åã®çš®é¡ ã¿ã€ãã³ã° æ ¹æ 芪åé¢ä¿ã«ããã¢ãžã¥ãŒã«ã®çµ±åïŒåã¢ãžã¥ãŒã«ã¯èŠªã¢ãžã¥ãŒã«ããããé·ç§»ãããªãïŒ åã¢ãžã¥ãŒã«ã®å
šç»é¢ç§»è¡ãå®äºãããããã å®éã®ç»é¢éå±€ã«æ²¿ããXMLåŽã®ãšã³ããªãæ©ãæžããããPRãå°ãã ActivityçŽäžã®åäžNavHostãžã®çµ±åïŒãããã¬ãã«ã®ã¢ãžã¥ãŒã«çŸ€ïŒ è€æ°ã¢ãžã¥ãŒã«ããŸãšãŸã£ãŠããäžæ¬ 1ã€ã ãå
ã«çµ±åãããšé察称ãªäžéç¶æ
ã«ãªããActivityåŽã®NavHosté
ç·ãXMLã®å€§èŠæš¡åé€ã¯ãŸãšããŠè¡ãæ¹ããªã¹ã¯ãäœã ãã®ç« ã§ã¯åè
ãæ±ããŸããåŸè
ã¯æ¬¡ç« ã§ãã 2-1. NavGraphBuilderæ¡åŒµãšããŠã®ã°ã©ãåé¢ çµ±åã«åããŠãã¢ãžã¥ãŒã«ã®ã°ã©ã㯠NavGraphBuilder ã®æ¡åŒµé¢æ°ãšããŠåãåºããé·ç§»ã®å
¥ãå£ã NavController ã®æ¡åŒµé¢æ°ãšããŠå
¬éããŸããããã¯å
¬åŒã® Encapsulate your navigation code ã瀺ããŠãã圢ã§ããGoogleã®å
¬åŒãµã³ãã«ã¢ããªã§ããNow in Androidããåfeatureã¢ãžã¥ãŒã«ã§åãæ§æãåã£ãŠããŸãã // SettingsGraph.kt â ã°ã©ãã»Destinationã»navigateToXxx() ã1ãã¡ã€ã«ã« fun NavGraphBuilder.settingsGraph(navController: NavHostController) { // ãã¹ãã°ã©ãã®å
¥ãå£ã1-1 ã® SettingsDestination ã« data object Graph ã远å ãã navigation<SettingsDestination.Graph>( startDestination = SettingsDestination.Settings, ) { composable<SettingsDestination.Settings> { ... } composable<SettingsDestination.Account> { ... } composable<SettingsDestination.ChangeEmail> { ... } } } fun NavController.navigateToSettings() = navigate(SettingsDestination.Graph) // çµ±åå
(MypageGraph.kt): 芪ã¢ãžã¥ãŒã«ã® NavHost ã«åã®ã°ã©ããçµã¿èŸŒã fun NavGraphBuilder.mypageGraph(navController: NavHostController) { composable<MypageDestination.Home> { MypageRoute(onClickSettings = { navController.navigateToSettings() }) } settingsGraph(navController) } 2-2. äžæçãªã¢ãžã¥ãŒã«éäŸåã®æ±ã å
¬åŒã® ã¢ãžã¥ãŒã«åã¬ã€ã ã§ã¯ãæ©èœã¢ãžã¥ãŒã«ã¯ããŒã¿å±€ã®ã¢ãžã¥ãŒã«ã«äŸåããæ©èœå士ã®ããåãã¯ä»²ä»ããã¢ãžã¥ãŒã«ïŒéåžžã¯appã¢ãžã¥ãŒã«ïŒãéã圢ã瀺ãããŠããŸããFAANSã§ããæ©èœã¢ãžã¥ãŒã«å士ã¯çŽæ¥äŸåããªãããã«ãŒã«ã«ããŠããŸãããã®çµ±åã§ã¯èŠªã¢ãžã¥ãŒã«ãåã¢ãžã¥ãŒã«ãžäŸåãã圢ã«ãªãããã®ã«ãŒã«ãšäžæçã«ççŸããŸããåäžNavHostãžã®çµ±åãŸã§ã® æéä»ãã®äŸå€ ãšäœçœ®ã¥ããæ¬¡ã®æ¡ä»¶ãèšããŠããŸãã äŸåã¯äžæ¹åã®ã¿ãšãã è§£æ¶ã¿ã€ãã³ã°ãTODOã³ã¡ã³ãã§å¿
ãæç€ºãã çµ±åå
ã®èµ·ç¹Fragmentã¯ãä»ã¢ãžã¥ãŒã«ããã® <action> ãæ¶ãããŸã§åé€ããªã 3. ActivityçŽäžã®åäžNavHostãžã®çµ±å ããã¯æ¬¡ã®ãã§ãŒãºãªã®ã§ãçŸç¶ãšèŠéãã ããæžããŸãã 3-1. ã«ãŒãåŽã®çŸç¶ Activity㯠NavHostFragment ã1ã€æã¡ãã«ãŒãã®ã°ã©ãïŒXMLïŒããåã¢ãžã¥ãŒã«ã®ã°ã©ãã <include> ã§åã蟌ãã§ãããã¹ãã©ãã·ã¥ãèªèšŒãªã©ã¢ããªå
±éã®ç»é¢ã¯ãŸã Fragment ãã£ãŒããªã³ã¯ã¯Activityãåãåãã findNavController(...) ã§XMLã® <deepLink> ã«è§£æ±ºããŠãã Activityã¹ã³ãŒãã§å
±æããViewModelãæ°çš®é¡ãããå¥Activityã§åããããŒãXMLã® <activity> ã§ç¹ãã£ãŠãã 3-2. çµ±åã®èŠéã çµ±åã®æ¬äœã¯ãActivityã®äžã«1ã€ã®NavHostã眮ããåã¢ãžã¥ãŒã«ã®ã°ã©ãã䞊ã¹ãããšããé
ç·ã®çœ®ãæãã§ãç»é¢ã®ã³ãŒãã«ã¯æãå
¥ããŸããã以äžã¯çŸæç¹ã®èšç»ã§ãå®è£
ãšæ€èšŒã¯ããããã§ãã æºå ïŒåã¢ãžã¥ãŒã«ã®ã°ã©ãã NavGraphBuilder æ¡åŒµã® xxxGraph() ã«æããã2ç« ã®çµ±åã§äœ¿ã£ãŠãã圢ãšåãã§ãåäžNavHostãžã®çµ±åã§ã¯ããã䞊ã¹ãã ãã«ãªããæ®ãã®ã¢ãžã¥ãŒã«ãšã¢ããªå
±éã®ç»é¢ã®ç§»è¡ãããã«å«ãŸãã åãæ¿ã ïŒActivityã® setContent ã«ã«ãŒãã®NavHostã眮ãã NavHostFragment ã»ã«ãŒãã®ã°ã©ãïŒXMLïŒã»åã¢ãžã¥ãŒã«ã®èµ·ç¹Fragmentãåé€ãããã¢ãžã¥ãŒã«æšªæã®å€æŽãªã®ã§ãæã£ã段éã§äžåºŠã«è¡ã ä»éãã眮ãæã ïŒãã£ãŒããªã³ã¯ã¯ navDeepLink<T> ãš handleDeepLink(intent) ã§çœ®ãæãããå
±æViewModelã¯ãã¹ãã°ã©ããžã®ã¹ã³ãŒãïŒ hiltViewModel(parentEntry) ïŒãå¥Activityã®èµ·å㯠activity<T> ã䜿ããããããNavigation Composeã«åå®å
šãªAPIããã // ã«ãŒãã® NavHost(èšç») setContent { FaansTheme { val navController = rememberNavController() FaansNavHost(navController, startDestination = RootDestination.Splash) { composable<RootDestination.Splash> { ... } homeGraph(navController) // åã¢ãžã¥ãŒã«ã® xxxGraph() ã䞊ã¹ã mypageGraph(navController) // 2ç« ã§ settingsGraph() ãçµã¿èŸŒã¿æžã¿ } } } åãæ¿ãã®ååŸã§ããç§»è¡åã«ã·ããªãªãæããŠçµ±ååŸã«åãã·ããªãªãæµãæé ããã®ãŸãŸäœ¿ããŸãã ã¢ãžã¥ãŒã«åäœã§ç©ã¿äžããŠãããã®ããæåŸã«äžåºŠã ãé
ç·ãçŽã㊠ãã®ç§»è¡ãçµããèšç»ã§ãã ç§»è¡ã®äžã§èŠããŠããèª²é¡ å
±åæ§é ãã®ãã®ã¯å
¬åŒã¬ã€ãã®å»¶é·ã§ãããããå®éã«éçšãããšããå€åŽãšå
åŽã«NavControllerã2ã€ãããããšããFragmentãšComposeã§ã©ã€ããµã€ã¯ã«ãã¹ã³ãŒãã®åäœãå€ããããšã«èµ·å ãã課é¡ãããã€ãåºãŠããŸããã 課é¡1ïŒæ»ããã¿ã³ã®é£æã§ç»é¢ããã¯ã€ãã¢ãŠããã æåã®ã¢ãžã¥ãŒã«ãç§»è¡ããçŽåŸã®ãã¶ã€ã³ã¬ãã¥ãŒã§ããæ»ããã¿ã³ãé£ç¶ã§ã¿ãããããšç»é¢ãçã£çœã«ãªãããšããææãåããŸãããç¹å®ã®ç«¯æ«ã§ã ãåçŸããçŸè±¡ã§ããã åå ã¯ã 1åç®ã®æ»ãã§ç»é¢ãåãæ¿ãã£ãŠããæäžã«ã2åç®ã®æ»ããå®è¡ãããŠãã ããšã§ããã¢ãžã¥ãŒã«å
ã®NavHostã¯ç»é¢ãå°ãªãããã2åç®ã® popBackStack() ã§èµ·ç¹ç»é¢ãŸã§æ¶ããNavHostã«è¡šç€ºãããã®ããªããªã£ãŠçœãç»é¢ã«ãªããŸãã 端æ«ã®æ»ãã¯NavHostãåŠçããããã¯ã¹ã¿ãã¯1ä»¶ã§ã¯ç¡å¹ã«ãªããããèµ·ç¹ç»é¢ãŸã§ã¯æ¶ããŸãããåé¡ã¯ããŒã«ããŒã®æ»ããã¿ã³ã§ãããã§ popBackStack() ãåŒãã§ããŸããã察å¿ã¯æ¬¡ã®ãšããã§ãã ComposeåŽã®æ»ãåŠçã dropUnlessResumed ã§å
ã¿ãç»é¢ã RESUMED ã§ãªããšãã®ã¿ãããç¡èŠãã ããŒã«ããŒã®æ»ãã popBackStack() ãã navigateUp() ã«æ¿ããã navigateUp() 㯠ããã¯ã¹ã¿ãã¯ã1ä»¶ã ãã®ãšãã¯popããªã ã®ã§ãèµ·ç¹ç»é¢ãæ¶ããªã å€åŽã®NavControllerãåŒã¶çµè·¯ã§ãåãããšãèµ·ããããã RESUMED æªæºãªãäœãããªã safePopBackStack æ¡åŒµãçšæãã // 察å¿1: dropUnlessResumed ã§å
ã / 察å¿2: navigateUp() ã䜿ã onNavigateBack = dropUnlessResumed { navController.navigateUp() } // 察å¿3: å€åŽã® NavController çšã®æ¡åŒµ fun NavController.safePopBackStack(): Boolean { val currentEntry = currentBackStackEntry ?: return false return if (currentEntry.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { popBackStack() } else { false } } Lifecycle 2.8.0 ã§è¿œå ããã dropUnlessResumed ã¯ãç»é¢ïŒ NavBackStackEntry ïŒã RESUMED ã§ãªããã°ãããã¯ãæšãŠãŸãã For Navigation users, it's recommended to safeguard navigate methods when using them while a composable is in transition as a result of navigation. â dropUnlessResumed â androidx.lifecycle.compose (KDoc) èŠçŽïŒé·ç§»ã¢ãã¡ãŒã·ã§ã³äžã®navigateåŒã³åºãã¯ããã®é¢æ°ã§ã¬ãŒãããããšãæšå¥šãããŠããã 察å¿2ã§ popBackStack() ã navigateUp() ã«æ¿ããã®ã¯ã端æ«ã®æ»ããšåãæåã«å¯ããããã§ããã©ã¡ããããã¯ã¹ã¿ãã¯1ä»¶ã§popããªãããšã¯ãandroidxã® navigateUpã®å®è£
ãš æ»ãã³ãŒã«ããã¯ã®æå¹æ¡ä»¶ ã§ç¢ºèªã§ããŸãã é·ç§»äžã®ã¿ããã¯ç¡èŠãããããããŸãã«ããäžåºŠæŒãå¿
èŠãããå Žé¢ã¯ããåŸãŸãããçœãç»é¢ã«ãªãããã¯èš±å®¹ã§ãããšå€æããŸããã 課é¡2ïŒåçããã«ãŒããæ»ã£ãçŽåŸã®é·ç§»ãçºç«ããªã 課é¡1ã§å
¥ãã RESUMED ã®ã¬ãŒãããOSã®åçããã«ãŒããæ»ã£ãçŽåŸã®é·ç§»åŠçã«ãä»ãããšããã é·ç§»ãçºç«ããªããªããŸãã ãåå ã¯Activityã®ä»æ§ã§ãã An activity can never receive a result in the resumed state. You can count on onResume being called after this method, though not necessarily immediately after. â Activity.onActivityResult â Android API reference èŠçŽïŒActivity㯠RESUMED ç¶æ
ã§çµæãåãåãããšã¯ãªãã onResume ã¯ãã®åŸã«åŒã°ããã çµæãé
ä¿¡ãããæç¹ã§Activity㯠RESUMED ã§ã¯ãªãã NavBackStackEntry ã®ã©ã€ããµã€ã¯ã«ããã¹ãã®ç¶æ
ãè¶
ããããŸããã dropUnlessResumed ã RESUMED ã¬ãŒãã¯ç»é¢å
ã®ã¿ããèµ·ç¹ã«ã ãä»ããActivityResultãéåæå®äºã³ãŒã«ããã¯èµ·ç¹ã«ã¯ä»ããªãããšãã䜿ãåãã«èœã¡çããŸããã 課é¡3ïŒæªç§»è¡ã®ç»é¢ãæãã§ãããã¯ã¹ã¿ãã¯ã¯å€±ãããªãã£ã ã¢ãžã¥ãŒã«å
ã§èšå®ç»é¢ããã¢ã«ãŠã³ãèšå®ãžé²ã¿ãããããæªç§»è¡ã®Fragmentãžé·ç§»ããŠãæ»ããæŒãããšããŸããæåŸ
ããã®ã¯ã¢ã«ãŠã³ãèšå®ã«æ»ãããšã§ãããšãããæªç§»è¡ã®Fragmentããæ»ããšèµ·ç¹ã®Fragmentã®ãã¥ãŒãåçæãããããããã®äžã®NavHostãäœãçŽãã㊠èšå®ç»é¢ãŸã§å·»ãæ»ãã®ã§ã¯ãªãã ããšå¿é
ããŠããŸããã çµæã¯ãã¢ã«ãŠã³ãèšå®ã«æ»ããŸã ã rememberNavController 㯠rememberSaveable ã§ç¶æ
ãä¿æããŠããŸãããããŠFragmentã¯ãããã¯ã¹ã¿ãã¯äžã§ãã¥ãŒãç Žæ£ããéã« ãã¥ãŒããªãŒã® SavedStateRegistry ã®ç¶æ
ãä¿åãããã¥ãŒã®åçææã«åŸ©å
ããŸããNavigation ComposeåŽã®ããã¯ã¹ã¿ãã¯ã¯ãã®çµè·¯ã§åŒãç¶ãããã®ã§ãåé¡ãããŸããã§ããã 課é¡4ïŒããã ã·ãŒããéãçŽããŠãåã®å
容ã衚瀺ããã äžèЧã®é
ç®ãã¿ãããããšéãããã ã·ãŒãã§ã2ã€ç®ã®é
ç®ãã¿ããããŠã1ã€ç®ã®å
容ã衚瀺ãããçŸè±¡ãQAã§èŠã€ããŸãããããã ã·ãŒãã®äžã§ hiltViewModel() ãããŒãªãã§ååŸããŠããã®ãåå ã§ãã hiltViewModel() 㯠åŒã³åºãå
ã®é·ç§»å
ïŒ NavBackStackEntry ïŒã«ã¹ã³ãŒããããŸã ããã®ããåãç»é¢ã®äžã§æ¡ä»¶ä»ãã«è¡šç€ºãããããã ã·ãŒãã¯ãã©ã®é
ç®ããéããŠãåäžã®ViewModelã€ã³ã¹ã¿ã³ã¹ãåãåããŸããFragmentæä»£ã¯ DialogFragment ããšã«ViewModelãäœãããŠããã®ã§èµ·ããªãã£ãæåã§ããã // ä¿®æ£å: åãç»é¢å
ã§ã¯åžžã«åãã€ã³ã¹ã¿ã³ã¹ãè¿ã viewModel: ItemDetailViewModel = hiltViewModel() // ä¿®æ£åŸ: 察象IDãããŒã«ããŠãé
ç®ããšã«å¥ã®ã€ã³ã¹ã¿ã³ã¹ã«ãã viewModel: ItemDetailViewModel = hiltViewModel(key = itemId) 察å¿ã¯å¯Ÿè±¡IDãããŒã«ããããšã§ãããã ãåã圢ã®åé¡ã¯ãã€ã¢ãã°ã LaunchedEffect ã§ãèµ·ãããããã³ãŒãã£ã³ã°èŠçŽãšã¬ãã¥ãŒèгç¹ã2ã€è¿œå ããŸããã1ã€ã¯ãæ¡ä»¶ä»ãã§è¡šç€ºããããã ã·ãŒãã»ãã€ã¢ãã°ã¯å¯Ÿè±¡IDãViewModelã®ããŒã«ãããã§ãããã1ã€ã¯ããã©ã¡ãŒã¿ä»ãã³ã³ããŒãã³ãã®åæå㯠LaunchedEffect(Unit) ã§ã¯ãªãèå¥ãã©ã¡ãŒã¿ãããŒã«ãããã§ãã 課é¡5ïŒç»é¢é·ç§»ãã¹ããæžããã«ã©ãæ
ä¿ããã 1-3ã§æžãããšããã NavHostã®ç»é¢é·ç§»ãã¹ãã¯æžããŸããã§ãã ãNavigation Composeã«ã¯ TestNavHostController ãªã©ãã¹ãçšã®APIãçšæãããŠããŸãããããªã®ã«ãªãæžããªãã£ãã®ããæ€èšããé ã«æžããŸãã TestNavHostController ãš createComposeRule ã䜿ãã°ãJVMäžã®Robolectricã§é·ç§»ãã¹ãèªäœã¯æžããŸããViewModelãæããªãç»é¢ã§ã¯å®éã«åããŸãããã€ãŸãããã®ã¯ hiltViewModel() ã§ããç»é¢ã®Composableã®äžã§åŒãã§ãããšããã¹ãçšã® ComponentActivity ãHiltã®ãšã³ããªãã€ã³ãã§ã¯ãªããã倱æããŸãã解決çã¯4ã€æ€èšããŸããã 解決ç ã¡ãªãã ãã¡ãªãã 1. NavHostã®åŒæ°ã§ViewModelãåãåã ã¢ãã¯ãæž¡ãã ãã§æžã èµ·ç¹Fragment衚瀺æã«å
šViewModelãçæããã 2. ãã¹ãçšã«NavHostã®ã³ããŒãäœã æ¬çªã®åŒæ°ãå¢ãããªã NavHostæŽæ°æã«ãã¹ãã忿޿°ïŒæŒããªã¹ã¯ïŒ 3. FactoryåŒæ°ãæãããç»é¢ã®ComposableåŒã³åºãæã«çæ 1ã®åé¡ãåé¿ãã€ã€ã¢ãã¯å¯èœ NavHostã®åŒæ°ãå¢ãã 4. Hiltãã¹ãç°å¢ãæ§ç¯ãã æ¬çªã³ãŒããå€ããªã ããŒã¿å±€ã®ãã¹ãããã«çŸ€ãåæ æ¡4㯠Now in Android ãæ¡ãæ¹åŒã§ã @HiltAndroidTest ã§æ¬ç©ã®Activityãèµ·åãã @TestInstallIn ã§ããŒã¿å±€ããã¹ãçšã®å®è£
ã«å·®ãæ¿ããŸãããã ã ããŒã¿å±€ãæåããå·®ãæ¿ããããèšèšã«ãªã£ãŠããããšãåæ ã§ããFAANSã§ã¯é·ç§»ãã¹ãã®åã«ããŒã¿å±€ãäœãæ¿ããããšã«ãªããç§»è¡ãã®ãã®ãã倧ããªäœæ¥ã§ããæ®ãæ¡3ãæããã©ã³ã¹ã®è¯ã圢ã§ããããå®è£
ãžé²ãåã«ãé·ç§»ãã¹ãã§äœã確ããããã®ããæŽçãçŽããŸããã 確ããããããšãåè§£ãããšããã¿ã³ããã©ã ããåŒã°ããããšã¯Screenã®ãŠããããã¹ãã§ã navigate(X) ã§Xã衚瀺ãããããšã¯Navigationã©ã€ãã©ãªèªèº«ã®ãã¹ãã§æ
ä¿ãããŠããŸããæ®ãã®ã¯ãã©ã ãã«æ£ãã navigate ãæž¡ããŠãããããšããNavHostã®é
ç·ã ãã§ããå
¬åŒã®ãã¹ãã¬ã€ããããã¹ã察象ãããã«éå®ããŠããŸãã The Navigation component handles all the work of managing navigation between destinations, passing arguments, and working with the FragmentManager . These capabilities are already rigorously tested, so there is no need to test them again in your app. What is important to test, however, are the interactions between the app-specific code in your fragments and their NavController . â Test Navigation â Android Developers èŠçŽïŒã©ã€ãã©ãªã®æ©èœã¯åãã¹ãäžèŠããã¹ããã¹ããªã®ã¯ãã¢ããªåºæã®ã³ãŒããšNavControllerã®ã€ã³ã¿ã©ã¯ã·ã§ã³ã ãã®é
ç·ããåå®å
šã«ãŒãã«ããããšã§å€§åã®ãã¹ã¯ã³ã³ãã€ã«æã«æ€åºãããæ®ãã®ã¯ã©ã ã1è¡ã®å®£èšã ãã§ãã ãã®ããã«Hiltãå«ããã¹ãç°å¢ãæŽããã³ã¹ãã¯èŠåããªã ãšå€æããScreenã®ãŠããããã¹ãã»ã³ãŒãã¬ãã¥ãŒã»QAã§æ
ä¿ããããšã«ããŸããã1-3ã®Maestroã«ãã確èªã¯ããã®åŸã«å ãã£ããã®ã§ããé·ç§»ãè€éã§ãã¹ããå¿
èŠãªç»é¢ã«ã€ããŠã¯ããããŒããŒã¿ãçšæããçµåãã¹ãã§æžãæ¹éã§ãã ç§»è¡ã®é²æãšçŸæç¹ã®å¹æ çŽå幎ã®ããã ãæ©èœéçºãšäžŠè¡ããŠç§»è¡ãé²ããææ¬¡ã®ãªãªãŒã¹ãç¶ç¶ã§ããŸããã8ã€ã®æ©èœã¢ãžã¥ãŒã«ã®ãã¡5ã€ã®ç§»è¡ãå®äºãããã®ãã¡1ã€ã¯èŠªã¢ãžã¥ãŒã«ã®NavHostãžçµ±åãããXMLåŽã®ãšã³ããªãåé€ã§ããŠããŸããç§»è¡ãå®äºããã¢ãžã¥ãŒã«ã§ã¯ããèæ¯ã»èª²é¡ãã§æããäºé管çãæ¬¡ã®ããã«è§£æ¶ãããŠããŸãã ç§»è¡å ç§»è¡åŸïŒå®äºããã¢ãžã¥ãŒã«ïŒ ç»é¢ã1ã€è¿œå ãããšãã®äœæ¥ Fragmentã¯ã©ã¹ãäœã / XMLã« <fragment> ãš <action> ãæžã / Safe Argsãçæãã / findNavController() ã§é·ç§»ãæžã Destinationã远å ãã composable<T> ãç»é²ãããé·ç§»ã®ã©ã ããšåŒæ°ã®åãåããåãå Žæã«æžã é·ç§»ã®å®çŸ©ã»å®è¡ã»åŒæ°ã®çœ®ãå Žæ XML / Fragment / Safe Args NavHostã®1ãæã«éãŸããåŒæ°ã¯ @Serializable ãªã¯ã©ã¹ã§ã³ã³ãã€ã«æã«æ€æ»ããã å¯ç£ç©ãšããŠãã¢ãžã¥ãŒã«ããšã®ç»é¢é·ç§»ã·ããªãªãæããMaestroã§å垰確èªã§ããããã«ãªããŸãããç§»è¡ã®æé ãã¹ãã«ãšããŠæ®ã£ãŠããã®ã§ãæ®ãã®ã¢ãžã¥ãŒã«ã¯æ
åœè
ãå€ãã£ãŠãåãé²ãæ¹ãã§ããŸãã æåŸã« æ®ã3ã€ã¯ç»é¢æ°ã®å€ã倧åã¢ãžã¥ãŒã«ã§ãããåããã¿ãŒã³ãšã·ããªãªã§é²ããããèŠéããç«ã£ãŠããŸãããã®å
ã«ããActivityçŽäžã®åäžNavHostãžã®çµ±åã¯3ç« ã®èŠéãã«æ²¿ã£ãŠé²ããåå®å
šã«ãŒãã§æããŠããã®ã§Navigation 3ãžã®ç§»è¡ãåãå»¶é·ç·äžã«ãããŸãã Maestroã®ã·ããªãªã¯ä»ã®ãšããããŒã«ã«ã§å®è¡ããŠããã ãã§ããã·ããªãªãæã£ãŠãããCIã§ç¶ç¶çã«åããç»é¢é·ç§»ãå£ããŠããªãããšãèªåã§ç¢ºãããããç¶æ
ã«ã§ããã°ãšèããŠããŸããå®çŸã§ãããã¯ãŸã èŠããŠããŸããããæã£ãã·ããªãªã®æ¬¡ã®äœ¿ãéãšããŠè©Šãããããšã®1ã€ã§ãã Fragmentè³ç£ãæ±ãããŸãŸComposeãé²ããŠããæ¹ã«ãšã£ãŠã æ¢åã®ç»é¢é·ç§»ãæ¢ããã«ç§»è¡ãé²ãã1ã€ã®ããæ¹ ãšããŠåèã«ãªãã°å¹žãã§ãã ZOZOã§ã¯ãäžç·ã«ãµãŒãã¹ãäœãäžããŠãããæ¹ãåéäžã§ãããèå³ã®ããæ¹ã¯ã以äžã®ãªã³ã¯ãããã²ãå¿åãã ããã corp.zozo.com
ããã«ã¡ã¯ããã¡ã€ã³ãã£æ ªåŒäŒç€Ÿã§ã¢ãã€ã«ãšã³ãžãã¢ãããŠããå è€ã§ããæè¡ã«ã³ãã¡ã¬ã³ã¹åãã®ã¢ãã€ã«ã¢ããªãFindy EventsãããReact Nativeã§éçºããŠããŸããiOS㯠App Store ãAndroid㯠Google Play ã§å
¬éããŠããŸãã 2026幎9æ11æ¥ãã13æ¥ã«ãããŠéå¬ããã iOSDC Japan 2026 ã«åå ãããã¡ã€ã³ãã£ã®ããŒã¹ã«ãç«ã¡ãŸãããããŒã¹ã§ã¯ãæ¥ããšã«èšåãå€ããã¢ã³ã±ãŒãã宿œããŸããã Findy Eventsã®éçºã§ã¯AIãç©æ¥µçã«äœ¿ã£ãŠããŸãããUIããã¹ããã©ããŸã§ä»»ãããã¯ãä»ãèãç¶ããŠããããŒãã§ããã¯ãã¹ãã©ãããã©ãŒã ã§éçºããŠããç«å ŽãšããŠã¯ãiOSãšAndroidã§ã©ããŸã§ãå
±éã®ã³ãŒãã«ããããæ°ã«ãªã£ãŠããŸããä»åã®iOSDCã§ã¯ããã®2ã€ãé ã«çœ®ããªããã»ãã·ã§ã³ãèŽããŸããã ãã®èšäºã§ã¯ãã¢ã³ã±ãŒãã®çµæãšèŽããã»ãã·ã§ã³ã®å
容ã玹ä»ããã«ã³ãã¡ã¬ã³ã¹ã¢ããªãäœãç«å ŽãããAIã«ä»»ããç¯å²ãšOSéã§å
±æããç¯å²ã«ã€ããŠèããããšãæžããŸããåãããã«ã©ãã«ç·ãåŒãããèããŠããã¢ãã€ã«ãšã³ãžãã¢ã®æ¹ã®åèã«ãªãã°å¬ããã§ãã iOSDC Japan 2026ãšã¯ ãã¡ã€ã³ãã£ã®ããŒã¹ ã¢ã³ã±ãŒãã§èããã人éãåŠã¶ã¹ãããšã 2ã€ã®ã»ãã·ã§ã³ããèãããAIã«ä»»ããç¯å² AIæä»£ã«ãããiOSèšèšã®å®ãæ¹ãšäººéã®åœ¹å² ã¹ããã¢ããªéçºã«ãããããŒãã¹ãšã³ãžãã¢ãªã³ã°ã®åãçµã¿ Findy Eventsã®éçºã«ç
§ãããŠæããããš KMPã®äºäŸããèãããOSéã§å
±æããç¯å² Kotlin Multiplatformã軞ã«ããæ±äººã¢ããªå
šé¢ãªãã¥ãŒã¢ã«ã®æè¡æŠç¥ Findy Eventsã®æè¡éžå®ã«ç
§ãããŠæããããš ãŸãšã iOSDC Japan 2026ãšã¯ iOSDC Japan 2026ã¯ãiOSé¢é£æè¡ãã³ã¢ã®ããŒãã«ããããœãããŠã§ã¢æè¡è
ã®ããã®ã«ã³ãã¡ã¬ã³ã¹ã§ããææã»ã³ãã©ã«ã¿ã¯ãŒããŒã«ïŒã«ã³ãã¡ã¬ã³ã¹ã§ã®çŸå°éå¬ãšããã³ãã³çæŸéã§ã®é
ä¿¡ãçµã¿åããããã€ããªãã圢åŒã§ã3æ¥éã«ããã£ãŠéå¬ãããŸãããäŒå Žã«ã¯ãããŽãšã¢ã€ã³ã³ã®ã¢ã¶ã€ã¯ããããã£ã倧ããªãããŒãæ²ããããŠããŸããã iosdc.jp ãã¡ã€ã³ãã£ã®ããŒã¹ ãã¡ã€ã³ãã£ã¯ãŽãŒã«ãã¹ãã³ãµãŒãšããŠåè³ããããŒã¹ãåºå±ããŸãããããŒã¹ã®äŒç»ã¯ãDevRelããŒã ãäºåã«å
¬éããæ¬¡ã®noteèšäºã§ç޹ä»ããŠããŸãã note.com ãã¡ã€ã³ãã£ã®DevRelããŒã ã¯ããšã³ãžãã¢ã®ã¹ãã«ã¢ããããã£ãªã¢ãå¿æŽããæ¹éã§æŽ»åããŠããŸããçæAIã®æ®åã§åŠã³æ¹ãå€ãã£ãŠããŠããäžã§ãããããã®ã³ã³ãã³ãäŒç»ã®åèã«ããããã«ããšã³ãžãã¢ã®åŠã³æ¹ãæ
å ±åéãããŒãã«ããã¢ã³ã±ãŒããäŒç»ããŸããã ããŒã¹ã§ã¯ãæ¥ããšã«ç°ãªãèšåãããŒãã«æ²ç€ºããã·ãŒã«ãä»ç®ã§åçããŠããã ããŸããã ãã®èšäºã§ã¯ããã®ãã¡ç§ãæ
åœãã9æ12æ¥ã®çµæã玹ä»ããŸãã ã¢ã³ã±ãŒãã§èããã人éãåŠã¶ã¹ãããšã ãAIãã³ãŒããæžãæä»£ã«ã人éãåŠã¶ã¹ãããšãããšããèšåã§ã9ã®éžæè¢ã«513æã®ã·ãŒã«ãéãŸããŸãããåœãŠã¯ãŸãéžæè¢ã«ã·ãŒã«ã貌ã£ãŠããã ã圢åŒã§ã1人ã§è€æ°ã®éžæè¢ã«è²Œã£ãŠããã ããå Žåããããããæ¬¡ã®è¡šã®æ°ã¯åçè
æ°ã§ã¯ãªãã·ãŒã«ã®ææ°ã§ãã é äœ éžæè¢ ã·ãŒã«ã®ææ° ã·ãŒã«å
šäœã«å ããå²å 1äœ äŒç»ãèŠä»¶å®çŸ©ããããã¯ãèšèš 125æ 24.4% 2äœ ããžãã¹çè§£ãã¹ããŒã¯ãã«ããŒã³ãã¥ãã±ãŒã·ã§ã³ 113æ 22.0% 3äœ ã¢ãŒããã¯ãã£ããã¡ã€ã³ã¢ããªã³ã° 74æ 14.4% 4äœ UI/UXãã¢ã¯ã»ã·ããªã㣠62æ 12.1% 5äœ å質ä¿èšŒããã¹ãèšèš 47æ 9.2% 6äœ ã»ãã¥ãªãã£èšèšãè
åšã¢ããªã³ã° 37æ 7.2% 7äœ éçºããã»ã¹ãããŒã éçºïŒCI/CDãã¬ãã¥ãŒãªã©ïŒ 26æ 5.1% 8äœ ããã©ãŒãã³ã¹ãã¹ã±ãŒã©ããªãã£èšèš 14æ 2.7% - ãã®ä» 15æ 2.9% ç¹ã«ã·ãŒã«ã®éãŸã£ãéžæè¢ã¯ãäŒç»ãèŠä»¶å®çŸ©ããããã¯ãèšèšããšãããžãã¹çè§£ãã¹ããŒã¯ãã«ããŒã³ãã¥ãã±ãŒã·ã§ã³ãã®2ã€ã§ããã®äžäœ2ã€ãåããããš238æãå
šäœã®ååè¿ãïŒ46.4%ïŒã«ãªããŸãããAIãã³ãŒããæžãæä»£ã«åŠã¶ã¹ãããšãšããŠãäœãäœãããæ±ºããããšããé¢ä¿è
ãšè©±ããŠåæãã€ããããšãéžãã æ¹ãå€ãã£ãããšããçµæã§ãã ããŒãã®åã§ã¯ãéžãã çç±ãèãããŠããã ããŸãããäžäœã®2ã€ã«å
±éããŠããã®ã¯ããAIã ãã§æžãã€ã¡ãŒãžããããªãããšãã声ã§ããäŒç»ãèŠä»¶å®çŸ©ã«ã€ããŠã¯ããäœãçã£ãŠããã®ããã©ããªãããã¯ããäœãã®ããšãã倿ã¯äººãããã®ã§ã¯ãªããããšããæèŠã§ãããããžãã¹çè§£ã«ã€ããŠã¯ãä»ç€Ÿã®ãããã¯ãéçºã«äŸé ŒãåããŠå
±åã§åãçµãã§ããç«å Žã®æ¹ããããçžæã®äºæ¥ãçè§£ããã»ã©ãããè¯ããã®ãäžç·ã«äœããããšãã話ãèããŸãããäžæ¹ã§ããããã¯AIæä»£ã«å§ãŸã£ã話ã§ã¯ãªããæãã倧äºã ã£ãããšããæèŠããããŸããã AIæä»£ã®ãšã³ãžãã¢ã®è¶å¢ãã€ãŸãã³ãŒããæžãããšã«ãšã©ãŸãããäŒç»ãããžãã¹ã®é åãŸã§é¢ãã£ãŠããããšã¯ãæè¿ãã話é¡ã«ãªããŸããããŒã¹ã§è©±ããŠããŠãããããæèããŠããæ¹ãå€ãå°è±¡ãåããŸããã 2ã€ã®ã»ãã·ã§ã³ããèãããAIã«ä»»ããç¯å² ããŒã¹ã§è©±ããæ¹ãããããUI/UXããã¹ãã¯AIã«ä»»ãã«ããããšãã声ãèããŸãããã¢ãã€ã«ã¢ããªã§ã¯å®æ©ãã·ãã¥ã¬ãŒã¿ã§ç¢ºèªããããŒãã«ãé«ãããã®ãã¹ãŠãAIã«ä»»ããã®ã¯é£ããããšãã話ã§ããåé ã«æžãããšãããããã¯Findy Eventsã®éçºã§ãæ°ã«ãªã£ãŠãããšããã§ããAIã䜿ã£ãéçºãæ±ã£ãã»ãã·ã§ã³ã®äžããããã®ç¹ã«éãããšããããã£ã2ã€ã玹ä»ããŸãã AIæä»£ã«ãããiOSèšèšã®å®ãæ¹ãšäººéã®åœ¹å² 9æ12æ¥ã«Track Bã§è¡ããããæ ªåŒäŒç€ŸTVerã®é è€æåŒ¥ããã®ã»ãã·ã§ã³ã§ãã fortee.jp TVerã®iOSã¢ããªã§ã®è©Šè¡é¯èª€ãããšã«ããå
容ã§ãAIã¯éããã®ã®ãä»ã®æç¹ã§ã¯ãŸã ä»»ããããã人ã®å€æãå¿
èŠã ãšãã話ã§ããã ä»»ããããªãçç±ãšããŠæãã£ãŠããã®ã¯ãAIã«ã¯ã§ããªã倿ãããããšã§ãã1ã€ã¯ããã°ãšä»æ§ã®èå¥ã§ããAIã¯å
šäœã§ã¯ãªãè¿ãã®ã³ãŒããææ¬ã«ãããããæ¢åã®ã³ãŒãã«ãã°ããããšãããã仿§ãšããŠæ±ã£ãŠããŸãããšããããŸãããã1ã€ã¯ãSwiftUIãšUIKitã®ã©ã¡ããæ¡çšããããšãã倿ã§ããç¹å®ã®OSããŒãžã§ã³ã§ã¯SwiftUIãæåŸ
ã©ããã«åããªããšãã£ãäºæ
ãèžãŸããŠãã©ã¡ãã䜿ãããæ±ºããããšã¯ãAIã«ã¯é£ãããšã®ããšã§ããã ãããã課é¡ã«æ°ã¥ããã¿ã€ãã³ã°ã§AIã®ã¬ãŒãã¬ãŒã«ãæŽåããå¹çåãé²ããåãçµã¿ãç¶ããŠããããã§ããããšãã°ãã¢ãŒããã¯ãã£ã®ã¬ã€ãã©ã€ã³ãå®ããããäººãæ±ºããå
容ãCLAUDE.mdãFigmaã«æ®ãããããŠããŸãã ã¹ããã¢ããªéçºã«ãããããŒãã¹ãšã³ãžãã¢ãªã³ã°ã®åãçµã¿ 9æ12æ¥ã«Track Bã§è¡ããããæ ªåŒäŒç€Ÿã«ã«ã¯ã³ã ïŒé£ã¹ãã°ïŒã®åé幞ããã®ã»ãã·ã§ã³ã§ãã fortee.jp é£ã¹ãã°ã®ã¢ããªéçºããŒã ã¯ããã¢ããªéçºã誰ã§ãã§ããããã«ããããšããããã·ã§ã³ã®ããšã§ãAIãæ£ããåããåå°ã¥ãããé²ããŠããŸããã¿ã€ãã«ã«ããããŒãã¹ãšã³ãžãã¢ãªã³ã°ã¯ããã®åå°ã«ãããéçºç°å¢ãæ
å ±ã®æŽãæ¹ãæããŸããã»ãã·ã§ã³ã§ã¯ããããã¯ãããšã®ã¯ãŒã¯ã¹ããŒã¹ã®æ§ç¯ããCLAUDE.mdãç¶ç¶çã«æ¹åããéçšããã¡ã€ã³ç¥èããã¹ãé
ç®ã®æŽåãªã©ã玹ä»ãããŸããã AIã«æž¡ãã³ã³ããã¹ãã¯éãã質ãéèŠããåäžã®ä¿¡é Œã§ããæ
å ±æºïŒSSOTïŒãä¿ã€ãšãã話ããããæ¹åãåãç¶ããä»çµã¿ãå«ããŠããã£ãŒãããã¯ã«ãŒãã匷ãåããŠããå°è±¡ãåããŸããããã¹ãã§ã¯ãMaestroã«ããUIãã¹ãã®èªåå®è¡ã§æåQAãæžãããŠããŠããã¹ãé
ç®ã®äœæã«ãAIã䜿ãåãçµã¿ãé²ããŠããããã§ãã Findy Eventsã®éçºã«ç
§ãããŠæããããš Findy Eventsã®éçºã§ãããã¹ãé
ç®ã®äœæã«AIã䜿ã£ãŠããŸããIssueã®èŠä»¶ãFigmaã®ãã¶ã€ã³ãããšã«AIãžäœãããåé
ç®ã«ã¯é¢ä¿ããGitHubã®ã³ããããèŒããŠããŸããã»ãã®çŸå Žã§ãåãããã«ãã¹ãé
ç®ã®æŽåã«AIã䜿ã£ãŠãããšèããã®ã¯ãå¿åŒ·ãæããŸããã äžæ¹ã§ãAIãè¿ãã®ã³ãŒããææ¬ã«ããŠãã°ã仿§ãšããŠæ±ã£ãŠããŸããšãã話ã¯ãæŽå²ã®ããã¢ããªã ããããçŽé¢ãã課é¡ã®ããã«æããŸãããFindy Eventsã¯ååãªãªãŒã¹ãããŸã æ¥ãæµ
ããå®è£
ãšé£ãéã説æãå€ãèšè¿°ãããã»ã©æºãŸã£ãŠããŸãããAIãææãæžãæ¹ã®çµ±äžãææ¡ããŠãããå Žé¢ãå€ãã®ãããã®ããããããããŸãããæŽå²ãç©ã¿éãªã£ãå
ã§åãåãããšã«ãªã課é¡ãå
ã«ç¥ããã®ã¯ãåèã«ãªããŸããã KMPã®äºäŸããèãããOSéã§å
±æããç¯å² AIã®è©±ãšã¯å¥ã«ãã¯ãã¹ãã©ãããã©ãŒã ã§éçºããŠããç«å ŽããèŽããã»ãã·ã§ã³ã§ãã Kotlin Multiplatformã軞ã«ããæ±äººã¢ããªå
šé¢ãªãã¥ãŒã¢ã«ã®æè¡æŠç¥ 9æ13æ¥ã«Track Cã§è¡ãããããã£ããæ ªåŒäŒç€Ÿã®å®®å·æé«ããã®ã»ãã·ã§ã³ã§ãã fortee.jp ã¹ã©ã€ããå
¬éãããŠããŸãã speakerdeck.com äºæ¥æŠç¥ãšã¢ãŒããã¯ãã£ã®ãºã¬ãè§£æ¶ããããã«ã24幎ç¶ãã¢ã«ãã€ãæ±äººãµãŒãã¹ã®iOSã¢ããªãå
šé¢ãªãã¥ãŒã¢ã«ããŠããäºäŸã§ãã Kotlin MultiplatformïŒKMPïŒãæ¡çšããèæ¯ã«ã¯ãã¢ããªéã§å·®ç°ãçãŸãããšãã課é¡ããããŸãããiOSãšAndroidã®ããŒã ãããããæ¥åã«ãŒã«ããã©ããã³ã°èŠä»¶ãçè§£ããå®è£
ãšãã¹ããããŠããããã§ããããã§ãå
±æããé åãæè»ã«éžã¹ãããšãUIã¯åOSã®ãã€ãã£ãå®è£
ãç¶æã§ããããšã瀟å
ã®Kotlinçµéšè
ã®ç¥èŠã掻ãããããšãããKMPãéžãã§ããŸãã KMPãæ¡çšãããšãiOSãšã³ãžãã¢ãKotlinã®ã³ãŒãã«è§Šããããšã«ãªããŸããããã§ããKMPã倿ŽããPull Requestã®çŽ4å²ãiOSéçºè
ã起祚ããŠããããã§ããiOSãšAndroidã®éçºè
ãäžç·ã«ä¿å®ã§ããŠãããšæããæ°åã§ããã äžæ¹ã§ãiOSã«KMPãçµã¿èŸŒãäžã§ã¯ãKMPããexportãããåãSwiftã³ã³ãã€ã©ãSendableãšããŠæ€èšŒã§ããªããšãããSwift Concurrencyãšã®å¢çã®èª²é¡ãèŠã€ãã£ãããã§ããããã§ããã¢ããªéã®å·®ç°ãã¡ã³ããã³ã¹ã®è² æ
ãè§£æ¶ããããã®ãã¬ãŒããªããšããŠããã®èª²é¡ãåãå
¥ãã倿ãããŠããŸããã Findy Eventsã®æè¡éžå®ã«ç
§ãããŠæããããš Findy Eventsã¯æ°èŠã¢ããªãšããŠç«ã¡äžãããæå°ãªãœãŒã¹ã§æéãªãªãŒã¹ããåæã«React Nativeãéžã³ãŸãããæ±ºãæã¯ãReactãšTypeScriptã®çŽ é€ãæã€ã¡ã³ããŒãå€ãçµç¹ã®ã¢ã»ãããšãiOSãšAndroidã®ãã©ãããã©ãŒã ç¹æ§ãçè§£ããŠããã¢ãã€ã«ãšã³ãžãã¢ãšããŠã®ãã¬ããžã§ãã KMPã®äºäŸã§ãã瀟å
ã®Kotlinçµéšè
ã®ç¥èŠã掻ãããããšãéžå®çç±ã«æãã£ãŠããŸãããéžãã æè¡ã¯éã£ãŠããçµç¹ãæã£ãŠããç¥èãæŽ»ãããšãã芳ç¹ã¯å
±éããŠãããšæããŸããã ãŸãšã ä»åã®iOSDCã§èã§æããã®ã¯ãã³ãŒããæžãããšã®AIåã¯ç¢ºå®ã«é²ãã§ããŠã人ãã¡ã€ã³ã§æ
ãé åããã³ãŒããæžãåã®å·¥çšãžç§»ã£ãŠããŠãããšããããšã§ããããŒã¹ã®ã¢ã³ã±ãŒãã§äžäœã«æãã£ãã®ã¯ãäŒç»ãèŠä»¶å®çŸ©ãããžãã¹çè§£ã§ããã ãã ãããã§å€§äºã ãšèšãããŠããããšã®å€ãã¯ãAIæä»£ã«å§ãŸã£ã話ã§ã¯ãããŸãããäœãäœãããæ±ºããããšããé¢ä¿è
ãšè©±ããŠåæãã€ããããšããæãã倧äºã§ãããAIãã³ãŒããæžãããã«ãªã£ãããšã§ãã³ãŒããæžãããšã«äœ¿ã£ãŠããæéã空ãããã®åãæåã®å·¥çšã«äœ¿ãããããã«ãªã£ãçµæããã®å·¥çšã«æ¹ããŠæèãåãããã«ãªã£ãã®ã ãšæããŸãã Findy Eventsã®éçºã§ãåãèª²é¡æãæã£ãŠããã®ã§ãã»ãã®çŸå Žã®æ¹ãã話ãèããã®ã¯åç©«ã§ãããKMPã®äºäŸã®ããã«ãæè¡éžå®ããŸããçµç¹ããããã¯ãã®åæãèžãŸããŠäººã決ãã倿ã§ããã¢ã³ã±ãŒãã®9ã€ã®éžæè¢ãèªåã®ããŒã ã«åœãŠã¯ããŠãããŸAIã«ä»»ããŠããç¯å²ãšäººãæ
ã£ãŠããç¯å²ã䞊ã¹ãŠã¿ããšãæ¬¡ã«æãå
¥ããå ŽæãèŠããŠãããããããŸããã æåŸã«ãiOSDC Japan 2026ãéå¶ããŠãã ãã£ãå®è¡å§å¡äŒãšã¹ã¿ããã®çããŸãç»å£è
ã®çããŸããããŠããŒã¹ã§ã¢ã³ã±ãŒãã«çããŠãã ãã£ãçããŸã«æè¬ç³ãäžããŸãã ãã¡ã€ã³ãã£ã§ã¯äžç·ã«äŒç€ŸãçãäžããŠãããã¡ã³ããŒãåéäžã§ããèå³ãæã£ãŠããã ããæ¹ã¯ãã¡ãã®ããŒãžãããå¿åãé¡ãããŸãã herp.careers
Webãã¢ããªã±ãŒã·ã§ã³éçºã«ãããŠãã¢ã¯ã»ã·ããªãã£ããããŠãŒã¶ãŒè£å©ããšããèšèãèããšãçããã¯ã©ãæããã§ããããããéèŠã ãšã¯ããã£ãŠããããã©ãéçºã³ã¹ãããããããäžéšã®ãŠãŒã¶ãŒã®ããã®ç¹å¥ãªå¯Ÿå¿ããšãã£ãã€ã¡ãŒãžãæã€æ¹ãå°ãªããªããããããŸããã ããããæè¡çãªèгç¹ããã¢ã¯ã»ã·ããªãã£ã®æ§é ãçŽè§£ããšãããã«ã¯ãšã³ãžãã¢ã«ãšã£ãŠãåççã§ãçŸããã¢ãŒããã¯ãã£ãååšããŸãã çµè«ããèšããšãã¢ã¯ã»ã·ããªãã£å¯Ÿå¿ãšã¯ããŠãŒã¶ãŒã®ããã®ç¹å¥ãªUIããŒãããäœããããšã§ã¯ãããŸããã ããŠãŒã¶ãŒãå©çšããŠããç°å¢ïŒOSããã©ãŠã¶ãªã©ïŒããšãã¢ããªã±ãŒã·ã§ã³ããæ£ããããŒã¿ã飿ºããæ©ã¿å¯ãããšã§åããŠæç«ããä»çµã¿ ãªã®ã§ãã ä»åã¯ãã¢ã¯ã»ã·ããªãã£ã®ã¯ããã®äžæ©ãšããŠãç°å¢ãšã¢ããªã®æ¡æãã«ã€ããŠè§£èª¬ããŸãã ãŠãŒã¶ãŒãæ¯ãã匷åãªæŠåšïŒãç°å¢ãã®æ¯æŽæè¡ ã¢ã¯ã»ã·ããªãã£ãèããäžã§å€§åæãšãªãã®ããããžã¿ã«ç°å¢ãžã®ã¢ã¯ã»ã¹ãæ¯ããä»çµã¿ã®å€§åã¯ãç°å¢åŽãã«ãã§ã«çšæãããŠããããšããããšã§ãã çŸä»£ã®OSïŒiOSãAndroidãWindowsãmacOSïŒãWebãã©ãŠã¶ã«ã¯ããŠãŒã¶ãŒã®å€æ§ãªç¹æ§ïŒèŠèŠãèŽèŠãéåæ©èœãèªç¥ãªã©ïŒãè£å©ããé«åºŠãªæ©èœãæšæºæèŒãããŠããŸãããããã¯å€§ãã2ã€ã®ã¬ã€ã€ãŒã«åããããŸãã ãŠãŒã¶ãŒãšãŒãžã§ã³ãïŒãã©ãŠã¶ãªã©ïŒïŒ ãºãŒã ãæåãµã€ãºãžã®å¯Ÿå¿ãããŒããŒãæäœãªã©ã衚瀺ãæäœã調æŽããæ©èœã æ¯æŽæè¡ïŒAssistive TechnologyïŒïŒ ã¹ã¯ãªãŒã³ãªãŒããŒïŒVoiceOver / TalkBack çïŒãã¹ã€ããã³ã³ãããŒã«ãªã©ãOSã®APIãéããŠããã€ã¹ã®æäœãçŽæ¥çã«è£å©ããæ©èœã ãŠãŒã¶ãŒã¯èªèº«ã®ç¶æ³ã«åãããŠããããã®æ©èœãONã«ãããèšå®ã調æŽãããããŠãWebã«ã¢ã¯ã»ã¹ããŠããŸããã€ãŸãããŠãŒã¶ãŒããå·®ãåºããããæ¡æã®ããã®æãã¯ããã§ã«ç°å¢åŽã«çšæãããŠããã®ã§ãã ãã ããã¢ã¯ã»ã·ããªãã£ã®ãã¹ãŠãç°å¢åŽãèªåã§æ
ã£ãŠãããããã§ã¯ãããŸãããã¢ããªã±ãŒã·ã§ã³èªèº«ããååãªã³ã³ãã©ã¹ãã®ç¢ºä¿ããã©ãŒã«ã¹é åºã®ç®¡çãé©åãªèŠåºãæ§é ãªã©ã責任ãæã£ãŠèšèšããç°å¢åŽãšæ£ããé£åããå¿
èŠããããŸãã ã¢ããªã±ãŒã·ã§ã³åŽã®åœ¹å²ïŒæ
å ±ã®ãªã¬ãŒã𿡿 ãŠãŒã¶ãŒãã¹ã¯ãªãŒã³ãªãŒããŒãONã«ããŠããã°ãã©ããªWebãµã€ãã§ãå®ç§ã«é³å£°ã§æäœã§ããã®ã§ããããïŒ çãã¯ãNoãã§ãã ã©ããªã«ç°å¢åŽïŒãã©ãŠã¶ãæ¯æŽæè¡ïŒãåªç§ã§ããã¢ããªã±ãŒã·ã§ã³åŽããä»ãç»é¢ã«äœã衚瀺ãããŠããŠãã©ã®ãã¿ã³ãæŒããã®ãããšããæå³æ
å ±ïŒã»ãã³ãã£ã¯ã¹ïŒãäŒããŠããªããã°ãæ¯æŽæè¡ã¯æ£ããæ©èœããŸããã Webã®äžçã§ã¯ãæžããã³ãŒãã¯ä»¥äžã®ããã«æ
å ±ãäŒéããŠãŠãŒã¶ãŒã«å±ããŸãã 1. Webã¢ã㪠(HTML / CSS / JavaScript)    â 2. ãã©ãŠã¶ (ãŠãŒã¶ãŒãšãŒãžã§ã³ã) â»DOMããªãŒãšåæã«ãã¢ã¯ã»ã·ããªãã£ããªãŒããæ§ç¯ Â Â Â â 3. OSã®ã¢ã¯ã»ã·ããªãã£API    â 4. æ¯æŽæè¡ (ã¹ã¯ãªãŒã³ãªãŒããŒãªã©)    â 5. ãŠãŒã¶ãŒ ãã©ãŠã¶ã¯ç»é¢ãæç»ããDOMããªãŒãšã¯å¥ã«ãæ¯æŽæè¡åãã®è£åŽã®ããŒã¿æ§é ã§ãããã¢ã¯ã»ã·ããªãã£ããªãŒããçæããŸããïŒâ»Chrome DevToolsã®ãAccessibilityïŒãŠãŒã¶ãŒè£å©ïŒãã¿ããªã©ã§å®éã«ç¢ºèªããããšãã§ããŸãïŒã HTMLã¯ããã®ã¢ã¯ã»ã·ããªãã£ããªãŒãæ£ããæ§ç¯ããããã®ãæç€ºæžããªã®ã§ãããã®ãªã¬ãŒãéåãããããç°å¢ãšæ£ããæ¥ç¶ïŒæ¡æïŒããããšãããããšã³ãžãã¢ã®åœ¹å²ã§ãã Microsoftãµã€ãã®ã¢ã¯ã»ã·ããªãã£ããªãŒ Webããã³ããšã³ãã«ããããæ©ã¿å¯ããã®å
·äœäŸ å
·äœçã«ãç°å¢ã𿡿ããããšã¯ã©ãããããšããå®åã§ãããã¡ãªã¢ã³ããã¿ãŒã³ãšæ¹åäŸãèŠãŠã¿ãŸãããã 1. é©åãªHTMLã¿ã°ã䜿ãïŒã»ãã³ãã£ã¯ã¹ã®äŒéïŒ NGïŒ <div onClick={submit}>éä¿¡</div> èŠèŠçã«ã¯ãã¿ã³ã«èŠããŠãããã©ãŠã¶ã«ã¯ãbuttonãšããŠã®åœ¹å²ïŒRoleïŒããäŒãããŸããããã®çµæãã¹ã¯ãªãŒã³ãªãŒããŒã§ãã¿ã³ãšããŠèªèãããªãã ãã§ãªããTabããŒã«ããããã©ãŒã«ã¹ç§»åãããEnter/SpaceããŒã«ãããæšæºçãªããŒããŒãæäœããäžå¯èœã«ãªããŸãã OKïŒ <button onClick={submit}>éä¿¡</button> <button>ã¿ã°ã䜿ãããšã§ããã©ãŠã¶ãæã€æšæºçãªã»ãã³ãã£ã¯ã¹ãšããŒããŒãæäœã¢ãã«ãå©çšã§ããç°å¢ãšã®æ¡æãã¹ã ãŒãºã«æç«ããŸãã 2. ãã©ãŒã ã®å
¥åæ¬ã«ã©ãã«ãçŽä»ããïŒé¢ä¿æ§ã®äŒéïŒ NGïŒ <span>ãååïŒ</span><input type="text" /> èŠèŠçã«ã¯ãååã®å
¥åæ¬ãã§ãããæ¯æŽæè¡ãå
¥åæ¬ã«ãã©ãŒã«ã¹ããéãåã«ãããã¹ããç·šéããã¹ãããšããèªã¿äžãããããäœãå
¥åãã¹ããåãããŸããã OKïŒ <label for="name">ãååïŒ</label><input type="text" id="name" /> <label> ã® for 屿§ãš <input> ã® id ãçŽä»ããããšã§ãããã¯ãååã®å
¥åæ¬ã ããšæç¢ºã«äŒãããŸããããã«ãã©ãã«ã®æåãã¯ãªãã¯ããŠãå
¥åæ¬ã«ãã©ãŒã«ã¹ãåœããããã«ãªããããŠã¹ãŠãŒã¶ãŒã®æäœæ§ãåäžããŸãã 3. ã¢ã€ã³ã³ãã¿ã³ã«æå³ãæãããïŒãã ãARIAã¯èšç»çã«ïŒ NGïŒ <button><img src="search-icon.png" /></button> æ¯æŽæè¡ã¯äœã®ãã¿ã³ãå€å®ã§ããŸããã OKïŒ <button aria-label="æ€çŽ¢"><img src="search-icon.png" alt="" /></button> aria-label ãªã©ãä»äžããããšã§ãæ
å ±ãè£ãããšãã§ããŸãã ãæ³šæã ãã ããW3Cã«ã¯ãARIAã®ç¬¬äžååïŒThe First Rule of ARIA UseïŒããšãããã®ããããŸããããã¯ãæšæºHTMLã§è¡šçŸã§ããå Žåã¯ããŸãæšæºHTMLã䜿ãããšããã«ãŒã«ã§ããARIAã¯äžèœè¬ã§ã¯ãªãã誀çšãããšæ¯æŽæè¡ãæ··ä¹±ãããããããããŸã§HTMLã®è£å©ãšããŠå©çšããŸãããã 4. ãã©ãŒã«ã¹ãèŠããããã«ããïŒããŒããŒãæäœã®æ
ä¿ïŒ NGïŒ *:focus { outline: none; } ãã¶ã€ã³äžã®çç±ã§ãã©ãŒã«ã¹ãªã³ã°ïŒæ ç·ïŒãæ¶ããŠããŸããšãããŠã¹ã䜿ããããŒããŒãã§æäœããŠãããŠãŒã¶ãŒã¯ãä»ãç»é¢ã®ã©ãã«ããã®ãããå
šãåãããªããªããŸãã OKïŒ :focus-visible æ¬äŒŒã¯ã©ã¹ãªã©ã掻çšããããŒããŒãæäœæã®ã¿ãã©ãŒã«ã¹ãªã³ã°ã衚瀺ããããã¹ã¿ã€ãªã³ã°ãããç°å¢ããã®ã¢ã¯ã»ã¹çµè·¯ãèŠèŠçã«é®æããªãããšãéèŠã§ãã ãç°å¢ããšãã¢ããªãã®æ©èœããªãŒããŒã©ããããé å ããã§äžã€ãå®è·µçãªèгç¹ãä»ãå ããŸããOSããã©ãŠã¶ã®æšæºæ©èœãšãã¢ããªã±ãŒã·ã§ã³ãç¬èªã«æäŸããæ©èœã¯ã 圹å²ããªãŒããŒã©ããïŒéè€ïŒããŠããé å ãååšããŸãã äŸãã°ãèªæ²»äœã®ãµã€ããªã©ã§ããèŠããããæåãµã€ãºå€æŽïŒå€§ã»äžã»å°ïŒãã®ãã¿ã³ã§ãã æåã®æ¡å€§ã¯ãæ¬æ¥ãã©ãŠã¶ã®ãºãŒã æ©èœãOSã®èšå®ã§è¡ããŸãããããããã¹ãŠã®ãŠãŒã¶ãŒãç°å¢ã®èšå®æ¹æ³ã«ç¿çããŠããããã§ã¯ãªããããã¢ããªåŽããããŠç¬èªã®UIãæäŸããç°å¢åŽã®æ©èœããå Žåã«ãã£ãŠã¯è£å®ãããŠããã®ã§ãã ããã§èª€è§£ããŠã¯ãããªãã®ã¯ããã¢ããªåŽã§ç¬èªã«ãã¿ã³ãäœã£ããããç°å¢åŽã®èšå®ã¯ç¡èŠããŠããããšããããã§ã¯ãªãããšã§ããç¬èªã®æåæ¡å€§ãã¿ã³ãäœã£ããããšãã£ãŠããã©ãŠã¶ã®æšæºãºãŒã æ©èœã䜿ã£ãæã«ã¬ã€ã¢ãŠãã厩ããŠããŸã£ãŠã¯æ¬æ«è»¢åã§ãã éèŠãªã®ã¯ãã¢ã¯ã»ã·ããªãã£æ©èœã远å ããããšä»¥äžã«ãæ¢åã®ç°å¢ã®æ©èœããå£ããªããããšããŠãŒã¶ãŒããã©ãŠã¶ã®æšæºãºãŒã æ©èœã䜿ã£ããšããŠããã¬ã€ã¢ãŠããç Žç¶»ããã³ã³ãã³ããå©çšã§ãããããªãWebæ¬æ¥ã®å
ç¢ã§ã¬ã¹ãã³ã·ããªèšèšãæ±ããããŸãã ãŸãšãïŒæ
å ±ããæšæºçãªåœ¢ãã§å
¬éããã ã¢ã¯ã»ã·ããªãã£å¯Ÿå¿ã®æ žå¿ã¯ããã¢ããªã±ãŒã·ã§ã³ãæã€æ
å ±ããOSãæ¯æŽæè¡ãçè§£ã§ããæšæºçãªåœ¢ã§å
¬éããããšãã«å°œããŸãã ãŠãŒã¶ãŒã䌞ã°ããæïŒæ¯æŽæè¡ããã©ãŠã¶ã®èšå®ïŒã«å¯ŸããŠãã¢ããªã±ãŒã·ã§ã³åŽããããã£ãããšæãæ¡ãè¿ãïŒããŒã¿ãæ£ããæž¡ãïŒããããããšã³ãžãã¢ã«ãããã¢ã¯ã»ã·ããªãã£å¯Ÿå¿ã®çé«ã§ãã ææ¥ããã®éçºã§ããã²ä»¥äžã®ãæåã®äžæ©ãã詊ããŠã¿ãŠãã ããã ããŒããŒãã ãã§æäœããŠã¿ãïŒ ããŠã¹ã眮ããTab ããŒãš Enter / Space ããŒã ãã§ãèªåãäœã£ãã¢ããªã®äž»èŠãªæ©èœã䜿ããã確èªããã ããŒã«ã§ç£æ»ããŠã¿ãïŒ Chromeã®Lighthouseããaxe DevToolsãªã©ã®ãã©ãŠã¶æ¡åŒµæ©èœã䜿ã£ãŠãèªåã®ããŒã«ã«ç°å¢ã®ã³ãŒãããã§ãã¯ããŠã¿ãã ãç°å¢ããšãã¢ããªãã®ããŒã¿é£æºãçµå®ãããšããæžããã³ãŒãã¯ãã匷åºã§ãæ¬åœã«å€ãã®äººã«å±ããœãããŠã§ã¢ã«ãªããŸãããã²ããã©ãŠã¶ãOSãšã®å¯Ÿè©±ããæèããå®è£
ãçã蟌ãã§ã¿ãŠãã ããã ã芧ããã ãããããšãããããŸãïŒ ãã®æçš¿ã¯ã圹ã«ç«ã¡ãŸãããïŒ åœ¹ã«ç«ã£ã 圹ã«ç«ããªãã£ã 0人ããã®æçš¿ã¯åœ¹ã«ç«ã£ããšèšã£ãŠããŸãã The post ã¢ã¯ã»ã·ããªãã£ã¯ãç°å¢ããšãã¢ããªãã®æ¡æã§ãã ãWebã®æ§é ããçŽè§£ããšã³ãžãã¢ã®åœ¹å²ã first appeared on SIOS Tech Lab .
















