SPB Git forge

spb/ka-ios

Public
20commits 1branches 0releases
17.2 MBsize
maindefault branch
28 days agolast push
Swift 100%

v1.0.0 (3) — icône SVG signature (encre grainée, KA lime ombre décalée, 12 pastilles d univers), connexion KA ID NATIVE (ASWebAuthenticationSession → hub client ka-ios → échange vérifié api-ka, carte de membre), contenus enrichis (photos + descriptions dans listes et fiches), corrections plist (orientations, iPhone seul, chiffrement exempt) ; builds 2 et 3 téléversées sur TestFlight

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

11 changed files +395 −25

modified KA/Core/Ecosystem.swift +12 −1
@@ -60,6 +60,8 @@ struct KAItem: Identifiable, Hashable, Codable {
60 60 var imageURL: URL?
61 61 var latitude: Double?
62 62 var longitude: Double?
63 + /// Description longue (fiche)
64 + var detail: String?
63 65 /// Paires libres affichées sur la fiche (« Année : 2021 », « Salaire : 65 000 $ »…)
64 66 var facts: [Fact]
65 67
@@ -111,6 +113,14 @@ enum Ecosystem {
111 113 private static func base(_ o: [String: JSONValue], universe: String, idKeys: [String] = ["uid"]) -> KAItem {
112 114 var id: String?
113 115 for k in idKeys { if let v = o[k]?.text { id = v; break } }
116 + // première image utilisable : champ `images` (liste) ou `image`
117 + var image: URL?
118 + if let arr = o["images"]?.array {
119 + image = arr.compactMap { $0.text }.first(where: { $0.hasPrefix("http") }).flatMap(URL.init(string:))
120 + }
121 + if image == nil {
122 + image = o.str("image", "image_url", "photo", "thumbnail").flatMap(URL.init(string:))
123 + }
114 124 return KAItem(
115 125 id: "\(universe):\(id ?? UUID().uuidString)",
116 126 universeID: universe,
@@ -118,9 +128,10 @@ enum Ecosystem {
118 128 subtitle: nil, priceLabel: nil,
119 129 city: o.str("city"),
120 130 url: o.str("url").flatMap(URL.init(string:)),
121 imageURL: o.str("image", "image_url", "photo", "thumbnail").flatMap(URL.init(string:)),
131 + imageURL: image,
122 132 latitude: o.num("lat", "latitude"),
123 133 longitude: o.num("lng", "lon", "longitude"),
134 + detail: o.str("description", "menu_summary", "bio").map { $0.strippingHTML.trimmingCharacters(in: .whitespacesAndNewlines) },
124 135 facts: []
125 136 )
126 137 }
added KA/Core/KAID.swift +167 −0
@@ -0,0 +1,167 @@
1 +// Auteur : Simon-Pierre Boucher — contact@spboucher.ai
2 +// KAID.swift — connexion KA ID NATIVE : ASWebAuthenticationSession vers le hub
3 +// groupe-ka.com (SSO officiel de l'écosystème, client « ka-ios »), retour par
4 +// le scheme ka-ios://auth, puis ÉCHANGE VÉRIFIÉ côté serveur (api-ka valide la
5 +// signature du jeton — aucun secret dans l'app) qui renvoie le profil.
6 +import AuthenticationServices
7 +import SwiftUI
8 +
9 +struct KAIDProfile: Codable, Equatable {
10 + var kaID: String
11 + var name: String?
12 + var email: String?
13 + var picture: String?
14 + var roleLabel: String?
15 + var city: String?
16 + var bio: String?
17 +}
18 +
19 +@MainActor
20 +final class KAIDManager: NSObject, ObservableObject, ASWebAuthenticationPresentationContextProviding {
21 + static let shared = KAIDManager()
22 + @Published var profile: KAIDProfile?
23 + @Published var busy = false
24 + @Published var lastError: String?
25 +
26 + private let storeKey = "ka.id.profile"
27 + private var session: ASWebAuthenticationSession?
28 +
29 + override init() {
30 + super.init()
31 + if let data = UserDefaults.standard.data(forKey: storeKey),
32 + let p = try? JSONDecoder().decode(KAIDProfile.self, from: data) {
33 + profile = p
34 + }
35 + }
36 +
37 + func login() {
38 + guard !busy else { return }
39 + busy = true
40 + lastError = nil
41 + let state = UUID().uuidString
42 + var comps = URLComponents(string: "https://www.groupe-ka.com/sso/authorize")!
43 + comps.queryItems = [
44 + .init(name: "client_id", value: "ka-ios"),
45 + .init(name: "redirect_uri", value: "ka-ios://auth/callback"),
46 + .init(name: "state", value: state),
47 + ]
48 + let s = ASWebAuthenticationSession(url: comps.url!, callbackURLScheme: "ka-ios") { [weak self] url, error in
49 + Task { @MainActor in
50 + guard let self else { return }
51 + defer { self.busy = false }
52 + guard error == nil, let url else {
53 + if let e = error as? ASWebAuthenticationSessionError, e.code == .canceledLogin { return }
54 + self.lastError = "Connexion annulée ou impossible."
55 + return
56 + }
57 + let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems ?? []
58 + guard items.first(where: { $0.name == "state" })?.value == state,
59 + let token = items.first(where: { $0.name == "ka_token" })?.value else {
60 + self.lastError = "Réponse du hub invalide."
61 + return
62 + }
63 + await self.exchange(token)
64 + }
65 + }
66 + s.presentationContextProvider = self
67 + s.prefersEphemeralWebBrowserSession = false // garde la session hub (Google/courriel)
68 + session = s
69 + s.start()
70 + }
71 +
72 + private func exchange(_ token: String) async {
73 + do {
74 + var req = URLRequest(url: URL(string: "https://www.api-ka.com/api/ios/auth/exchange")!)
75 + req.httpMethod = "POST"
76 + req.setValue("application/json", forHTTPHeaderField: "Content-Type")
77 + req.httpBody = try JSONSerialization.data(withJSONObject: ["ka_token": token])
78 + let (data, resp) = try await URLSession.shared.data(for: req)
79 + guard (resp as? HTTPURLResponse)?.statusCode == 200,
80 + let obj = try JSONSerialization.jsonObject(with: data) as? [String: Any],
81 + let kaID = obj["ka_id"] as? String else {
82 + lastError = "Vérification du compte impossible."
83 + return
84 + }
85 + let hub = obj["profile"] as? [String: Any]
86 + let p = KAIDProfile(
87 + kaID: kaID,
88 + name: obj["name"] as? String ?? hub?["name"] as? String,
89 + email: obj["email"] as? String,
90 + picture: obj["picture"] as? String ?? hub?["picture"] as? String,
91 + roleLabel: hub?["role_label"] as? String,
92 + city: hub?["city"] as? String,
93 + bio: hub?["bio"] as? String
94 + )
95 + profile = p
96 + if let d = try? JSONEncoder().encode(p) {
97 + UserDefaults.standard.set(d, forKey: storeKey)
98 + }
99 + Haptics.success()
100 + } catch {
101 + lastError = "Réseau indisponible — réessayez."
102 + }
103 + }
104 +
105 + func logout() {
106 + profile = nil
107 + UserDefaults.standard.removeObject(forKey: storeKey)
108 + Haptics.tap()
109 + }
110 +
111 + nonisolated func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
112 + MainActor.assumeIsolated {
113 + UIApplication.shared.connectedScenes
114 + .compactMap { ($0 as? UIWindowScene)?.keyWindow }
115 + .first ?? ASPresentationAnchor()
116 + }
117 + }
118 +}
119 +
120 +// MARK: - Carte de membre Groupe KA
121 +
122 +struct KAIDCard: View {
123 + let profile: KAIDProfile
124 + var body: some View {
125 + VStack(alignment: .leading, spacing: 10) {
126 + HStack {
127 + Text("Groupe").font(.system(.subheadline, design: .rounded).weight(.bold))
128 + .foregroundStyle(Color(hex: "#f5f3ee"))
129 + Text("KA").font(.system(.caption, design: .rounded).weight(.bold))
130 + .foregroundStyle(KATheme.lime)
131 + .padding(.horizontal, 6).padding(.vertical, 2)
132 + .background(Color(hex: "#f5f3ee").opacity(0.14), in: RoundedRectangle(cornerRadius: 6))
133 + Spacer()
134 + Text("MEMBRE")
135 + .font(.system(size: 9, design: .monospaced).weight(.bold))
136 + .foregroundStyle(KATheme.lime)
137 + }
138 + HStack(spacing: 12) {
139 + AsyncImage(url: profile.picture.flatMap(URL.init(string:))) { phase in
140 + if case .success(let img) = phase { img.resizable() }
141 + else { KATheme.lime.opacity(0.25).overlay(
142 + Text(String(profile.name?.prefix(1) ?? "K")).font(.title2.weight(.bold)).foregroundStyle(KATheme.lime)) }
143 + }
144 + .frame(width: 54, height: 54)
145 + .clipShape(Circle())
146 + .overlay(Circle().strokeBorder(KATheme.lime, lineWidth: 1.6))
147 + VStack(alignment: .leading, spacing: 2) {
148 + Text(profile.name ?? "Membre KA")
149 + .font(.headline).foregroundStyle(Color(hex: "#f5f3ee"))
150 + Text(profile.kaID)
151 + .font(.system(.caption, design: .monospaced).weight(.bold))
152 + .foregroundStyle(KATheme.lime)
153 + if let r = profile.roleLabel ?? profile.city {
154 + Text(r).font(.caption2).foregroundStyle(Color(hex: "#f5f3ee").opacity(0.6))
155 + }
156 + }
157 + Spacer()
158 + }
159 + Text("Un seul compte pour les 13 plateformes de l'écosystème.")
160 + .font(.caption2).foregroundStyle(Color(hex: "#f5f3ee").opacity(0.55))
161 + }
162 + .padding(16)
163 + .background(KATheme.inkLight, in: RoundedRectangle(cornerRadius: 16, style: .continuous))
164 + .overlay(RoundedRectangle(cornerRadius: 16, style: .continuous)
165 + .strokeBorder(KATheme.lime.opacity(0.5), lineWidth: 1.2))
166 + }
167 +}
modified KA/Design/Theme.swift +22 −4
@@ -128,10 +128,28 @@ struct KAItemRow: View {
128 128
129 129 var body: some View {
130 130 HStack(alignment: .top, spacing: 12) {
131 RoundedRectangle(cornerRadius: 8, style: .continuous)
132 .fill(universe?.accent.opacity(0.9) ?? .gray)
133 .frame(width: 5)
134 .padding(.vertical, 2)
131 + if let img = item.imageURL {
132 + AsyncImage(url: img) { phase in
133 + switch phase {
134 + case .success(let image):
135 + image.resizable().aspectRatio(contentMode: .fill)
136 + default:
137 + (universe?.accent ?? .gray).opacity(0.12)
138 + .overlay(Image(systemName: universe?.symbol ?? "photo")
139 + .foregroundStyle(universe?.accent ?? .gray))
140 + }
141 + }
142 + .frame(width: 74, height: 74)
143 + .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
144 + .overlay(RoundedRectangle(cornerRadius: 10, style: .continuous)
145 + .strokeBorder(.primary.opacity(0.25), lineWidth: 1))
146 + .accessibilityHidden(true)
147 + } else {
148 + RoundedRectangle(cornerRadius: 8, style: .continuous)
149 + .fill(universe?.accent.opacity(0.9) ?? .gray)
150 + .frame(width: 5)
151 + .padding(.vertical, 2)
152 + }
135 153 VStack(alignment: .leading, spacing: 4) {
136 154 Text(item.title)
137 155 .font(.headline)
modified KA/Features/ProfileView.swift +41 −15
@@ -7,6 +7,7 @@ struct ProfileView: View {
7 7 @AppStorage("ka.favUniverses") private var favUniversesRaw = ""
8 8 @AppStorage("ka.appearance") private var appearance = "auto"
9 9 @Environment(\.colorScheme) private var scheme
10 + @StateObject private var kaid = KAIDManager.shared
10 11
11 12 private var favIDs: Set<String> {
12 13 Set(favUniversesRaw.split(separator: ",").map(String.init))
@@ -16,23 +17,48 @@ struct ProfileView: View {
16 17 NavigationStack {
17 18 List {
18 19 Section {
19 HStack(spacing: 14) {
20 ZStack {
21 RoundedRectangle(cornerRadius: 14, style: .continuous)
22 .fill(KATheme.inkLight)
23 .frame(width: 56, height: 56)
24 Text("KA").font(.system(size: 22, weight: .bold, design: .rounded))
25 .foregroundStyle(KATheme.lime)
26 .rotationEffect(.degrees(-4))
20 + if let p = kaid.profile {
21 + KAIDCard(profile: p)
22 + .listRowInsets(EdgeInsets())
23 + .listRowBackground(Color.clear)
24 + Link(destination: Ecosystem.hubURL.appendingPathComponent("/compte")) {
25 + Label("Gérer mon profil sur groupe-ka.com", systemImage: "person.text.rectangle")
27 26 }
28 VStack(alignment: .leading, spacing: 3) {
29 Text("Votre KA ID").font(.headline)
30 Text("Un seul compte pour tout l'écosystème — création et gestion sur groupe-ka.com.")
31 .font(.caption).foregroundStyle(.secondary)
27 + Button(role: .destructive) { kaid.logout() } label: {
28 + Label("Se déconnecter", systemImage: "rectangle.portrait.and.arrow.right")
29 + }
30 + } else {
31 + HStack(spacing: 14) {
32 + ZStack {
33 + RoundedRectangle(cornerRadius: 14, style: .continuous)
34 + .fill(KATheme.inkLight)
35 + .frame(width: 56, height: 56)
36 + Text("KA").font(.system(size: 22, weight: .bold, design: .rounded))
37 + .foregroundStyle(KATheme.lime)
38 + .rotationEffect(.degrees(-4))
39 + }
40 + VStack(alignment: .leading, spacing: 3) {
41 + Text("Votre KA ID").font(.headline)
42 + Text("Un seul compte pour les 13 plateformes de l'écosystème.")
43 + .font(.caption).foregroundStyle(.secondary)
44 + }
45 + }
46 + Button {
47 + kaid.login()
48 + } label: {
49 + HStack {
50 + Label("Se connecter avec KA ID", systemImage: "person.crop.circle.badge.checkmark")
51 + Spacer()
52 + if kaid.busy { ProgressView() }
53 + }
54 + }
55 + .disabled(kaid.busy)
56 + Link(destination: Ecosystem.signupURL) {
57 + Label("Créer un compte sur groupe-ka.com", systemImage: "arrow.up.right.square")
58 + }
59 + if let e = kaid.lastError {
60 + Text(e).font(.caption).foregroundStyle(.red)
32 61 }
33 }
34 Link(destination: Ecosystem.signupURL) {
35 Label("Se connecter / créer un compte KA ID", systemImage: "person.crop.circle.badge.checkmark")
36 62 }
37 63 }
38 64
modified KA/Features/UniversesView.swift +32 −0
@@ -195,6 +195,25 @@ struct ItemDetailView: View {
195 195 var body: some View {
196 196 ScrollView {
197 197 VStack(alignment: .leading, spacing: 16) {
198 + if let img = item.imageURL {
199 + AsyncImage(url: img) { phase in
200 + switch phase {
201 + case .success(let image):
202 + image.resizable().aspectRatio(contentMode: .fill)
203 + case .failure:
204 + EmptyView()
205 + default:
206 + (universe?.accent ?? .gray).opacity(0.1)
207 + .overlay(ProgressView())
208 + }
209 + }
210 + .frame(maxWidth: .infinity)
211 + .frame(height: 230)
212 + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
213 + .overlay(RoundedRectangle(cornerRadius: 16, style: .continuous)
214 + .strokeBorder(.primary.opacity(0.3), lineWidth: 1.2))
215 + .accessibilityLabel("Photo : \(item.title)")
216 + }
198 217 if let u = universe {
199 218 HStack { KAChip(text: u.wordmark, accent: u.accent); Spacer() }
200 219 }
@@ -208,6 +227,19 @@ struct ItemDetailView: View {
208 227 if let c = item.city { KAChip(text: c) }
209 228 }
210 229
230 + if let detail = item.detail, !detail.isEmpty {
231 + VStack(alignment: .leading, spacing: 6) {
232 + Text("À propos").font(.headline)
233 + Text(detail)
234 + .font(.subheadline)
235 + .foregroundStyle(KATheme.ink2(scheme))
236 + .lineLimit(12)
237 + }
238 + .padding(14)
239 + .frame(maxWidth: .infinity, alignment: .leading)
240 + .kaCard()
241 + }
242 +
211 243 if !item.facts.isEmpty {
212 244 VStack(spacing: 0) {
213 245 ForEach(item.facts, id: \.self) { f in
added KA/Info.plist +44 −0
@@ -0,0 +1,44 @@
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>CFBundleExecutable</key>
8 + <string>$(EXECUTABLE_NAME)</string>
9 + <key>CFBundleIdentifier</key>
10 + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 + <key>CFBundleInfoDictionaryVersion</key>
12 + <string>6.0</string>
13 + <key>CFBundleName</key>
14 + <string>$(PRODUCT_NAME)</string>
15 + <key>CFBundlePackageType</key>
16 + <string>APPL</string>
17 + <key>CFBundleShortVersionString</key>
18 + <string>1.0</string>
19 + <key>CFBundleURLTypes</key>
20 + <array>
21 + <dict>
22 + <key>CFBundleURLName</key>
23 + <string>com.groupeka.ka.auth</string>
24 + <key>CFBundleURLSchemes</key>
25 + <array>
26 + <string>ka-ios</string>
27 + </array>
28 + </dict>
29 + </array>
30 + <key>CFBundleVersion</key>
31 + <string>1</string>
32 + <key>ITSAppUsesNonExemptEncryption</key>
33 + <false/>
34 + <key>UILaunchScreen</key>
35 + <dict/>
36 + <key>UISupportedInterfaceOrientations</key>
37 + <array>
38 + <string>UIInterfaceOrientationPortrait</string>
39 + <string>UIInterfaceOrientationPortraitUpsideDown</string>
40 + <string>UIInterfaceOrientationLandscapeLeft</string>
41 + <string>UIInterfaceOrientationLandscapeRight</string>
42 + </array>
43 +</dict>
44 +</plist>
modified KA/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024.png +0 −0

Binary file not shown.

modified KATests/KATests.swift +1 −1
@@ -78,7 +78,7 @@ final class AdapterTests: XCTestCase {
78 78 let store = FavoritesStore()
79 79 let item = KAItem(id: "t:1", universeID: "lou-ka", title: "Test",
80 80 subtitle: nil, priceLabel: nil, city: nil, url: nil,
81 imageURL: nil, latitude: nil, longitude: nil, facts: [])
81 + imageURL: nil, latitude: nil, longitude: nil, detail: nil, facts: [])
82 82 XCTAssertFalse(store.isFavorite(item))
83 83 store.toggle(item)
84 84 XCTAssertTrue(store.isFavorite(item))
added docs/icon.svg +60 −0
@@ -0,0 +1,60 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
2 + <!-- Icône KA — Groupe-KA · éditorial sharp (encre, lime, ombre décalée, 12 univers) -->
3 + <defs>
4 + <radialGradient id="glow" cx="32%" cy="26%" r="75%">
5 + <stop offset="0%" stop-color="#d9f26b" stop-opacity="0.20"/>
6 + <stop offset="55%" stop-color="#d9f26b" stop-opacity="0.05"/>
7 + <stop offset="100%" stop-color="#d9f26b" stop-opacity="0"/>
8 + </radialGradient>
9 + <filter id="grain">
10 + <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" stitchTiles="stitch"/>
11 + <feColorMatrix type="saturate" values="0"/>
12 + <feComponentTransfer><feFuncA type="linear" slope="0.05"/></feComponentTransfer>
13 + <feComposite operator="over" in2="SourceGraphic"/>
14 + </filter>
15 + </defs>
16 +
17 + <!-- fond encre + halo + grain -->
18 + <rect width="1024" height="1024" fill="#141814"/>
19 + <rect width="1024" height="1024" fill="url(#glow)"/>
20 + <rect width="1024" height="1024" filter="url(#grain)" opacity="0.6"/>
21 +
22 + <!-- cadre papier fin (signature bordure) -->
23 + <rect x="52" y="52" width="920" height="920" rx="64" fill="none"
24 + stroke="#f5f3ee" stroke-opacity="0.16" stroke-width="6"/>
25 +
26 + <!-- kicker mono -->
27 + <text x="512" y="188" text-anchor="middle"
28 + font-family="Menlo, ui-monospace, monospace" font-size="44" font-weight="700"
29 + letter-spacing="18" fill="#8b928c">·KA = AGRÉGER</text>
30 +
31 + <!-- KA : ombre décalée dure (vert profond) puis lime, légère rotation -->
32 + <g transform="rotate(-4 512 560)">
33 + <text x="534" y="712" text-anchor="middle"
34 + font-family="-apple-system, 'SF Pro Rounded', 'Arial Rounded MT Bold', sans-serif"
35 + font-size="470" font-weight="800" letter-spacing="-18"
36 + fill="#123f2e">KA</text>
37 + <text x="512" y="690" text-anchor="middle"
38 + font-family="-apple-system, 'SF Pro Rounded', 'Arial Rounded MT Bold', sans-serif"
39 + font-size="470" font-weight="800" letter-spacing="-18"
40 + fill="#d9f26b">KA</text>
41 + </g>
42 +
43 + <!-- les 12 univers : pastilles d'accent officielles -->
44 + <g transform="translate(512 862)">
45 + <g transform="translate(-341 0)">
46 + <circle cx="0" r="26" fill="#1c7ed6"/>
47 + <circle cx="62" r="26" fill="#ff6a00"/>
48 + <circle cx="124" r="26" fill="#e23744"/>
49 + <circle cx="186" r="26" fill="#ff5148"/>
50 + <circle cx="248" r="26" fill="#ff5a2a"/>
51 + <circle cx="310" r="26" fill="#c4532e"/>
52 + <circle cx="372" r="26" fill="#1f9d55"/>
53 + <circle cx="434" r="26" fill="#f08c00"/>
54 + <circle cx="496" r="26" fill="#d6336c"/>
55 + <circle cx="558" r="26" fill="#7048e8"/>
56 + <circle cx="620" r="26" fill="#3b5bdb"/>
57 + <circle cx="682" r="26" fill="#0c8599"/>
58 + </g>
59 + </g>
60 +</svg>
added docs/screenshots/04-accueil-vignettes.png +0 −0

Binary file not shown.

modified project.yml +16 −4
@@ -10,7 +10,7 @@ settings:
10 10 base:
11 11 SWIFT_VERSION: "5.0"
12 12 MARKETING_VERSION: "1.0.0"
13 CURRENT_PROJECT_VERSION: "1"
13 + CURRENT_PROJECT_VERSION: "3"
14 14 DEVELOPMENT_TEAM: "3YM54G49SN"
15 15 CODE_SIGN_STYLE: Automatic
16 16 GENERATE_INFOPLIST_FILE: true
@@ -27,13 +27,25 @@ targets:
27 27 settings:
28 28 base:
29 29 PRODUCT_BUNDLE_IDENTIFIER: com.groupeka.ka
30 + TARGETED_DEVICE_FAMILY: "1"
31 + INFOPLIST_KEY_CFBundleDevelopmentRegion: fr
30 32 INFOPLIST_KEY_CFBundleDisplayName: KA
31 33 INFOPLIST_KEY_UILaunchScreen_Generation: true
32 34 INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone: "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"
33 35 INFOPLIST_KEY_NSLocationWhenInUseUsageDescription: "KA affiche les logements, restos, sorties et emplois autour de vous sur la carte unifiée."
34 INFOPLIST_KEY_ITSAppUsesNonExemptEncryption: false
35 INFOPLIST_KEY_CFBundleDevelopmentRegion: fr
36 TARGETED_DEVICE_FAMILY: "1"
36 + info:
37 + path: KA/Info.plist
38 + properties:
39 + CFBundleURLTypes:
40 + - CFBundleURLName: com.groupeka.ka.auth
41 + CFBundleURLSchemes: [ka-ios]
42 + UISupportedInterfaceOrientations:
43 + - UIInterfaceOrientationPortrait
44 + - UIInterfaceOrientationPortraitUpsideDown
45 + - UIInterfaceOrientationLandscapeLeft
46 + - UIInterfaceOrientationLandscapeRight
47 + UILaunchScreen: {}
48 + ITSAppUsesNonExemptEncryption: false
37 49 KAWidgets:
38 50 type: app-extension
39 51 platform: iOS
40 52