SPB Git forge

spb/admin-ka-ios

Public
5commits 1branches 0releases
204.0 KBsize
maindefault branch
1 mo agolast push
Swift 100%

Admin-Ka iOS 1.0.0 — app native SwiftUI (WKWebView) de la console administration-ka.com

- shell SwiftUI natif enveloppant l'app web live (reste synchronisé)
- identité Groupe KA : icône + splash pastille « Ka » orange, fond crème, status bar sombre
- WKWebView : cookie de session persistant (reste connecté), pull-to-refresh, barre de progression, liens externes → Safari, écran hors-ligne + réessayer
- généré via XcodeGen (project.yml), build simulateur OK (BUILD SUCCEEDED)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 mo ago (Aug 20, 2026)

13 changed files +708 −0

added .gitignore +6 −0
@@ -0,0 +1,6 @@
1 +.DS_Store
2 +build/
3 +DerivedData/
4 +*.xcuserstate
5 +xcuserdata/
6 +screenshots/
added AdminKa.xcodeproj/project.pbxproj +331 −0
@@ -0,0 +1,331 @@
1 +// !$*UTF8*$!
2 +{
3 + archiveVersion = 1;
4 + classes = {
5 + };
6 + objectVersion = 77;
7 + objects = {
8 +
9 +/* Begin PBXBuildFile section */
10 + 1CC4EBA58D193E277836B4D5 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2739E070934E8C3D308CD5A /* RootView.swift */; };
11 + 20A558D6B8FC5AE82F0E6427 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EC15E5AB22A402E48B8B802 /* WebView.swift */; };
12 + 487B17229190CA32181C16ED /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1680D228D3E13F84839372FC /* Theme.swift */; };
13 + 4D2A2A6FA8B6C7B1D451F11E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 024D4B21DFAE3435E68D3F6E /* Assets.xcassets */; };
14 + C05A68A8575AA602A84238F4 /* AdminKaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CBC6B9B16C2398EE73269BD /* AdminKaApp.swift */; };
15 +/* End PBXBuildFile section */
16 +
17 +/* Begin PBXFileReference section */
18 + 024D4B21DFAE3435E68D3F6E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
19 + 1680D228D3E13F84839372FC /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
20 + 7CBC6B9B16C2398EE73269BD /* AdminKaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminKaApp.swift; sourceTree = "<group>"; };
21 + 7EC15E5AB22A402E48B8B802 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = "<group>"; };
22 + 89EB727B6E26717290559869 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
23 + A2739E070934E8C3D308CD5A /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
24 + AA1AA8F42A604E07DEDCDE63 /* AdminKa.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = AdminKa.app; sourceTree = BUILT_PRODUCTS_DIR; };
25 +/* End PBXFileReference section */
26 +
27 +/* Begin PBXGroup section */
28 + 90F5E72F6AA48CFED030632C /* Sources */ = {
29 + isa = PBXGroup;
30 + children = (
31 + 7CBC6B9B16C2398EE73269BD /* AdminKaApp.swift */,
32 + 89EB727B6E26717290559869 /* Info.plist */,
33 + A2739E070934E8C3D308CD5A /* RootView.swift */,
34 + 1680D228D3E13F84839372FC /* Theme.swift */,
35 + 7EC15E5AB22A402E48B8B802 /* WebView.swift */,
36 + );
37 + path = Sources;
38 + sourceTree = "<group>";
39 + };
40 + B4F12EC1BBB6B452CD9FE526 = {
41 + isa = PBXGroup;
42 + children = (
43 + DAFED2B2A69DF6505282D01D /* Resources */,
44 + 90F5E72F6AA48CFED030632C /* Sources */,
45 + D36ACF724AF308D7BDA2A549 /* Products */,
46 + );
47 + sourceTree = "<group>";
48 + };
49 + D36ACF724AF308D7BDA2A549 /* Products */ = {
50 + isa = PBXGroup;
51 + children = (
52 + AA1AA8F42A604E07DEDCDE63 /* AdminKa.app */,
53 + );
54 + name = Products;
55 + sourceTree = "<group>";
56 + };
57 + DAFED2B2A69DF6505282D01D /* Resources */ = {
58 + isa = PBXGroup;
59 + children = (
60 + 024D4B21DFAE3435E68D3F6E /* Assets.xcassets */,
61 + );
62 + path = Resources;
63 + sourceTree = "<group>";
64 + };
65 +/* End PBXGroup section */
66 +
67 +/* Begin PBXNativeTarget section */
68 + 8E0C5BDFE3B1E2F7C0127165 /* AdminKa */ = {
69 + isa = PBXNativeTarget;
70 + buildConfigurationList = 583958912936F9495C454662 /* Build configuration list for PBXNativeTarget "AdminKa" */;
71 + buildPhases = (
72 + A2AC93E617ED07410C954474 /* Sources */,
73 + 7771283C46E6761B499F72E7 /* Resources */,
74 + );
75 + buildRules = (
76 + );
77 + dependencies = (
78 + );
79 + name = AdminKa;
80 + packageProductDependencies = (
81 + );
82 + productName = AdminKa;
83 + productReference = AA1AA8F42A604E07DEDCDE63 /* AdminKa.app */;
84 + productType = "com.apple.product-type.application";
85 + };
86 +/* End PBXNativeTarget section */
87 +
88 +/* Begin PBXProject section */
89 + F0E286B46B8531D92B3B311A /* Project object */ = {
90 + isa = PBXProject;
91 + attributes = {
92 + BuildIndependentTargetsInParallel = YES;
93 + LastUpgradeCheck = 1430;
94 + TargetAttributes = {
95 + 8E0C5BDFE3B1E2F7C0127165 = {
96 + DevelopmentTeam = "";
97 + ProvisioningStyle = Automatic;
98 + };
99 + };
100 + };
101 + buildConfigurationList = 717B4BEBFAACA90E6681012D /* Build configuration list for PBXProject "AdminKa" */;
102 + developmentRegion = en;
103 + hasScannedForEncodings = 0;
104 + knownRegions = (
105 + Base,
106 + en,
107 + );
108 + mainGroup = B4F12EC1BBB6B452CD9FE526;
109 + minimizedProjectReferenceProxies = 1;
110 + preferredProjectObjectVersion = 77;
111 + productRefGroup = D36ACF724AF308D7BDA2A549 /* Products */;
112 + projectDirPath = "";
113 + projectRoot = "";
114 + targets = (
115 + 8E0C5BDFE3B1E2F7C0127165 /* AdminKa */,
116 + );
117 + };
118 +/* End PBXProject section */
119 +
120 +/* Begin PBXResourcesBuildPhase section */
121 + 7771283C46E6761B499F72E7 /* Resources */ = {
122 + isa = PBXResourcesBuildPhase;
123 + buildActionMask = 2147483647;
124 + files = (
125 + 4D2A2A6FA8B6C7B1D451F11E /* Assets.xcassets in Resources */,
126 + );
127 + runOnlyForDeploymentPostprocessing = 0;
128 + };
129 +/* End PBXResourcesBuildPhase section */
130 +
131 +/* Begin PBXSourcesBuildPhase section */
132 + A2AC93E617ED07410C954474 /* Sources */ = {
133 + isa = PBXSourcesBuildPhase;
134 + buildActionMask = 2147483647;
135 + files = (
136 + C05A68A8575AA602A84238F4 /* AdminKaApp.swift in Sources */,
137 + 1CC4EBA58D193E277836B4D5 /* RootView.swift in Sources */,
138 + 487B17229190CA32181C16ED /* Theme.swift in Sources */,
139 + 20A558D6B8FC5AE82F0E6427 /* WebView.swift in Sources */,
140 + );
141 + runOnlyForDeploymentPostprocessing = 0;
142 + };
143 +/* End PBXSourcesBuildPhase section */
144 +
145 +/* Begin XCBuildConfiguration section */
146 + 1868F3C33EEB2827677E728B /* Release */ = {
147 + isa = XCBuildConfiguration;
148 + buildSettings = {
149 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
150 + CODE_SIGN_IDENTITY = "iPhone Developer";
151 + ENABLE_USER_SCRIPT_SANDBOXING = YES;
152 + INFOPLIST_FILE = Sources/Info.plist;
153 + LD_RUNPATH_SEARCH_PATHS = (
154 + "$(inherited)",
155 + "@executable_path/Frameworks",
156 + );
157 + PRODUCT_BUNDLE_IDENTIFIER = com.groupeka.adminka;
158 + SDKROOT = iphoneos;
159 + TARGETED_DEVICE_FAMILY = "1,2";
160 + };
161 + name = Release;
162 + };
163 + 3EF528462DA820975843B2FD /* Release */ = {
164 + isa = XCBuildConfiguration;
165 + buildSettings = {
166 + ALWAYS_SEARCH_USER_PATHS = NO;
167 + CLANG_ANALYZER_NONNULL = YES;
168 + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
169 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
170 + CLANG_CXX_LIBRARY = "libc++";
171 + CLANG_ENABLE_MODULES = YES;
172 + CLANG_ENABLE_OBJC_ARC = YES;
173 + CLANG_ENABLE_OBJC_WEAK = YES;
174 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
175 + CLANG_WARN_BOOL_CONVERSION = YES;
176 + CLANG_WARN_COMMA = YES;
177 + CLANG_WARN_CONSTANT_CONVERSION = YES;
178 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
179 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
180 + CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
181 + CLANG_WARN_EMPTY_BODY = YES;
182 + CLANG_WARN_ENUM_CONVERSION = YES;
183 + CLANG_WARN_INFINITE_RECURSION = YES;
184 + CLANG_WARN_INT_CONVERSION = YES;
185 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
186 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
187 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
188 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
189 + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
190 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
191 + CLANG_WARN_STRICT_PROTOTYPES = YES;
192 + CLANG_WARN_SUSPICIOUS_MOVE = YES;
193 + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
194 + CLANG_WARN_UNREACHABLE_CODE = YES;
195 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
196 + CODE_SIGN_STYLE = Automatic;
197 + COPY_PHASE_STRIP = NO;
198 + CURRENT_PROJECT_VERSION = 1;
199 + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
200 + DEVELOPMENT_TEAM = "";
201 + ENABLE_NS_ASSERTIONS = NO;
202 + ENABLE_STRICT_OBJC_MSGSEND = YES;
203 + GCC_C_LANGUAGE_STANDARD = gnu11;
204 + GCC_NO_COMMON_BLOCKS = YES;
205 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
206 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
207 + GCC_WARN_UNDECLARED_SELECTOR = YES;
208 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
209 + GCC_WARN_UNUSED_FUNCTION = YES;
210 + GCC_WARN_UNUSED_VARIABLE = YES;
211 + IPHONEOS_DEPLOYMENT_TARGET = 16.0;
212 + MARKETING_VERSION = 1.0.0;
213 + MTL_ENABLE_DEBUG_INFO = NO;
214 + MTL_FAST_MATH = YES;
215 + PRODUCT_NAME = "$(TARGET_NAME)";
216 + SDKROOT = iphoneos;
217 + SWIFT_COMPILATION_MODE = wholemodule;
218 + SWIFT_OPTIMIZATION_LEVEL = "-O";
219 + SWIFT_VERSION = 5.0;
220 + };
221 + name = Release;
222 + };
223 + 4180D4646B0316B143DEBBB4 /* Debug */ = {
224 + isa = XCBuildConfiguration;
225 + buildSettings = {
226 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
227 + CODE_SIGN_IDENTITY = "iPhone Developer";
228 + ENABLE_USER_SCRIPT_SANDBOXING = YES;
229 + INFOPLIST_FILE = Sources/Info.plist;
230 + LD_RUNPATH_SEARCH_PATHS = (
231 + "$(inherited)",
232 + "@executable_path/Frameworks",
233 + );
234 + PRODUCT_BUNDLE_IDENTIFIER = com.groupeka.adminka;
235 + SDKROOT = iphoneos;
236 + TARGETED_DEVICE_FAMILY = "1,2";
237 + };
238 + name = Debug;
239 + };
240 + D5BA178F1D4A548C72B78A88 /* Debug */ = {
241 + isa = XCBuildConfiguration;
242 + buildSettings = {
243 + ALWAYS_SEARCH_USER_PATHS = NO;
244 + CLANG_ANALYZER_NONNULL = YES;
245 + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
246 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
247 + CLANG_CXX_LIBRARY = "libc++";
248 + CLANG_ENABLE_MODULES = YES;
249 + CLANG_ENABLE_OBJC_ARC = YES;
250 + CLANG_ENABLE_OBJC_WEAK = YES;
251 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
252 + CLANG_WARN_BOOL_CONVERSION = YES;
253 + CLANG_WARN_COMMA = YES;
254 + CLANG_WARN_CONSTANT_CONVERSION = YES;
255 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
256 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
257 + CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
258 + CLANG_WARN_EMPTY_BODY = YES;
259 + CLANG_WARN_ENUM_CONVERSION = YES;
260 + CLANG_WARN_INFINITE_RECURSION = YES;
261 + CLANG_WARN_INT_CONVERSION = YES;
262 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
264 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
265 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
266 + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
267 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
268 + CLANG_WARN_STRICT_PROTOTYPES = YES;
269 + CLANG_WARN_SUSPICIOUS_MOVE = YES;
270 + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
271 + CLANG_WARN_UNREACHABLE_CODE = YES;
272 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
273 + CODE_SIGN_STYLE = Automatic;
274 + COPY_PHASE_STRIP = NO;
275 + CURRENT_PROJECT_VERSION = 1;
276 + DEBUG_INFORMATION_FORMAT = dwarf;
277 + DEVELOPMENT_TEAM = "";
278 + ENABLE_STRICT_OBJC_MSGSEND = YES;
279 + ENABLE_TESTABILITY = YES;
280 + GCC_C_LANGUAGE_STANDARD = gnu11;
281 + GCC_DYNAMIC_NO_PIC = NO;
282 + GCC_NO_COMMON_BLOCKS = YES;
283 + GCC_OPTIMIZATION_LEVEL = 0;
284 + GCC_PREPROCESSOR_DEFINITIONS = (
285 + "$(inherited)",
286 + "DEBUG=1",
287 + );
288 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
289 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
290 + GCC_WARN_UNDECLARED_SELECTOR = YES;
291 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
292 + GCC_WARN_UNUSED_FUNCTION = YES;
293 + GCC_WARN_UNUSED_VARIABLE = YES;
294 + IPHONEOS_DEPLOYMENT_TARGET = 16.0;
295 + MARKETING_VERSION = 1.0.0;
296 + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
297 + MTL_FAST_MATH = YES;
298 + ONLY_ACTIVE_ARCH = YES;
299 + PRODUCT_NAME = "$(TARGET_NAME)";
300 + SDKROOT = iphoneos;
301 + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
302 + SWIFT_OPTIMIZATION_LEVEL = "-Onone";
303 + SWIFT_VERSION = 5.0;
304 + };
305 + name = Debug;
306 + };
307 +/* End XCBuildConfiguration section */
308 +
309 +/* Begin XCConfigurationList section */
310 + 583958912936F9495C454662 /* Build configuration list for PBXNativeTarget "AdminKa" */ = {
311 + isa = XCConfigurationList;
312 + buildConfigurations = (
313 + 4180D4646B0316B143DEBBB4 /* Debug */,
314 + 1868F3C33EEB2827677E728B /* Release */,
315 + );
316 + defaultConfigurationIsVisible = 0;
317 + defaultConfigurationName = Debug;
318 + };
319 + 717B4BEBFAACA90E6681012D /* Build configuration list for PBXProject "AdminKa" */ = {
320 + isa = XCConfigurationList;
321 + buildConfigurations = (
322 + D5BA178F1D4A548C72B78A88 /* Debug */,
323 + 3EF528462DA820975843B2FD /* Release */,
324 + );
325 + defaultConfigurationIsVisible = 0;
326 + defaultConfigurationName = Debug;
327 + };
328 +/* End XCConfigurationList section */
329 + };
330 + rootObject = F0E286B46B8531D92B3B311A /* Project object */;
331 +}
added AdminKa.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 −0
@@ -0,0 +1,7 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<Workspace
3 + version = "1.0">
4 + <FileRef
5 + location = "self:">
6 + </FileRef>
7 +</Workspace>
added Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +6 −0
@@ -0,0 +1,6 @@
1 +{
2 + "images" : [
3 + { "filename" : "icon-1024.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" }
4 + ],
5 + "info" : { "author" : "xcode", "version" : 1 }
6 +}
added Resources/Assets.xcassets/AppIcon.appiconset/icon-1024.png +0 −0

