{"id":3521,"date":"2026-05-07T14:13:00","date_gmt":"2026-05-07T13:13:00","guid":{"rendered":"https:\/\/www.kangama.com\/?p=3521"},"modified":"2026-05-07T14:41:03","modified_gmt":"2026-05-07T13:41:03","slug":"smappservice-swiftui-app-macos-demarrage","status":"publish","type":"post","link":"https:\/\/www.kangama.com\/en\/smappservice-swiftui-app-macos-demarrage\/","title":{"rendered":"SMAppService SwiftUI : lancer une application macOS au d\u00e9marrage"},"content":{"rendered":"<div id=\"bsf_rt_marker\"><\/div>\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>Apr\u00e8s avoir travaill\u00e9 sur la <a href=\"https:\/\/www.kangama.com\/menu-bar-macos-swiftui-guide-pratique\/\" data-type=\"link\" data-id=\"https:\/\/www.kangama.com\/menu-bar-macos-swiftui-guide-pratique\/\"><strong>menu bar<\/strong><\/a>, le <strong><a href=\"https:\/\/www.kangama.com\/floating-panel-macos-swiftui-nspanel\/\" data-type=\"link\" data-id=\"https:\/\/www.kangama.com\/floating-panel-macos-swiftui-nspanel\/\">floating panel<\/a><\/strong> et le <strong><a href=\"https:\/\/www.kangama.com\/devcachecleaner-workspace-macos\/\" data-type=\"link\" data-id=\"https:\/\/www.kangama.com\/devcachecleaner-workspace-macos\/\">nettoyage des dossiers<\/a><\/strong> g\u00e9n\u00e9r\u00e9s dans les projets, j\u2019ai ajout\u00e9 une nouvelle am\u00e9lioration \u00e0 <strong><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\/\">DevCacheCleaner<\/a><\/strong> : permettre \u00e0 l\u2019application de se lancer automatiquement au d\u00e9marrage du Mac gr\u00e2ce \u00e0 <strong><a href=\"https:\/\/developer.apple.com\/documentation\/servicemanagement\/smappservice?language=objc\" data-type=\"link\" data-id=\"https:\/\/developer.apple.com\/documentation\/servicemanagement\/smappservice?language=objc\" target=\"_blank\" rel=\"noopener\">SMAppService<\/a> SwiftUI<\/strong>.<\/p>\n\n\n\n<p>Pour un outil comme DevCacheCleaner, ce comportement a du sens. L\u2019application est pens\u00e9e comme un utilitaire discret, disponible depuis la barre de menus macOS, que l\u2019on peut ouvrir rapidement lorsqu\u2019on veut nettoyer les caches d\u00e9veloppeur ou lib\u00e9rer de l\u2019espace. Dans ce contexte, demander \u00e0 l\u2019utilisateur de relancer manuellement l\u2019application apr\u00e8s chaque red\u00e9marrage n\u2019est pas id\u00e9al.<\/p>\n\n\n\n<p>L\u2019objectif \u00e9tait donc simple : ajouter une option claire dans l\u2019interface pour activer ou d\u00e9sactiver le lancement au d\u00e9marrage, sans script externe, sans manipulation manuelle et sans solution d\u00e9tourn\u00e9e. Pour cela, j\u2019ai utilis\u00e9 <code>SMAppService<\/code> l\u2019API propos\u00e9e par Apple pour g\u00e9rer ce type d\u2019int\u00e9gration sur macOS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pourquoi utiliser SMAppService sur macOS ?<\/h2>\n\n\n\n<p>Historiquement, g\u00e9rer le lancement au d\u00e9marrage d\u2019une application macOS pouvait devenir assez p\u00e9nible. Selon les cas, il fallait passer par un helper, manipuler des \u00e9l\u00e9ments de connexion ou mettre en place une logique plus lourde que n\u00e9cessaire.<\/p>\n\n\n\n<p>Avec <code>SMAppService<\/code>, Apple propose une approche plus propre pour enregistrer une application comme \u00e9l\u00e9ment de connexion. Dans le cas de DevCacheCleaner, je n\u2019avais pas besoin d\u2019un agent complexe. Je voulais simplement que l\u2019application principale soit disponible dans la barre de menus apr\u00e8s l\u2019ouverture de session.<\/p>\n\n\n\n<p>C\u2019est pr\u00e9cis\u00e9ment ce que permet <code>SMAppService.mainApp<\/code>. L\u2019application peut s\u2019enregistrer comme \u00e9l\u00e9ment d\u2019ouverture, puis macOS se charge de la lancer automatiquement lors des prochaines connexions de l\u2019utilisateur.<\/p>\n\n\n\n<p>Cette approche est int\u00e9ressante parce qu\u2019elle respecte le fonctionnement attendu de macOS. L\u2019utilisateur garde le contr\u00f4le, l\u2019application appara\u00eet dans les r\u00e9glages du syst\u00e8me, et le comportement reste compr\u00e9hensible.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ajouter une option \u201cLancer au d\u00e9marrage\u201d dans SwiftUI<\/h2>\n\n\n\n<p>La premi\u00e8re \u00e9tape consiste \u00e0 proposer un r\u00e9glage explicite dans l\u2019application. C\u2019est important, car une application ne doit pas d\u00e9cider seule de se lancer automatiquement. Le comportement doit \u00eatre visible, activable et d\u00e9sactivable par l\u2019utilisateur.<\/p>\n\n\n\n<p>Cette option peut prendre la forme d\u2019un simple <code>Toggle<\/code> dans l\u2019\u00e9cran de pr\u00e9f\u00e9rences.<\/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>import SwiftUI\nimport ServiceManagement\n\nstruct SettingsView: View {\n    @State private var launchAtLogin = false\n\n    var body: some View {\n        Form {\n            Toggle(\"Lancer DevCacheCleaner au d\u00e9marrage\", isOn: $launchAtLogin)\n                .onChange(of: launchAtLogin) { _, newValue in\n                    updateLaunchAtLogin(enabled: newValue)\n                }\n        }\n        .onAppear {\n            launchAtLogin = SMAppService.mainApp.status == .enabled\n        }\n    }\n\n    private func updateLaunchAtLogin(enabled: Bool) {\n        do {\n            if enabled {\n                try SMAppService.mainApp.register()\n            } else {\n                try SMAppService.mainApp.unregister()\n            }\n        } catch {\n            print(\"Erreur lors de la mise \u00e0 jour du lancement au d\u00e9marrage: \\(error)\")\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\">import<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">SwiftUI<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F286C4\">import<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">ServiceManagement<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F286C4\">struct<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #97E1F1; font-style: italic\">SettingsView<\/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\"> launchAtLogin <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #BF9EEE\">false<\/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\">Form<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #97E1F1\">Toggle<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #E7EE98\">Lancer DevCacheCleaner au d\u00e9marrage<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #F6F6F4\">, <\/span><span style=\"color: #97E1F1\">isOn<\/span><span style=\"color: #F6F6F4\">: $launchAtLogin)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">                .<\/span><span style=\"color: #97E1F1\">onChange<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #97E1F1\">of<\/span><span style=\"color: #F6F6F4\">: launchAtLogin) { <\/span><span style=\"color: #BF9EEE\">_<\/span><span style=\"color: #F6F6F4\">, newValue <\/span><span style=\"color: #F286C4\">in<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">                    <\/span><span style=\"color: #97E1F1\">updateLaunchAtLogin<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #97E1F1\">enabled<\/span><span style=\"color: #F6F6F4\">: newValue)<\/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 style=\"color: #97E1F1\">onAppear<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            launchAtLogin <\/span><span style=\"color: #F286C4\">=<\/span><span style=\"color: #F6F6F4\"> SMAppService.mainApp.status <\/span><span style=\"color: #F286C4\">==<\/span><span style=\"color: #F6F6F4\"> .enabled<\/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>\n<span class=\"line\"><span style=\"color: #F6F6F4\">    <\/span><span style=\"color: #F286C4\">private<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #F286C4\">func<\/span><span style=\"color: #F6F6F4\"> <\/span><span style=\"color: #62E884\">updateLaunchAtLogin<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #62E884; font-style: italic\">enabled<\/span><span style=\"color: #F6F6F4\">: <\/span><span style=\"color: #97E1F1; font-style: italic\">Bool<\/span><span style=\"color: #F6F6F4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">        <\/span><span style=\"color: #F286C4\">do<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #F286C4\">if<\/span><span style=\"color: #F6F6F4\"> enabled {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">                <\/span><span style=\"color: #F286C4\">try<\/span><span style=\"color: #F6F6F4\"> SMAppService.mainApp.<\/span><span style=\"color: #97E1F1\">register<\/span><span style=\"color: #F6F6F4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            } <\/span><span style=\"color: #F286C4\">else<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">                <\/span><span style=\"color: #F286C4\">try<\/span><span style=\"color: #F6F6F4\"> SMAppService.mainApp.<\/span><span style=\"color: #97E1F1\">unregister<\/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 style=\"color: #F286C4\">catch<\/span><span style=\"color: #F6F6F4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F6F6F4\">            <\/span><span style=\"color: #97E1F1\">print<\/span><span style=\"color: #F6F6F4\">(<\/span><span style=\"color: #DEE492\">&quot;<\/span><span style=\"color: #E7EE98\">Erreur lors de la mise \u00e0 jour du lancement au d\u00e9marrage: <\/span><span style=\"color: #F286C4\">\\(<\/span><span style=\"color: #E7EE98\">error<\/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 principe est assez lisible. Lorsque l\u2019utilisateur active l\u2019option, l\u2019application appelle <code>register()<\/code>. Lorsqu\u2019il la d\u00e9sactive, elle appelle <code>unregister()<\/code>. La logique reste donc simple c\u00f4t\u00e9 SwiftUI, tout en s\u2019int\u00e9grant proprement avec macOS.<\/p>\n\n\n\n<p>Juste apr\u00e8s l\u2019activation, macOS affiche aussi une notification native indiquant qu\u2019un nouvel \u00e9l\u00e9ment de connexion a \u00e9t\u00e9 ajout\u00e9. Dans mon cas, le syst\u00e8me affiche un message pr\u00e9cisant que DevCacheCleaner s\u2019ouvrira automatiquement \u00e0 la connexion et que ce comportement peut \u00eatre g\u00e9r\u00e9 dans \u201cLogin Items &amp; Extensions\u201d.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-thumbnail\" style=\"margin-bottom:var(--wp--preset--spacing--60)\"><a href=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/SMAppService-notification.png\"><img fetchpriority=\"high\" decoding=\"async\" width=\"420\" height=\"158\" src=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/SMAppService-notification-420x158.png\" alt=\"SMAppService SwiftUI\" class=\"wp-image-3528\" srcset=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/SMAppService-notification-420x158.png 420w, https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/SMAppService-notification.png 740w\" sizes=\"(max-width: 420px) 100vw, 420px\" \/><\/a><\/figure>\n\n\n\n<p>Cette notification est un d\u00e9tail important dans l\u2019exp\u00e9rience utilisateur. Elle rend l\u2019action transparente. M\u00eame si l\u2019activation vient bien d\u2019un r\u00e9glage dans DevCacheCleaner, macOS confirme que l\u2019application vient d\u2019\u00eatre ajout\u00e9e aux \u00e9l\u00e9ments d\u2019ouverture. Pour un utilitaire de menu bar, c\u2019est rassurant : le lancement automatique n\u2019est pas cach\u00e9, il est signal\u00e9 clairement par le syst\u00e8me.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ne pas stocker uniquement l\u2019\u00e9tat en local<\/h2>\n\n\n\n<p>Un point important est de ne pas se contenter d\u2019une valeur sauvegard\u00e9e dans <code>UserDefaults<\/code>. L\u2019utilisateur peut aussi modifier les \u00e9l\u00e9ments d\u2019ouverture directement depuis les R\u00e9glages Syst\u00e8me de macOS.<\/p>\n\n\n\n<p>Une fois l\u2019application enregistr\u00e9e avec <code>SMAppService.mainApp.register()<\/code>, DevCacheCleaner appara\u00eet dans : <strong>R\u00e9glages Syst\u00e8me > G\u00e9n\u00e9ral > Ouverture et extensions<\/strong><\/p>\n\n\n\n<p>L\u2019application est alors visible dans la liste des \u00e9l\u00e9ments qui s\u2019ouvrent automatiquement avec la session. L\u2019utilisateur peut donc v\u00e9rifier que DevCacheCleaner est bien enregistr\u00e9, mais aussi le d\u00e9sactiver directement depuis macOS sans repasser par l\u2019application.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\" style=\"margin-bottom:var(--wp--preset--spacing--70)\"><a href=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/Ouverture-extensions-settings.jpg\"><img decoding=\"async\" width=\"921\" height=\"280\" src=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/Ouverture-extensions-settings.jpg\" alt=\"SMAppService SwiftUI\" class=\"wp-image-3529\" srcset=\"https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/Ouverture-extensions-settings.jpg 921w, https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/Ouverture-extensions-settings-744x226.jpg 744w, https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/Ouverture-extensions-settings-420x128.jpg 420w, https:\/\/www.kangama.com\/wp-content\/uploads\/2026\/05\/Ouverture-extensions-settings-768x233.jpg 768w\" sizes=\"(max-width: 921px) 100vw, 921px\" \/><\/a><\/figure>\n\n\n\n<p>C\u2019est pour cette raison que je pr\u00e9f\u00e8re relire l\u2019\u00e9tat r\u00e9el depuis le syst\u00e8me :<\/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>SMAppService.mainApp.status == .enabled<\/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: #F6F6F4\">SMAppService.mainApp.status <\/span><span style=\"color: #F286C4\">==<\/span><span style=\"color: #F6F6F4\"> .enabled<\/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>Cela permet d\u2019afficher une interface coh\u00e9rente avec la configuration r\u00e9elle du Mac. Si l\u2019utilisateur retire DevCacheCleaner des \u00e9l\u00e9ments d\u2019ouverture depuis macOS, l\u2019application peut refl\u00e9ter ce changement au prochain affichage des r\u00e9glages.<\/p>\n\n\n\n<p>C\u2019est un d\u00e9tail, mais c\u2019est exactement le type de d\u00e9tail qui rend une application macOS plus propre et plus professionnelle. L\u2019interface ne montre pas seulement ce que l\u2019application pense avoir enregistr\u00e9. Elle montre ce que macOS consid\u00e8re r\u00e9ellement comme actif.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Une fonctionnalit\u00e9 simple, mais utile pour l\u2019exp\u00e9rience utilisateur<\/h2>\n\n\n\n<p>Sur le papier, le lancement au d\u00e9marrage peut sembler secondaire. Pourtant, pour une application de menu bar, c\u2019est presque une fonctionnalit\u00e9 naturelle.<\/p>\n\n\n\n<p>DevCacheCleaner n\u2019est pas une application que l\u2019on ouvre pour y passer du temps. C\u2019est un outil que l\u2019on veut avoir sous la main au bon moment. Le fait de le retrouver automatiquement dans la barre de menus apr\u00e8s le d\u00e9marrage rend l\u2019exp\u00e9rience plus fluide.<\/p>\n\n\n\n<p>C\u2019est aussi coh\u00e9rent avec l\u2019\u00e9volution de la s\u00e9rie d\u2019articles autour de DevCacheCleaner. Le projet a commenc\u00e9 par une id\u00e9e simple : nettoyer les caches d\u00e9veloppeur sur macOS. Ensuite, il a fallu construire une interface adapt\u00e9e avec SwiftUI, ajouter une menu bar utile, ouvrir une fen\u00eatre avanc\u00e9e avec un floating panel, puis enrichir le nettoyage avec les dossiers g\u00e9n\u00e9r\u00e9s dans les projets.<\/p>\n\n\n\n<p>Avec <code>SMAppService<\/code>, on ajoute une brique suppl\u00e9mentaire : l\u2019int\u00e9gration naturelle au quotidien de l\u2019utilisateur macOS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ce que j\u2019en retiens<\/h2>\n\n\n\n<p>La mise en place de <code>SMAppService<\/code> dans DevCacheCleaner m\u2019a rappel\u00e9 qu\u2019une bonne application macOS ne se limite pas \u00e0 ses fonctionnalit\u00e9s principales. Elle doit aussi respecter les habitudes de la plateforme.<\/p>\n\n\n\n<p>Un utilitaire de menu bar doit \u00eatre discret, rapide \u00e0 ouvrir et pr\u00e9sent quand on en a besoin. Le lancement au d\u00e9marrage va dans ce sens, \u00e0 condition de rester une option claire, activable et d\u00e9sactivable par l\u2019utilisateur.<\/p>\n\n\n\n<p>Techniquement, l\u2019int\u00e9gration reste assez simple avec SwiftUI. Il faut importer <code>ServiceManagement<\/code>, utiliser <code>SMAppService.mainApp<\/code>, appeler <code>register()<\/code> ou <code>unregister()<\/code>, puis synchroniser l\u2019interface avec le statut r\u00e9el du syst\u00e8me.<\/p>\n\n\n\n<p>Cette derni\u00e8re partie est importante, car macOS reste la source de v\u00e9rit\u00e9. Si l\u2019utilisateur modifie l\u2019\u00e9tat de DevCacheCleaner dans les R\u00e9glages Syst\u00e8me, l\u2019application doit pouvoir s\u2019adapter \u00e0 cette configuration.<\/p>\n\n\n\n<p>C\u2019est une petite am\u00e9lioration, mais elle rend DevCacheCleaner plus agr\u00e9able \u00e0 utiliser au quotidien.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Ajouter le lancement au d\u00e9marrage avec <code>SMAppService<\/code> est une \u00e9tape logique pour une application macOS d\u00e9velopp\u00e9e avec SwiftUI, surtout lorsqu\u2019il s\u2019agit d\u2019un utilitaire de menu bar comme DevCacheCleaner.<\/p>\n\n\n\n<p>Cette fonctionnalit\u00e9 am\u00e9liore l\u2019exp\u00e9rience utilisateur sans complexifier l\u2019application. Elle montre aussi l\u2019importance de penser un outil dans son usage r\u00e9el : pas seulement ce qu\u2019il fait, mais la mani\u00e8re dont il s\u2019int\u00e8gre dans le quotidien du d\u00e9veloppeur.<\/p>\n\n\n\n<p>C\u2019est exactement l\u2019approche que je cherche \u00e0 garder avec DevCacheCleaner : construire progressivement un outil simple, utile et bien int\u00e9gr\u00e9 \u00e0 macOS. Si vous d\u00e9veloppez une application macOS avec SwiftUI et que vous voulez am\u00e9liorer son int\u00e9gration au syst\u00e8me, <code>SMAppService<\/code> est une solution propre \u00e0 envisager pour g\u00e9rer le lancement au d\u00e9marrage.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n<style>.wp-block-kadence-advancedheading.kt-adv-heading3521_2ee210-75, .wp-block-kadence-advancedheading.kt-adv-heading3521_2ee210-75[data-kb-block=\"kb-adv-heading3521_2ee210-75\"]{font-size:var(--global-kb-font-size-md, 1.25rem);font-style:normal;}.wp-block-kadence-advancedheading.kt-adv-heading3521_2ee210-75 mark.kt-highlight, .wp-block-kadence-advancedheading.kt-adv-heading3521_2ee210-75[data-kb-block=\"kb-adv-heading3521_2ee210-75\"] 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-heading3521_2ee210-75 img.kb-inline-image, .wp-block-kadence-advancedheading.kt-adv-heading3521_2ee210-75[data-kb-block=\"kb-adv-heading3521_2ee210-75\"] img.kb-inline-image{width:150px;vertical-align:baseline;}<\/style>\n<h4 class=\"kt-adv-heading3521_2ee210-75 wp-block-kadence-advancedheading\" data-kb-block=\"kb-adv-heading3521_2ee210-75\">Article similaire<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li style=\"margin-top:var(--wp--preset--spacing--20);margin-right:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--20);margin-left:var(--wp--preset--spacing--60)\"><a href=\"https:\/\/www.kangama.com\/outil-macos-nettoyer-cache-developpeur\/\" data-type=\"post\" data-id=\"3105\">Cr\u00e9er un outil macOS pour nettoyer les caches d\u00e9veloppeur : retour d\u2019exp\u00e9rience<\/a><\/li>\n\n\n\n<li style=\"margin-right:var(--wp--preset--spacing--60);margin-left:var(--wp--preset--spacing--60)\"><a href=\"https:\/\/www.kangama.com\/menu-bar-macos-swiftui-guide-pratique\/\" data-type=\"link\" data-id=\"https:\/\/www.kangama.com\/menu-bar-macos-swiftui-guide-pratique\/\">Comment cr\u00e9er une menu bar utile sur macOS avec SwiftUI<\/a><\/li>\n\n\n\n<li style=\"margin-right:var(--wp--preset--spacing--60);margin-left:var(--wp--preset--spacing--60)\"><a href=\"https:\/\/www.kangama.com\/devcachecleaner-workspace-macos\/\" data-type=\"link\" data-id=\"https:\/\/www.kangama.com\/menu-bar-macos-swiftui-guide-pratique\/\">DevCacheCleaner 0.2.2-alpha : nettoyer aussi les dossiers g\u00e9n\u00e9r\u00e9s de vos projets<\/a><\/li>\n\n\n\n<li style=\"margin-right:var(--wp--preset--spacing--60);margin-left:var(--wp--preset--spacing--60)\"><a href=\"https:\/\/www.kangama.com\/floating-panel-macos-swiftui-nspanel\/\" data-type=\"link\" data-id=\"https:\/\/www.kangama.com\/menu-bar-macos-swiftui-guide-pratique\/\">Floating Panel sur macOS avec SwiftUI : ouvrir une fen\u00eatre avanc\u00e9e depuis la menu bar<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n<style>.kb-row-layout-id3521_f40687-1f > .kt-row-column-wrap{align-content:center;}:where(.kb-row-layout-id3521_f40687-1f > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:center;}.kb-row-layout-id3521_f40687-1f > .kt-row-column-wrap{column-gap:var(--global-kb-gap-lg, 4rem);row-gap:var(--global-kb-gap-lg, 4rem);max-width:900px;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-id3521_f40687-1f{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-id3521_f40687-1f > .kt-row-layout-overlay{opacity:0.50;background-color:var(--ast-global-color-8);}.kb-row-layout-id3521_f40687-1f ,.kb-row-layout-id3521_f40687-1f h1,.kb-row-layout-id3521_f40687-1f h2,.kb-row-layout-id3521_f40687-1f h3,.kb-row-layout-id3521_f40687-1f h4,.kb-row-layout-id3521_f40687-1f h5,.kb-row-layout-id3521_f40687-1f h6{color:var(--global-palette3, #1A202C);}.kb-row-layout-id3521_f40687-1f a{color:var(--global-palette1, #3182CE);}.kb-row-layout-id3521_f40687-1f 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-id3521_f40687-1f{background-attachment:scroll;}}@media all and (max-width: 1024px){.kb-row-layout-id3521_f40687-1f > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id3521_f40687-1f > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id3521_f40687-1f 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-column3521_96e6ed-7a > .kt-inside-inner-col{display:flex;}.kadence-column3521_96e6ed-7a > .kt-inside-inner-col,.kadence-column3521_96e6ed-7a > .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-column3521_96e6ed-7a > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column3521_96e6ed-7a > .kt-inside-inner-col{flex-direction:column;justify-content:center;}.kadence-column3521_96e6ed-7a > .kt-inside-inner-col > .aligncenter{width:100%;}.kt-row-column-wrap > .kadence-column3521_96e6ed-7a{align-self:center;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3521_96e6ed-7a{align-self:auto;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3521_96e6ed-7a > .kt-inside-inner-col{flex-direction:column;justify-content:center;}.kadence-column3521_96e6ed-7a > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column3521_96e6ed-7a{text-align:center;}.kadence-column3521_96e6ed-7a{position:relative;}@media all and (max-width: 1024px){.kt-row-column-wrap > .kadence-column3521_96e6ed-7a{align-self:center;}}@media all and (max-width: 1024px){.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3521_96e6ed-7a{align-self:auto;}}@media all and (max-width: 1024px){.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3521_96e6ed-7a > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 1024px){.kadence-column3521_96e6ed-7a > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kt-row-column-wrap > .kadence-column3521_96e6ed-7a{align-self:center;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3521_96e6ed-7a{align-self:auto;}.kt-inner-column-height-full:not(.kt-has-1-columns) > .wp-block-kadence-column.kadence-column3521_96e6ed-7a > .kt-inside-inner-col{flex-direction:column;justify-content:center;}.kadence-column3521_96e6ed-7a > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column3521_96e6ed-7a\"><div class=\"kt-inside-inner-col\"><style>.wp-block-kadence-advancedheading.kt-adv-heading3521_fc2c09-eb, .wp-block-kadence-advancedheading.kt-adv-heading3521_fc2c09-eb[data-kb-block=\"kb-adv-heading3521_fc2c09-eb\"]{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-heading3521_fc2c09-eb mark.kt-highlight, .wp-block-kadence-advancedheading.kt-adv-heading3521_fc2c09-eb[data-kb-block=\"kb-adv-heading3521_fc2c09-eb\"] 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-heading3521_fc2c09-eb img.kb-inline-image, .wp-block-kadence-advancedheading.kt-adv-heading3521_fc2c09-eb[data-kb-block=\"kb-adv-heading3521_fc2c09-eb\"] img.kb-inline-image{width:150px;vertical-align:baseline;}<\/style>\n<h2 class=\"kt-adv-heading3521_fc2c09-eb wp-block-kadence-advancedheading has-ast-global-color-5-color has-text-color\" data-kb-block=\"kb-adv-heading3521_fc2c09-eb\"><strong><strong><strong>Besoin d\u2019un regard technique sur votre projet ?<br>Je peux vous accompagner sur vos d\u00e9veloppements.<\/strong><\/strong><\/strong><\/h2>\n\n\n<style>.wp-block-kadence-advancedbtn.kb-btns3521_c3f486-16{gap:var(--global-kb-gap-xs, 0.5rem );justify-content:center;align-items:center;}.kt-btns3521_c3f486-16 .kt-button{font-weight:normal;font-style:normal;}.kt-btns3521_c3f486-16 .kt-btn-wrap-0{margin-right:5px;}.wp-block-kadence-advancedbtn.kt-btns3521_c3f486-16 .kt-btn-wrap-0 .kt-button{color:#555555;border-color:#555555;}.wp-block-kadence-advancedbtn.kt-btns3521_c3f486-16 .kt-btn-wrap-0 .kt-button:hover, .wp-block-kadence-advancedbtn.kt-btns3521_c3f486-16 .kt-btn-wrap-0 .kt-button:focus{color:#ffffff;border-color:#444444;}.wp-block-kadence-advancedbtn.kt-btns3521_c3f486-16 .kt-btn-wrap-0 .kt-button::before{display:none;}.wp-block-kadence-advancedbtn.kt-btns3521_c3f486-16 .kt-btn-wrap-0 .kt-button:hover, .wp-block-kadence-advancedbtn.kt-btns3521_c3f486-16 .kt-btn-wrap-0 .kt-button:focus{background:#444444;}<\/style>\n<div class=\"wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns3521_c3f486-16\"><style>ul.menu .wp-block-kadence-advancedbtn .kb-btn3521_88d626-7b.kb-button{width:initial;}.wp-block-kadence-advancedbtn .kb-btn3521_88d626-7b.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-btn3521_88d626-7b.kb-button:hover, .wp-block-kadence-advancedbtn .kb-btn3521_88d626-7b.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-btn3521_88d626-7b.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-btn3521_88d626-7b.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-btn3521_88d626-7b 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>","protected":false},"excerpt":{"rendered":"<p>Utilisez SMAppService pour lancer une application macOS SwiftUI au d\u00e9marrage, avec l\u2019exemple concret de DevCacheCleaner.<\/p>","protected":false},"author":1,"featured_media":3540,"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":[],"class_list":["post-3521","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developpement-logiciel-technique"],"_links":{"self":[{"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/posts\/3521","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=3521"}],"version-history":[{"count":19,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/posts\/3521\/revisions"}],"predecessor-version":[{"id":3545,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/posts\/3521\/revisions\/3545"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/media\/3540"}],"wp:attachment":[{"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/media?parent=3521"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/categories?post=3521"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kangama.com\/en\/wp-json\/wp\/v2\/tags?post=3521"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}