{"id":3303,"date":"2026-04-16T21:01:01","date_gmt":"2026-04-16T20:01:01","guid":{"rendered":"https:\/\/www.kangama.com\/?p=3303"},"modified":"2026-04-26T09:57:12","modified_gmt":"2026-04-26T08:57:12","slug":"observable-dans-swiftui-ios17-state-bindable","status":"publish","type":"post","link":"https:\/\/www.kangama.com\/en\/observable-dans-swiftui-ios17-state-bindable\/","title":{"rendered":"@Observable dans SwiftUI : comprendre le nouveau syst\u00e8me d\u2019observation introduit avec iOS 17"},"content":{"rendered":"<div id=\"bsf_rt_marker\"><\/div>\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>Depuis les premi\u00e8res versions de SwiftUI, la gestion de l\u2019\u00e9tat repose sur un ensemble de m\u00e9canismes qui, bien que puissants, ont rapidement montr\u00e9 certaines limites dans des projets concrets. Entre&nbsp;<code>ObservableObject<\/code>,&nbsp;<code>@Published<\/code>,&nbsp;<code>@StateObject<\/code>ou encore&nbsp;<code>@ObservedObject<\/code>, il n\u2019\u00e9tait pas toujours \u00e9vident de comprendre pr\u00e9cis\u00e9ment comment et pourquoi une vue se mettait \u00e0 jour.<\/p>\n\n\n\n<p>Dans la continuit\u00e9 de mon d\u00e9veloppement de mon <a href=\"https:\/\/www.kangama.com\/outil-macos-nettoyer-cache-developpeur\/\" data-type=\"link\" data-id=\"https:\/\/www.kangama.com\/outil-macos-nettoyer-cache-developpeur\/\">application macOS de nettoyage de caches d\u00e9veloppeur<\/a>, je poursuis mon exploration de SwiftUI et de ses \u00e9volutions r\u00e9centes. Ce type de projet concret met rapidement en lumi\u00e8re les limites des anciens m\u00e9canismes, notamment d\u00e8s que l\u2019\u00e9tat devient un peu plus complexe ou partag\u00e9 entre plusieurs vues.<\/p>\n\n\n\n<p>Avec l\u2019arriv\u00e9e d\u2019iOS 17, Apple a d\u00e9cid\u00e9 de repenser en profondeur ce syst\u00e8me en introduisant le framework&nbsp;<strong>Observation<\/strong>&nbsp;et le macro&nbsp;<code>@Observable<\/code>. Derri\u00e8re cette \u00e9volution se cache un objectif clair : rendre l\u2019observation des donn\u00e9es&nbsp;<strong>plus fine, plus performante et surtout plus intuitive<\/strong>&nbsp;\u00e0 utiliser au quotidien.<\/p>\n\n\n\n<p>Dans cet article, nous allons prendre le temps de comprendre ce qui change r\u00e9ellement, pourquoi ce nouveau mod\u00e8le simplifie le d\u00e9veloppement SwiftUI, et comment adapter votre code existant sans introduire de complexit\u00e9 inutile.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Une nouvelle philosophie : @Observable dans SwiftUI<\/h2>\n\n\n\n<p>L\u00e0 o\u00f9&nbsp;<code>ObservableObject<\/code>&nbsp;reposait sur une logique relativement globale,&nbsp;<code>@Observable<\/code>&nbsp;introduit une approche beaucoup plus pr\u00e9cise, bas\u00e9e sur l\u2019utilisation r\u00e9elle des donn\u00e9es dans les vues.<\/p>\n\n\n\n<p>Concr\u00e8tement, cela signifie que SwiftUI ne va plus observer un objet dans son ensemble, mais uniquement les propri\u00e9t\u00e9s qui sont effectivement utilis\u00e9es dans le&nbsp;<code>body<\/code>&nbsp;d\u2019une vue. Cette diff\u00e9rence peut sembler subtile au premier abord, mais elle change profond\u00e9ment la mani\u00e8re dont les mises \u00e0 jour sont d\u00e9clench\u00e9es.<\/p>\n\n\n\n<p>Prenons un exemple simple pour illustrer ce comportement :<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>@Observable\nclass CounterViewModel {\n    var count: Int = 0\n    var title: String = \"Compteur\"\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">@Observable<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F286C4\">class<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">CounterViewModel<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> count: <\/span><span style=\"color: #97E1F1; font-style: italic\">Int<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #BF9EEE\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> title: <\/span><span style=\"color: #97E1F1; font-style: italic\">String<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #E7EE98\">Compteur<\/span><span style=\"color: #DEE492\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Dans une vue SwiftUI :<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>struct CounterView: View {\n    @State private var viewModel = CounterViewModel()\n\n    var body: some View {\n        Text(\"\\(viewModel.count)\")\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">struct<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">CounterView<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">View <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@State<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">private<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> viewModel <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">CounterViewModel<\/span><span style=\"color: #F6F6F4\">()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> body: <\/span><span style=\"color: #F286C4\">some<\/span><span style=\"color: #F6F6F4\"> View {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        <\/span><span style=\"color: #97E1F1\">Text<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F286C4\">\\(<\/span><span style=\"color: #E7EE98\">viewModel.<\/span><span style=\"color: #BF9EEE\">count<\/span><span style=\"color: #F286C4\">)<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F6F6F4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Dans ce cas pr\u00e9cis, seule la propri\u00e9t\u00e9&nbsp;<code>count<\/code>&nbsp;est r\u00e9ellement observ\u00e9e par SwiftUI. Si&nbsp;<code>title<\/code>&nbsp;est modifi\u00e9e, la vue ne sera pas redessin\u00e9e, car elle n\u2019est jamais utilis\u00e9e dans le&nbsp;<code>body<\/code>. Ce comportement permet d\u2019\u00e9viter des rafra\u00eechissements inutiles, ce qui devient particuli\u00e8rement int\u00e9ressant dans des interfaces plus complexes.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Les limites de ObservableObject dans les projets r\u00e9els<\/h2>\n\n\n\n<p>Pour bien mesurer l\u2019apport de&nbsp;<code>@Observable<\/code>, il est utile de revenir sur les limites que l\u2019on rencontre avec&nbsp;<code>ObservableObject<\/code>dans un contexte r\u00e9el.<\/p>\n\n\n\n<p>Le premier point concerne le caract\u00e8re global de l\u2019observation. D\u00e8s qu\u2019une propri\u00e9t\u00e9 marqu\u00e9e avec&nbsp;<code>@Published<\/code>&nbsp;est modifi\u00e9e, l\u2019ensemble des vues qui observent l\u2019objet sont notifi\u00e9es, ind\u00e9pendamment de la propri\u00e9t\u00e9 r\u00e9ellement utilis\u00e9e. Cela peut rapidement entra\u00eener des recalculs inutiles et d\u00e9grader les performances, notamment lorsque les mod\u00e8les deviennent plus riches.<\/p>\n\n\n\n<p>Un autre aspect concerne la verbosit\u00e9 du code. Entre la d\u00e9claration des propri\u00e9t\u00e9s avec&nbsp;<code>@Published<\/code>, l\u2019utilisation de&nbsp;<code>@StateObject<\/code>&nbsp;pour g\u00e9rer le cycle de vie, et&nbsp;<code>@ObservedObject<\/code>&nbsp;pour la propagation, on se retrouve avec un ensemble de r\u00e8gles qui alourdissent la lecture et rendent l\u2019intention moins claire.<\/p>\n\n\n\n<p>Exemple classique avec&nbsp;<code>ObservableObject<\/code>&nbsp;:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>class CounterViewModel: ObservableObject {\n    @Published var count: Int = 0\n    @Published var title: String = \"\"\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">class<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">CounterViewModel<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">ObservableObject <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@Published<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> count: <\/span><span style=\"color: #97E1F1; font-style: italic\">Int<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #BF9EEE\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@Published<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> title: <\/span><span style=\"color: #97E1F1; font-style: italic\">String<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #DEE492\">&quot;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>M\u00eame si seule la propri\u00e9t\u00e9&nbsp;<code>count<\/code>&nbsp;est utilis\u00e9e dans la vue, une modification de&nbsp;<code>title<\/code>&nbsp;entra\u00eenera un rafra\u00eechissement. Ce comportement peut sembler acceptable sur des cas simples, mais devient rapidement probl\u00e9matique \u00e0 grande \u00e9chelle.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Comprendre le tracking des propri\u00e9t\u00e9s dans SwiftUI<\/h2>\n\n\n\n<p>Le v\u00e9ritable changement introduit avec&nbsp;<code>@Observable<\/code>&nbsp;repose sur le m\u00e9canisme de tracking des acc\u00e8s aux propri\u00e9t\u00e9s. Plut\u00f4t que de notifier toutes les vues \u00e0 chaque modification, SwiftUI enregistre pr\u00e9cis\u00e9ment quelles propri\u00e9t\u00e9s sont utilis\u00e9es lors de l\u2019\u00e9valuation du&nbsp;<code>body<\/code>.<\/p>\n\n\n\n<p>Ce m\u00e9canisme s\u2019appuie sur le framework&nbsp;<strong>Observation<\/strong>, qui intercepte les lectures de propri\u00e9t\u00e9s. Lorsqu\u2019une vue acc\u00e8de \u00e0 une valeur, cette d\u00e9pendance est enregistr\u00e9e. Par la suite, seule une modification de cette propri\u00e9t\u00e9 sp\u00e9cifique d\u00e9clenchera une mise \u00e0 jour.<\/p>\n\n\n\n<p>Ce mod\u00e8le permet d\u2019obtenir un comportement beaucoup plus pr\u00e9visible. L\u00e0 o\u00f9 auparavant il \u00e9tait parfois difficile de comprendre pourquoi une vue se rafra\u00eechissait, le lien entre l\u2019\u00e9tat et le rendu devient d\u00e9sormais explicite.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Migrer un ViewModel vers @Observable<\/h2>\n\n\n\n<p>La migration depuis&nbsp;<code>ObservableObject<\/code>&nbsp;vers&nbsp;<code>@Observable<\/code>&nbsp;est g\u00e9n\u00e9ralement assez simple, mais elle implique de revoir certains changements.<\/p>\n\n\n\n<p>Prenons un exemple classique :<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>class UserViewModel: ObservableObject {\n    @Published var name: String = \"John\"\n    @Published var age: Int = 30\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">class<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">UserViewModel<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">ObservableObject <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@Published<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> name: <\/span><span style=\"color: #97E1F1; font-style: italic\">String<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #E7EE98\">John<\/span><span style=\"color: #DEE492\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@Published<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> age: <\/span><span style=\"color: #97E1F1; font-style: italic\">Int<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #BF9EEE\">30<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Dans une vue :<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>struct UserView: View {\n    @StateObject private var viewModel = UserViewModel()\n\n    var body: some View {\n        VStack {\n            Text(viewModel.name)\n            Text(\"\\(viewModel.age)\")\n        }\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">struct<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">UserView<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">View <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@StateObject<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">private<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> viewModel <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">UserViewModel<\/span><span style=\"color: #F6F6F4\">()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> body: <\/span><span style=\"color: #F286C4\">some<\/span><span style=\"color: #F6F6F4\"> View {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        <\/span><span style=\"color: #97E1F1\">VStack<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #97E1F1\">Text<\/span><span style=\"color: #F6F6F4\">(viewModel.name)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #97E1F1\">Text<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F286C4\">\\(<\/span><span style=\"color: #E7EE98\">viewModel.<\/span><span style=\"color: #F6F6F4\">age<\/span><span style=\"color: #F286C4\">)<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F6F6F4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Avec&nbsp;<code>@Observable<\/code>, la version devient beaucoup plus l\u00e9g\u00e8re :<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>import Observation\n\n@Observable\nclass UserViewModel {\n    var name: String = \"John\"\n    var age: Int = 30\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">import<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">Observation<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F286C4\">@Observable<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F286C4\">class<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">UserViewModel<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> name: <\/span><span style=\"color: #97E1F1; font-style: italic\">String<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #E7EE98\">John<\/span><span style=\"color: #DEE492\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> age: <\/span><span style=\"color: #97E1F1; font-style: italic\">Int<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #BF9EEE\">30<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>struct UserView: View {\n    @State private var viewModel = UserViewModel()\n\n    var body: some View {\n        VStack {\n            Text(viewModel.name)\n            Text(\"\\(viewModel.age)\")\n        }\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">struct<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">UserView<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">View <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@State<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">private<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> viewModel <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">UserViewModel<\/span><span style=\"color: #F6F6F4\">()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> body: <\/span><span style=\"color: #F286C4\">some<\/span><span style=\"color: #F6F6F4\"> View {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        <\/span><span style=\"color: #97E1F1\">VStack<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #97E1F1\">Text<\/span><span style=\"color: #F6F6F4\">(viewModel.name)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #97E1F1\">Text<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F286C4\">\\(<\/span><span style=\"color: #E7EE98\">viewModel.<\/span><span style=\"color: #F6F6F4\">age<\/span><span style=\"color: #F286C4\">)<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F6F6F4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Le changement le plus notable est la disparition de&nbsp;<code>@Published<\/code>&nbsp;et le remplacement de&nbsp;<code>@StateObject<\/code>&nbsp;par&nbsp;<code>@State<\/code>. Cela peut surprendre au d\u00e9but, mais refl\u00e8te une simplification du mod\u00e8le. La vue devient simplement propri\u00e9taire de son \u00e9tat.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Bien comprendre @State, @Bindable et @Environment<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/04\/comprendre-@State-@Bindable-@Environment.jpeg\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1000\" height=\"730\" src=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/04\/comprendre-@State-@Bindable-@Environment.jpeg\" alt=\"@Observable dans SwiftUI\" class=\"wp-image-3323\" srcset=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/04\/comprendre-@State-@Bindable-@Environment.jpeg 1000w, https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/04\/comprendre-@State-@Bindable-@Environment-744x543.jpeg 744w, https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/04\/comprendre-@State-@Bindable-@Environment-420x307.jpeg 420w, https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/04\/comprendre-@State-@Bindable-@Environment-768x561.jpeg 768w\" sizes=\"(max-width: 1000px) 100vw, 1000px\" \/><\/a><\/figure>\n\n\n\n<p>L\u2019introduction de&nbsp;<code>@Observable<\/code>&nbsp;ne se limite pas \u00e0 un nouveau macro. Elle s\u2019accompagne aussi d\u2019une clarification importante dans la mani\u00e8re d\u2019utiliser les property wrappers dans SwiftUI. L\u00e0 o\u00f9 les anciennes approches pouvaient pr\u00eater \u00e0 confusion, notamment entre&nbsp;<code>@StateObject<\/code>&nbsp;et&nbsp;<code>@ObservedObject<\/code>, le nouveau mod\u00e8le propose une s\u00e9paration beaucoup plus nette des responsabilit\u00e9s.<\/p>\n\n\n\n<p>Pour bien tirer parti de ce syst\u00e8me, il est essentiel de comprendre le r\u00f4le pr\u00e9cis de&nbsp;<code>@State<\/code>,&nbsp;<code>@Bindable<\/code>&nbsp;et&nbsp;<code>@Environment<\/code>, ainsi que la mani\u00e8re dont ils s\u2019articulent entre eux dans une architecture SwiftUI moderne.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-medium-font-size\">@State : la source de v\u00e9rit\u00e9 locale<\/h3>\n\n\n\n<p>Avec&nbsp;<code>@Observable<\/code>,&nbsp;<code>@State<\/code>&nbsp;devient le point d\u2019entr\u00e9e naturel pour instancier et poss\u00e9der un mod\u00e8le au sein d\u2019une vue. L\u00e0 o\u00f9 l\u2019on utilisait auparavant&nbsp;<code>@StateObject<\/code>&nbsp;pour g\u00e9rer le cycle de vie d\u2019un&nbsp;<code>ObservableObject<\/code>, cette distinction dispara\u00eet compl\u00e8tement.<\/p>\n\n\n\n<p>Lorsqu\u2019une vue cr\u00e9e une instance d\u2019un mod\u00e8le observable, elle en devient responsable. SwiftUI se charge alors de conserver cette instance \u00e0 travers les recompositions de la vue, tout en assurant la mise \u00e0 jour de l\u2019interface lorsque les propri\u00e9t\u00e9s utilis\u00e9es changent.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>@State private var viewModel = UserViewModel()<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">@State<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">private<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> viewModel <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">UserViewModel<\/span><span style=\"color: #F6F6F4\">()<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Ce changement simplifie consid\u00e9rablement le mod\u00e8le mental. Il n\u2019y a plus besoin de se demander si un objet doit \u00eatre observ\u00e9 ou simplement stock\u00e9 :&nbsp;<code>@State<\/code>&nbsp;remplit d\u00e9sormais ces deux r\u00f4les de mani\u00e8re transparente.<\/p>\n\n\n\n<p>Dans la pratique, cela signifie que toute donn\u00e9e&nbsp;<strong>cr\u00e9\u00e9e et contr\u00f4l\u00e9e localement par une vue<\/strong>&nbsp;doit passer par&nbsp;<code>@State<\/code>. C\u2019est elle qui devient la&nbsp;<strong>source de v\u00e9rit\u00e9 principale<\/strong>&nbsp;dans ce contexte.<\/p>\n\n\n\n<p>Une erreur fr\u00e9quente lors de la migration consiste \u00e0 continuer d\u2019utiliser&nbsp;<code>@StateObject<\/code>. Avec&nbsp;<code>@Observable<\/code>, cela n\u2019a plus lieu d\u2019\u00eatre et peut m\u00eame introduire des comportements inattendus.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-medium-font-size\">@Bindable : modifier un mod\u00e8le depuis une sous-vue<\/h3>\n\n\n\n<p>Si&nbsp;<code>@State<\/code>&nbsp;permet de poss\u00e9der une donn\u00e9e, il ne suffit pas d\u00e8s que l\u2019on souhaite la modifier depuis une vue enfant. C\u2019est pr\u00e9cis\u00e9ment dans ce cas que&nbsp;<code>@Bindable<\/code>&nbsp;intervient.<\/p>\n\n\n\n<p>Ce wrapper permet d\u2019exposer un mod\u00e8le observable sous forme de binding, ce qui rend possible l\u2019utilisation de la syntaxe&nbsp;<code>$<\/code>&nbsp;sur ses propri\u00e9t\u00e9s. Sans lui, SwiftUI ne peut pas g\u00e9n\u00e9rer automatiquement les bindings n\u00e9cessaires pour des composants interactifs comme&nbsp;<code>TextField<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>struct EditView: View {\n    @Bindable var viewModel: UserViewModel\n\n    var body: some View {\n        TextField(\"Name\", text: $viewModel.name)\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">struct<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">EditView<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">View <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@Bindable<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> viewModel: UserViewModel<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> body: <\/span><span style=\"color: #F286C4\">some<\/span><span style=\"color: #F6F6F4\"> View {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        <\/span><span style=\"color: #97E1F1\">TextField<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #E7EE98\">Name<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F6F6F4\">, <\/span><span style=\"color: #97E1F1\">text<\/span><span style=\"color: #F6F6F4\">: $viewModel.name)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Dans cet exemple, la vue ne poss\u00e8de pas le mod\u00e8le, mais elle a besoin de le modifier.&nbsp;<code>@Bindable<\/code>&nbsp;joue donc un r\u00f4le d\u2019interm\u00e9diaire, en permettant \u00e0 SwiftUI de suivre les mutations tout en conservant une observation fine des propri\u00e9t\u00e9s.<\/p>\n\n\n\n<p>Ce point est particuli\u00e8rement important dans les formulaires ou les \u00e9crans d\u2019\u00e9dition. Sans&nbsp;<code>@Bindable<\/code>, il devient rapidement difficile de maintenir un code propre et coh\u00e9rent.<\/p>\n\n\n\n<p>Un pi\u00e8ge courant consiste \u00e0 passer un mod\u00e8le observable \u00e0 une sous-vue sans utiliser&nbsp;<code>@Bindable<\/code>, puis \u00e0 tenter d\u2019acc\u00e9der \u00e0&nbsp;<code>$viewModel.property<\/code>. Cela ne fonctionnera pas, car SwiftUI ne dispose pas des informations n\u00e9cessaires pour cr\u00e9er le binding.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-medium-font-size\">@Environment : partager un \u00e9tat global<\/h3>\n\n\n\n<p>Enfin,&nbsp;<code>@Environment<\/code>&nbsp;permet de g\u00e9rer les donn\u00e9es qui doivent \u00eatre accessibles \u00e0 plusieurs niveaux de la hi\u00e9rarchie de vues, sans avoir \u00e0 les passer explicitement de parent \u00e0 enfant.<\/p>\n\n\n\n<p>Avec&nbsp;<code>@Observable<\/code>,&nbsp;<code>@EnvironmentObject<\/code>&nbsp;est remplac\u00e9 par une approche plus moderne et plus typ\u00e9e. On injecte directement une instance dans l\u2019environnement, puis on la r\u00e9cup\u00e8re en utilisant son type.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>@main\nstruct MyApp: App {\n    @State private var viewModel = UserViewModel()\n\n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n                .environment(viewModel)\n        }\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">@main<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F286C4\">struct<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">MyApp<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">App <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@State<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">private<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> viewModel <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1\">UserViewModel<\/span><span style=\"color: #F6F6F4\">()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> body: <\/span><span style=\"color: #F286C4\">some<\/span><span style=\"color: #F6F6F4\"> Scene {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        <\/span><span style=\"color: #97E1F1\">WindowGroup<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #97E1F1\">ContentView<\/span><span style=\"color: #F6F6F4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">                .<\/span><span style=\"color: #97E1F1\">environment<\/span><span style=\"color: #F6F6F4\">(viewModel)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Puis dans une vue :<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#f6f6f4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>struct ContentView: View {\n    @Environment(UserViewModel.self) var viewModel\n\n    var body: some View {\n        Text(viewModel.name)\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula-soft\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F286C4\">struct<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">ContentView<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">View <\/span><span style=\"color: #F6F6F4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">@Environment<\/span><span style=\"color: #F6F6F4\">(UserViewModel.<\/span><span style=\"color: #F286C4\">self<\/span><span style=\"color: #F6F6F4\">) <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> viewModel<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">var<\/span><span style=\"color: #F6F6F4\"> body: <\/span><span style=\"color: #F286C4\">some<\/span><span style=\"color: #F6F6F4\"> View {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        <\/span><span style=\"color: #97E1F1\">Text<\/span><span style=\"color: #F6F6F4\">(viewModel.name)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Cette approche pr\u00e9sente plusieurs avantages. Elle rend le code plus explicite, r\u00e9duit les risques d\u2019erreurs li\u00e9s aux types, et s\u2019int\u00e8gre naturellement avec le nouveau syst\u00e8me d\u2019observation fine.<\/p>\n\n\n\n<p>Cependant, il est important de ne pas abuser de&nbsp;<code>@Environment<\/code>. M\u00eame si son utilisation est tr\u00e8s pratique, elle peut introduire un couplage fort entre les vues si elle est utilis\u00e9e de mani\u00e8re excessive. Dans la plupart des cas, elle doit \u00eatre r\u00e9serv\u00e9e aux donn\u00e9es r\u00e9ellement globales, comme une session utilisateur, des param\u00e8tres applicatifs ou un gestionnaire de navigation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Retour d\u2019exp\u00e9rience et pi\u00e8ges fr\u00e9quents<\/h2>\n\n\n\n<p>L\u2019adoption de&nbsp;<code>@Observable<\/code> dans SwiftUI&nbsp;simplifie clairement le code, mais elle demande aussi de changer certains r\u00e9flexes h\u00e9rit\u00e9s de&nbsp;<code>ObservableObject<\/code>. L\u2019erreur la plus fr\u00e9quente consiste \u00e0 migrer sans revoir la mani\u00e8re dont l\u2019\u00e9tat est structur\u00e9. Avec ce nouveau syst\u00e8me, il devient essentiel de bien distinguer&nbsp;<strong>qui poss\u00e8de la donn\u00e9e et qui la modifie<\/strong>, sous peine de perdre en lisibilit\u00e9.<\/p>\n\n\n\n<p>Un autre point d\u2019attention concerne les bindings. Contrairement aux anciennes approches, l\u2019intention doit \u00eatre explicite. Oublier&nbsp;<code>@Bindable<\/code>&nbsp;dans une vue qui modifie un mod\u00e8le est une erreur classique, qui peut rapidement bloquer l\u2019impl\u00e9mentation.<\/p>\n\n\n\n<p>Enfin, m\u00eame si&nbsp;<code>@Environment<\/code>&nbsp;est plus simple \u00e0 utiliser, il doit rester r\u00e9serv\u00e9 aux donn\u00e9es r\u00e9ellement globales. Une utilisation excessive peut introduire un couplage fort entre les vues et complexifier l\u2019architecture.<\/p>\n\n\n\n<p>Avec un peu de pratique, ce nouveau mod\u00e8le devient rapidement naturel. Il permet non seulement de r\u00e9duire le boilerplate, mais aussi de rendre le comportement des vues plus pr\u00e9visible et plus facile \u00e0 raisonner.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Avec&nbsp;<code>@Observable<\/code>, Apple introduit bien plus qu\u2019un simple remplacement de&nbsp;<code>ObservableObject<\/code>. Ce nouveau syst\u00e8me red\u00e9finit la mani\u00e8re dont SwiftUI g\u00e8re l\u2019\u00e9tat, en mettant l\u2019accent sur la pr\u00e9cision, la performance et la simplicit\u00e9.<\/p>\n\n\n\n<p>Une fois les nouveaux concepts assimil\u00e9s, le code devient plus lisible et plus naturel \u00e0 \u00e9crire. Le lien entre les donn\u00e9es et l\u2019interface est plus direct, ce qui facilite la compr\u00e9hension et la maintenance des applications.<\/p>\n\n\n\n<p>Dans la majorit\u00e9 des cas, on peut r\u00e9sumer l\u2019approche de la mani\u00e8re suivante : la vue qui cr\u00e9e la donn\u00e9e utilise&nbsp;<code>@State<\/code>, les vues qui la modifient utilisent&nbsp;<code>@Bindable<\/code>, et les donn\u00e9es globales passent par&nbsp;<code>@Environment<\/code>. Cette r\u00e8gle simple permet d\u2019\u00e9viter la plupart des erreurs et de tirer pleinement parti du nouveau syst\u00e8me.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-medium-font-size\">Ressources utiles pour aller plus loin<\/h3>\n\n\n\n<p>Si vous souhaitez approfondir l\u2019utilisation de&nbsp;<strong>@Observable en SwiftUI<\/strong>&nbsp;et comprendre plus en d\u00e9tail les choix d\u2019Apple, voici quelques ressources officielles particuli\u00e8rement int\u00e9ressantes.<\/p>\n\n\n\n<p>La documentation Apple propose un guide complet pour accompagner la transition depuis\u00a0<code>ObservableObject<\/code>\u00a0vers le nouveau syst\u00e8me bas\u00e9 sur\u00a0<code>@Observable<\/code>\u00a0:<br>\ud83d\udc49\u00a0<a href=\"https:\/\/developer.apple.com\/documentation\/SwiftUI\/Migrating-from-the-observable-object-protocol-to-the-observable-macro\" target=\"_blank\" data-type=\"link\" data-id=\"https:\/\/developer.apple.com\/documentation\/SwiftUI\/Migrating-from-the-observable-object-protocol-to-the-observable-macro\" rel=\"noreferrer noopener\">Migrating from the ObservableObject protocol to the Observable macro<\/a><\/p>\n\n\n\n<p>Pour une vision plus globale et comprendre les intentions derri\u00e8re ce changement, la session de la WWDC 2023 d\u00e9di\u00e9e \u00e0 l\u2019Observation est \u00e9galement tr\u00e8s instructive :<br>\ud83d\udc49&nbsp;<a href=\"https:\/\/developer.apple.com\/videos\/play\/wwdc2023\/10149\/\" target=\"_blank\" data-type=\"link\" data-id=\"https:\/\/developer.apple.com\/videos\/play\/wwdc2023\/10149\/\" rel=\"noreferrer noopener nofollow\">WWDC 2023 \u2013 Discover Observation in SwiftUI<\/a><\/p>\n\n\n\n<p>Ces ressources permettent de compl\u00e9ter les exemples pr\u00e9sent\u00e9s dans cet article et d\u2019aller plus loin dans l\u2019adoption du nouveau mod\u00e8le propos\u00e9 par SwiftUI.<\/p>\n\n\n<style>.kb-row-layout-id3303_713e5f-8a > .kt-row-column-wrap{align-content:center;}:where(.kb-row-layout-id3303_713e5f-8a > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:center;}.kb-row-layout-id3303_713e5f-8a > .kt-row-column-wrap{column-gap:var(--global-kb-gap-lg, 4rem);row-gap:var(--global-kb-gap-lg, 4rem);max-width:800px;margin-left:auto;margin-right:auto;padding-top:var(--global-kb-spacing-xxl, 5rem);padding-bottom:var(--global-kb-spacing-xxl, 5rem);grid-template-columns:minmax(0, 1fr);}.kb-row-layout-id3303_713e5f-8a{background-color:var(--global-palette8, #F7FAFC);background-image:url('https:\/\/www.kangama.com\/wp-content\/uploads\/2021\/12\/me-contacter-scaled.jpg');background-size:cover;background-position:center center;background-attachment:fixed;background-repeat:no-repeat;}.kb-row-layout-id3303_713e5f-8a > .kt-row-layout-overlay{opacity:0.50;background-color:var(--ast-global-color-8);}.kb-row-layout-id3303_713e5f-8a ,.kb-row-layout-id3303_713e5f-8a h1,.kb-row-layout-id3303_713e5f-8a h2,.kb-row-layout-id3303_713e5f-8a h3,.kb-row-layout-id3303_713e5f-8a h4,.kb-row-layout-id3303_713e5f-8a h5,.kb-row-layout-id3303_713e5f-8a h6{color:var(--global-palette3, #1A202C);}.kb-row-layout-id3303_713e5f-8a a{color:var(--global-palette1, #3182CE);}.kb-row-layout-id3303_713e5f-8a a:hover{color:var(--global-palette2, #2B6CB0);}@media all and (max-width: 1024px), only screen and (min-device-width: 1024px) and (max-device-width: 1366px) and (-webkit-min-device-pixel-ratio: 2) and (hover: none){.kb-row-layout-id3303_713e5f-8a{background-attachment:scroll;}}@media all and (max-width: 1024px){.kb-row-layout-id3303_713e5f-8a > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id3303_713e5f-8a > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id3303_713e5f-8a alignnone has-theme-palette8-background-color kt-row-has-bg wp-block-kadence-rowlayout\"><div class=\"kt-row-layout-overlay kt-row-overlay-normal\"><\/div><div class=\"kt-row-column-wrap kt-has-1-columns kt-row-layout-equal kt-tab-layout-inherit kt-mobile-layout-row kt-row-valign-middle\">\n<style>.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{display:flex;}.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col,.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col:before{border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-right-radius:0px;border-bottom-left-radius:0px;}.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{flex-direction:column;justify-content:center;}.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col > .aligncenter{width:100%;}.kt-row-column-wrap > .kadence-column3303_d8f4c1-cc{align-self:center;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3303_d8f4c1-cc{align-self:auto;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{flex-direction:column;justify-content:center;}.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column3303_d8f4c1-cc{text-align:center;}.kadence-column3303_d8f4c1-cc{position:relative;}@media all and (max-width: 1024px){.kt-row-column-wrap > .kadence-column3303_d8f4c1-cc{align-self:center;}}@media all and (max-width: 1024px){.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3303_d8f4c1-cc{align-self:auto;}}@media all and (max-width: 1024px){.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 1024px){.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kt-row-column-wrap > .kadence-column3303_d8f4c1-cc{align-self:center;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3303_d8f4c1-cc{align-self:auto;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{flex-direction:column;justify-content:center;}.kadence-column3303_d8f4c1-cc > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column3303_d8f4c1-cc\"><div class=\"kt-inside-inner-col\"><style>.wp-block-kadence-advancedheading.kt-adv-heading3303_942c1a-e0, .wp-block-kadence-advancedheading.kt-adv-heading3303_942c1a-e0[data-kb-block=\"kb-adv-heading3303_942c1a-e0\"]{margin-top:0px;margin-bottom:var(--global-kb-spacing-lg, 3rem);text-align:center;font-size:var(--global-kb-font-size-lg, 2rem);line-height:1.2em;font-style:normal;}.wp-block-kadence-advancedheading.kt-adv-heading3303_942c1a-e0 mark.kt-highlight, .wp-block-kadence-advancedheading.kt-adv-heading3303_942c1a-e0[data-kb-block=\"kb-adv-heading3303_942c1a-e0\"] mark.kt-highlight{font-style:normal;color:#f76a0c;-webkit-box-decoration-break:clone;box-decoration-break:clone;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.wp-block-kadence-advancedheading.kt-adv-heading3303_942c1a-e0 img.kb-inline-image, .wp-block-kadence-advancedheading.kt-adv-heading3303_942c1a-e0[data-kb-block=\"kb-adv-heading3303_942c1a-e0\"] img.kb-inline-image{width:150px;vertical-align:baseline;}<\/style>\n<h2 class=\"kt-adv-heading3303_942c1a-e0 wp-block-kadence-advancedheading has-ast-global-color-5-color has-text-color\" data-kb-block=\"kb-adv-heading3303_942c1a-e0\"><strong>Vous souhaitez \u00eatre accompagn\u00e9 pour lancer ou faire \u00e9voluer votre application mobile ?<\/strong><\/h2>\n\n\n<style>.wp-block-kadence-advancedbtn.kb-btns3303_4008ac-ce{gap:var(--global-kb-gap-xs, 0.5rem );justify-content:center;align-items:center;}.kt-btns3303_4008ac-ce .kt-button{font-weight:normal;font-style:normal;}.kt-btns3303_4008ac-ce .kt-btn-wrap-0{margin-right:5px;}.wp-block-kadence-advancedbtn.kt-btns3303_4008ac-ce .kt-btn-wrap-0 .kt-button{color:#555555;border-color:#555555;}.wp-block-kadence-advancedbtn.kt-btns3303_4008ac-ce .kt-btn-wrap-0 .kt-button:hover, .wp-block-kadence-advancedbtn.kt-btns3303_4008ac-ce .kt-btn-wrap-0 .kt-button:focus{color:#ffffff;border-color:#444444;}.wp-block-kadence-advancedbtn.kt-btns3303_4008ac-ce .kt-btn-wrap-0 .kt-button::before{display:none;}.wp-block-kadence-advancedbtn.kt-btns3303_4008ac-ce .kt-btn-wrap-0 .kt-button:hover, .wp-block-kadence-advancedbtn.kt-btns3303_4008ac-ce .kt-btn-wrap-0 .kt-button:focus{background:#444444;}<\/style>\n<div class=\"wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns3303_4008ac-ce\"><style>ul.menu .wp-block-kadence-advancedbtn .kb-btn3303_b754e7-f0.kb-button{width:initial;}.wp-block-kadence-advancedbtn .kb-btn3303_b754e7-f0.kb-button{color:var(--ast-global-color-7);background:rgba(0,0,0,0);font-weight:bold;text-transform:uppercase;border-top:2px solid var(--ast-global-color-7);border-right:2px solid var(--ast-global-color-7);border-bottom:2px solid var(--ast-global-color-7);border-left:2px solid var(--ast-global-color-7);}.wp-block-kadence-advancedbtn .kb-btn3303_b754e7-f0.kb-button:hover, .wp-block-kadence-advancedbtn .kb-btn3303_b754e7-f0.kb-button:focus{color:var(--ast-global-color-5);background:var(--ast-global-color-7);}@media all and (max-width: 1024px){.wp-block-kadence-advancedbtn .kb-btn3303_b754e7-f0.kb-button{border-top:2px solid var(--ast-global-color-7);border-right:2px solid var(--ast-global-color-7);border-bottom:2px solid var(--ast-global-color-7);border-left:2px solid var(--ast-global-color-7);}}@media all and (max-width: 767px){.wp-block-kadence-advancedbtn .kb-btn3303_b754e7-f0.kb-button{border-top:2px solid var(--ast-global-color-7);border-right:2px solid var(--ast-global-color-7);border-bottom:2px solid var(--ast-global-color-7);border-left:2px solid var(--ast-global-color-7);}}<\/style><a class=\"kb-button kt-button button kb-btn3303_b754e7-f0 kt-btn-size-standard kt-btn-width-type-auto kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-false  wp-block-button__link wp-block-kadence-singlebtn\" href=\"https:\/\/www.kangama.com\/contact\/\"><span class=\"kt-btn-inner-text\">\u00c9changeons sur votre projet<\/span><\/a><\/div>\n<\/div><\/div>\n\n<\/div><\/div>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Apprenez \u00e0 utiliser @Observable dans SwiftUI (iOS 17) et ma\u00eetrisez @State, @Bindable et @Environment simplement.<\/p>","protected":false},"author":1,"featured_media":3319,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[16],"tags":[21],"class_list":["post-3303","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developpement-logiciel-technique","tag-swiftui"],"_links":{"self":[{"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/posts\/3303","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/comments?post=3303"}],"version-history":[{"count":18,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/posts\/3303\/revisions"}],"predecessor-version":[{"id":3384,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/posts\/3303\/revisions\/3384"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/media\/3319"}],"wp:attachment":[{"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/media?parent=3303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/categories?post=3303"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/tags?post=3303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}