SPB Git

spb/admin-ka-ios

Public

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 (Aug 20, 2026) Browse files

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
Menu

Theme