Binary file not shown.

added Resources/Assets.xcassets/Contents.json +1 −0
@@ -0,0 +1 @@
1 +{ "info" : { "author" : "xcode", "version" : 1 } }
added Resources/Assets.xcassets/LaunchBackground.colorset/Contents.json +6 −0
@@ -0,0 +1,6 @@
1 +{
2 + "colors" : [
3 + { "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0.925", "green" : "0.945", "red" : "0.949" } }, "idiom" : "universal" }
4 + ],
5 + "info" : { "author" : "xcode", "version" : 1 }
6 +}
added Sources/AdminKaApp.swift +12 −0
@@ -0,0 +1,12 @@
1 +import SwiftUI
2 +
3 +@main
4 +struct AdminKaApp: App {
5 + var body: some Scene {
6 + WindowGroup {
7 + RootView()
8 + .preferredColorScheme(.light)
9 + .statusBarHidden(false)
10 + }
11 + }
12 +}
added Sources/Info.plist +49 −0
@@ -0,0 +1,49 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 +<plist version="1.0">
4 +<dict>
5 + <key>CFBundleDevelopmentRegion</key>
6 + <string>$(DEVELOPMENT_LANGUAGE)</string>
7 + <key>CFBundleDisplayName</key>
8 + <string>Admin-Ka</string>
9 + <key>CFBundleExecutable</key>
10 + <string>$(EXECUTABLE_NAME)</string>
11 + <key>CFBundleIdentifier</key>
12 + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
13 + <key>CFBundleInfoDictionaryVersion</key>
14 + <string>6.0</string>
15 + <key>CFBundleName</key>
16 + <string>$(PRODUCT_NAME)</string>
17 + <key>CFBundlePackageType</key>
18 + <string>APPL</string>
19 + <key>CFBundleShortVersionString</key>
20 + <string>1.0.0</string>
21 + <key>CFBundleVersion</key>
22 + <string>1</string>
23 + <key>NSAppTransportSecurity</key>
24 + <dict>
25 + <key>NSAllowsArbitraryLoads</key>
26 + <false/>
27 + </dict>
28 + <key>UILaunchScreen</key>
29 + <dict>
30 + <key>UIColorName</key>
31 + <string>LaunchBackground</string>
32 + </dict>
33 + <key>UIStatusBarStyle</key>
34 + <string>UIStatusBarStyleDarkContent</string>
35 + <key>UISupportedInterfaceOrientations</key>
36 + <array>
37 + <string>UIInterfaceOrientationPortrait</string>
38 + </array>
39 + <key>UISupportedInterfaceOrientations~ipad</key>
40 + <array>
41 + <string>UIInterfaceOrientationPortrait</string>
42 + <string>UIInterfaceOrientationPortraitUpsideDown</string>
43 + <string>UIInterfaceOrientationLandscapeLeft</string>
44 + <string>UIInterfaceOrientationLandscapeRight</string>
45 + </array>
46 + <key>UIViewControllerBasedStatusBarAppearance</key>
47 + <false/>
48 +</dict>
49 +</plist>
added Sources/RootView.swift +90 −0
@@ -0,0 +1,90 @@
1 +import SwiftUI
2 +
3 +struct RootView: View {
4 + @StateObject private var model = WebModel()
5 + @State private var showSplash = true
6 +
7 + var body: some View {
8 + ZStack {
9 + Ka.bg.ignoresSafeArea()
10 +
11 + WebView(model: model)
12 + .ignoresSafeArea(.container, edges: .bottom)
13 + .opacity(model.loaded ? 1 : 0)
14 +
15 + // barre de progression fine en haut pendant le chargement
16 + if !model.loaded && !model.failed {
17 + VStack(spacing: 0) {
18 + GeometryReader { geo in
19 + Rectangle()
20 + .fill(Ka.orange)
21 + .frame(width: geo.size.width * max(0.04, model.progress), height: 3)
22 + .animation(.easeOut(duration: 0.25), value: model.progress)
23 + }
24 + .frame(height: 3)
25 + Spacer()
26 + }
27 + }
28 +
29 + if showSplash { splash.transition(.opacity) }
30 + if model.failed { offline }
31 + }
32 + .onChange(of: model.loaded) { loaded in
33 + if loaded {
34 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
35 + withAnimation(.easeInOut(duration: 0.4)) { showSplash = false }
36 + }
37 + }
38 + }
39 + }
40 +
41 + private var splash: some View {
42 + ZStack {
43 + Ka.bg.ignoresSafeArea()
44 + VStack(spacing: 18) {
45 + KaMark(size: 92)
46 + Text("Admin-Ka")
47 + .font(.system(size: 26, weight: .heavy, design: .rounded))
48 + .foregroundColor(Ka.ink)
49 + Text("Console Claude Code · Groupe KA")
50 + .font(.system(size: 13, weight: .semibold, design: .rounded))
51 + .foregroundColor(Ka.muted)
52 + if !model.failed {
53 + ProgressView().tint(Ka.orange).padding(.top, 6)
54 + }
55 + }
56 + }
57 + }
58 +
59 + private var offline: some View {
60 + ZStack {
61 + Ka.bg.ignoresSafeArea()
62 + VStack(spacing: 16) {
63 + KaMark(size: 72)
64 + Text("Hors ligne")
65 + .font(.system(size: 22, weight: .heavy, design: .rounded))
66 + .foregroundColor(Ka.ink)
67 + Text("Impossible de joindre administration-ka.com")
68 + .font(.system(size: 14, weight: .medium, design: .rounded))
69 + .foregroundColor(Ka.muted)
70 + .multilineTextAlignment(.center)
71 + .padding(.horizontal, 40)
72 + Button {
73 + showSplash = true
74 + model.reload()
75 + } label: {
76 + Text("Réessayer")
77 + .font(.system(size: 16, weight: .heavy, design: .rounded))
78 + .foregroundColor(Ka.ink)
79 + .padding(.horizontal, 26).padding(.vertical, 13)
80 + .background(
81 + Capsule().fill(Ka.orange)
82 + .overlay(Capsule().strokeBorder(Ka.ink, lineWidth: 2))
83 + )
84 + .shadow(color: Ka.ink, radius: 0, x: 3, y: 3)
85 + }
86 + .padding(.top, 6)
87 + }
88 + }
89 + }
90 +}
added Sources/Theme.swift +44 −0
@@ -0,0 +1,44 @@
1 +import SwiftUI
2 +
3 +// Identité Groupe KA (mêmes valeurs que l'app web)
4 +enum Ka {
5 + static let bg = Color(hex: 0xF2F1EC)
6 + static let card = Color(hex: 0xFCFBF8)
7 + static let ink = Color(hex: 0x1A1611)
8 + static let orange = Color(hex: 0xF97316)
9 + static let muted = Color(hex: 0x6D675C)
10 +
11 + static let url = URL(string: "https://www.administration-ka.com")!
12 +}
13 +
14 +extension Color {
15 + init(hex: UInt, alpha: Double = 1) {
16 + self.init(
17 + .sRGB,
18 + red: Double((hex >> 16) & 0xff) / 255,
19 + green: Double((hex >> 8) & 0xff) / 255,
20 + blue: Double(hex & 0xff) / 255,
21 + opacity: alpha
22 + )
23 + }
24 +}
25 +
26 +// Pastille « Ka » réutilisable (splash + états)
27 +struct KaMark: View {
28 + var size: CGFloat = 84
29 + var body: some View {
30 + RoundedRectangle(cornerRadius: size * 0.28, style: .continuous)
31 + .fill(Ka.orange)
32 + .overlay(
33 + RoundedRectangle(cornerRadius: size * 0.28, style: .continuous)
34 + .strokeBorder(Ka.ink, lineWidth: max(2, size * 0.035))
35 + )
36 + .overlay(
37 + Text("Ka")
38 + .font(.system(size: size * 0.42, weight: .heavy, design: .rounded))
39 + .foregroundColor(Ka.ink)
40 + )
41 + .frame(width: size, height: size)
42 + .shadow(color: Ka.ink.opacity(0.18), radius: 0, x: size * 0.05, y: size * 0.05)
43 + }
44 +}
added Sources/WebView.swift +111 −0
@@ -0,0 +1,111 @@
1 +import SwiftUI
2 +import WebKit
3 +import UIKit
4 +
5 +// État partagé entre le WKWebView et l'interface SwiftUI
6 +final class WebModel: ObservableObject {
7 + @Published var progress: Double = 0
8 + @Published var loaded = false
9 + @Published var failed = false
10 + var reloadRequested = false
11 + var webView: WKWebView?
12 +
13 + func reload() {
14 + failed = false
15 + loaded = false
16 + progress = 0
17 + if let wv = webView {
18 + if wv.url == nil { wv.load(URLRequest(url: Ka.url)) } else { wv.reload() }
19 + }
20 + }
21 +}
22 +
23 +struct WebView: UIViewRepresentable {
24 + @ObservedObject var model: WebModel
25 +
26 + func makeCoordinator() -> Coordinator { Coordinator(model: model) }
27 +
28 + func makeUIView(context: Context) -> WKWebView {
29 + let cfg = WKWebViewConfiguration()
30 + cfg.websiteDataStore = .default() // persiste le cookie de session (reste connecté)
31 + cfg.allowsInlineMediaPlayback = true
32 + cfg.defaultWebpagePreferences.allowsContentJavaScript = true
33 +
34 + let wv = WKWebView(frame: .zero, configuration: cfg)
35 + wv.navigationDelegate = context.coordinator
36 + wv.uiDelegate = context.coordinator
37 + wv.allowsBackForwardNavigationGestures = false
38 + wv.scrollView.bounces = true
39 + wv.scrollView.contentInsetAdjustmentBehavior = .never
40 + wv.isOpaque = false
41 + wv.backgroundColor = UIColor(Ka.bg)
42 + wv.scrollView.backgroundColor = UIColor(Ka.bg)
43 + wv.customUserAgent = "AdminKa-iOS/1.0 (Groupe KA console)"
44 +
45 + // pull-to-refresh natif
46 + let rc = UIRefreshControl()
47 + rc.tintColor = UIColor(Ka.orange)
48 + rc.addTarget(context.coordinator, action: #selector(Coordinator.onRefresh(_:)), for: .valueChanged)
49 + wv.scrollView.refreshControl = rc
50 +
51 + context.coordinator.observe(wv)
52 + model.webView = wv
53 + wv.load(URLRequest(url: Ka.url))
54 + return wv
55 + }
56 +
57 + func updateUIView(_ uiView: WKWebView, context: Context) {}
58 +
59 + final class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {
60 + let model: WebModel
61 + private var obs: NSKeyValueObservation?
62 + init(model: WebModel) { self.model = model }
63 +
64 + func observe(_ wv: WKWebView) {
65 + obs = wv.observe(\.estimatedProgress, options: [.new]) { [weak self] wv, _ in
66 + DispatchQueue.main.async { self?.model.progress = wv.estimatedProgress }
67 + }
68 + }
69 +
70 + @objc func onRefresh(_ sender: UIRefreshControl) {
71 + model.webView?.reload()
72 + }
73 +
74 + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
75 + webView.scrollView.refreshControl?.endRefreshing()
76 + DispatchQueue.main.async {
77 + withAnimation(.easeOut(duration: 0.35)) { self.model.loaded = true }
78 + self.model.failed = false
79 + }
80 + }
81 +
82 + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { fail(error) }
83 + func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { fail(error) }
84 + private func fail(_ error: Error) {
85 + let ns = error as NSError
86 + if ns.code == NSURLErrorCancelled { return }
87 + DispatchQueue.main.async {
88 + self.model.webView?.scrollView.refreshControl?.endRefreshing()
89 + self.model.failed = true
90 + }
91 + }
92 +
93 + // liens externes (target=_blank / http externes) → Safari
94 + func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration,
95 + for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
96 + if let url = navigationAction.request.url { UIApplication.shared.open(url) }
97 + return nil
98 + }
99 + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
100 + decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
101 + if let url = navigationAction.request.url, let host = url.host,
102 + navigationAction.navigationType == .linkActivated,
103 + host != Ka.url.host {
104 + UIApplication.shared.open(url)
105 + decisionHandler(.cancel)
106 + return
107 + }
108 + decisionHandler(.allow)
109 + }
110 + }
111 +}
added project.yml +45 −0
@@ -0,0 +1,45 @@
1 +name: AdminKa
2 +options:
3 + bundleIdPrefix: com.groupeka
4 + deploymentTarget:
5 + iOS: "16.0"
6 + createIntermediateGroups: true
7 +settings:
8 + base:
9 + MARKETING_VERSION: "1.0.0"
10 + CURRENT_PROJECT_VERSION: "1"
11 + DEVELOPMENT_TEAM: ""
12 + CODE_SIGN_STYLE: Automatic
13 + SWIFT_VERSION: "5.0"
14 +targets:
15 + AdminKa:
16 + type: application
17 + platform: iOS
18 + sources:
19 + - Sources
20 + - Resources
21 + info:
22 + path: Sources/Info.plist
23 + properties:
24 + CFBundleDisplayName: Admin-Ka
25 + CFBundleShortVersionString: "1.0.0"
26 + CFBundleVersion: "1"
27 + UILaunchScreen:
28 + UIColorName: LaunchBackground
29 + UISupportedInterfaceOrientations:
30 + - UIInterfaceOrientationPortrait
31 + UISupportedInterfaceOrientations~ipad:
32 + - UIInterfaceOrientationPortrait
33 + - UIInterfaceOrientationPortraitUpsideDown
34 + - UIInterfaceOrientationLandscapeLeft
35 + - UIInterfaceOrientationLandscapeRight
36 + UIStatusBarStyle: UIStatusBarStyleDarkContent
37 + UIViewControllerBasedStatusBarAppearance: false
38 + NSAppTransportSecurity:
39 + NSAllowsArbitraryLoads: false
40 + settings:
41 + base:
42 + PRODUCT_BUNDLE_IDENTIFIER: com.groupeka.adminka
43 + ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
44 + TARGETED_DEVICE_FAMILY: "1,2"
45 + ENABLE_USER_SCRIPT_SANDBOXING: "YES"
46