v11: encode wiederhergestellt, Absturz-Melder, Methoden-Check
This commit is contained in:
1 parent
b85fe90c38
commit
98a94e765e
5 files changed
+89
-5
No files matched your search
@@ -28,7 +28,6 @@
|
||||
65AABC92308B81050050A7AC /* GuidanceManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 65AABC90308B81050050A7AC /* GuidanceManager.m */; };
|
||||
65AABC98308B83BE0050A7AC /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 65AABC94308B83BE0050A7AC /* SettingsViewController.m */; };
|
||||
65AABC99308B83BE0050A7AC /* BVGSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 65AABC95308B83BE0050A7AC /* BVGSettings.m */; };
|
||||
65AABC9C308B87630050A7AC /* AppIcon.appiconset in Resources */ = {isa = PBXBuildFile; fileRef = 65AABC9B308B87630050A7AC /* AppIcon.appiconset */; };
|
||||
65AABCA5308B87E00050A7AC /* CardCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 65AABC9E308B87E00050A7AC /* CardCell.m */; };
|
||||
65AABCA6308B87E00050A7AC /* ChipBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 65AABCA1308B87E00050A7AC /* ChipBar.m */; };
|
||||
65AABCA7308B87E00050A7AC /* BVGTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 65AABCA3308B87E00050A7AC /* BVGTheme.m */; };
|
||||
@@ -78,7 +77,6 @@
|
||||
65AABC94308B83BE0050A7AC /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = "<group>"; };
|
||||
65AABC95308B83BE0050A7AC /* BVGSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BVGSettings.m; sourceTree = "<group>"; };
|
||||
65AABC97308B83BE0050A7AC /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = "<group>"; };
|
||||
65AABC9B308B87630050A7AC /* AppIcon.appiconset */ = {isa = PBXFileReference; lastKnownFileType = folder; path = AppIcon.appiconset; sourceTree = "<group>"; };
|
||||
65AABC9D308B87E00050A7AC /* CardCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CardCell.h; sourceTree = "<group>"; };
|
||||
65AABC9E308B87E00050A7AC /* CardCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CardCell.m; sourceTree = "<group>"; };
|
||||
65AABC9F308B87E00050A7AC /* ChipBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChipBar.h; sourceTree = "<group>"; };
|
||||
@@ -137,7 +135,6 @@
|
||||
65AABC74308B7A0E0050A7AC /* BVGAPI.h */,
|
||||
65AABC93308B83BE0050A7AC /* BVGSettings.h */,
|
||||
65AABC95308B83BE0050A7AC /* BVGSettings.m */,
|
||||
65AABC9B308B87630050A7AC /* AppIcon.appiconset */,
|
||||
65AABC97308B83BE0050A7AC /* SettingsViewController.h */,
|
||||
65AABC94308B83BE0050A7AC /* SettingsViewController.m */,
|
||||
65AABC76308B7A0E0050A7AC /* BVGAPI.m */,
|
||||
@@ -235,7 +232,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
65AABCAC308B91540050A7AC /* add_files.rb in Resources */,
|
||||
65AABC9C308B87630050A7AC /* AppIcon.appiconset in Resources */,
|
||||
659F88E3308B5CFA00D46649 /* LaunchScreen.storyboard in Resources */,
|
||||
659F88E0308B5CFA00D46649 /* Assets.xcassets in Resources */,
|
||||
65AABC8B308B7D8C0050A7AC /* LIESMICH.txt in Resources */,
|
||||
|
||||
@@ -16,9 +16,38 @@
|
||||
@property (nonatomic, strong) UITabBarController *tabs;
|
||||
@end
|
||||
|
||||
// Absturz-Melder: speichert Grund + Stack einer nicht abgefangenen Exception,
|
||||
// beim naechsten Start wird beides angezeigt (iOS 9 schreibt den Grund nicht in den Crash-Report).
|
||||
static NSString * const kLastCrashKey = @"lastCrash";
|
||||
|
||||
static void BVGUncaughtException(NSException *e) {
|
||||
NSArray *stack = e.callStackSymbols ?: @[];
|
||||
stack = [stack subarrayWithRange:NSMakeRange(0, MIN((NSUInteger)25, stack.count))];
|
||||
NSString *report = [NSString stringWithFormat:@"%@\n%@\n\n%@", e.name, e.reason ?: @"(kein Grund)",
|
||||
[stack componentsJoinedByString:@"\n"]];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:report forKey:kLastCrashKey];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void)showLastCrashIfAny {
|
||||
NSString *report = [[NSUserDefaults standardUserDefaults] stringForKey:kLastCrashKey];
|
||||
if (!report) return;
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kLastCrashKey];
|
||||
|
||||
UIAlertController *a = [UIAlertController alertControllerWithTitle:@"Letzter Absturz"
|
||||
message:report
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[a addAction:[UIAlertAction actionWithTitle:@"Kopieren" style:UIAlertActionStyleDefault handler:^(UIAlertAction *x) {
|
||||
[UIPasteboard generalPasteboard].string = report;
|
||||
}]];
|
||||
[a addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self.window.rootViewController presentViewController:a animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
NSSetUncaughtExceptionHandler(&BVGUncaughtException);
|
||||
[application registerUserNotificationSettings:
|
||||
[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge)
|
||||
categories:nil]];
|
||||
@@ -48,6 +77,9 @@
|
||||
self.window.tintColor = [BVGTheme textPrimary];
|
||||
self.window.rootViewController = self.tabs;
|
||||
[self.window makeKeyAndVisible];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self showLastCrashIfAny];
|
||||
});
|
||||
|
||||
// Favoriten in die iOS-Suche aufnehmen (falls frisch installiert)
|
||||
for (NSDictionary *f in [BVGAPI favorites]) [BVGAPI indexStopInSpotlight:f[@"id"] name:f[@"name"]];
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ extern NSString * const BVGAlarmsChangedNotification;
|
||||
|
||||
// Netzwerk
|
||||
+ (void)getJSON:(NSString *)path completion:(void (^)(id json, NSError *error))completion;
|
||||
+ (NSString *)encode:(NSString *)s;
|
||||
+ (NSString *)encode:(id)value; // NSString, NSNumber, nil/NSNull -> URL-kodiert
|
||||
+ (NSString *)lastSource; // "serverlele" oder "transport.rest" - wer zuletzt geantwortet hat
|
||||
|
||||
// Zeit
|
||||
|
||||
@@ -87,6 +87,22 @@ NSString * const BVGAlarmsChangedNotification = @"BVGAlarmsChanged";
|
||||
}] resume];
|
||||
}
|
||||
|
||||
// Fuer URL-Pfade und -Parameter: nur ASCII-Buchstaben/Ziffern bleiben unkodiert
|
||||
// (sonst liefert NSURL bei Umlauten nil). Nimmt auch NSNumber/NSNull entgegen.
|
||||
+ (NSString *)encode:(id)value {
|
||||
NSString *s = nil;
|
||||
if ([value isKindOfClass:[NSString class]]) s = value;
|
||||
else if (value && value != [NSNull null]) s = [value description];
|
||||
if (!s.length) return @"";
|
||||
static NSCharacterSet *allowed;
|
||||
static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
allowed = [NSCharacterSet characterSetWithCharactersInString:
|
||||
@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"];
|
||||
});
|
||||
return [s stringByAddingPercentEncodingWithAllowedCharacters:allowed] ?: @"";
|
||||
}
|
||||
|
||||
#pragma mark - Zeit
|
||||
|
||||
+ (NSDateFormatter *)isoFormatter {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Prueft, ob jede in einer .h deklarierte Methode in der passenden .m implementiert ist.
|
||||
Hintergrund: Objective-C kompiliert auch dann, wenn eine Methode fehlt - die App stuerzt erst
|
||||
zur Laufzeit mit "unrecognized selector" ab.
|
||||
|
||||
Aufruf (im Projektordner): python3 BVGNearby/check_methods.py BVGNearby
|
||||
Rueckgabe: 0 = alles da, 1 = etwas fehlt (bricht z.B. deploy.sh ab)
|
||||
"""
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
folder = sys.argv[1] if len(sys.argv) > 1 else "."
|
||||
missing = []
|
||||
|
||||
for header in sorted(glob.glob(os.path.join(folder, "*.h"))):
|
||||
impl_path = header[:-2] + ".m"
|
||||
if not os.path.exists(impl_path):
|
||||
continue
|
||||
hdr = open(header, encoding="utf-8").read()
|
||||
impl = open(impl_path, encoding="utf-8").read()
|
||||
for decl in re.findall(r"^[+-]\s*\([^)]*\)\s*[^;]+;", hdr, re.M):
|
||||
kind = decl[0]
|
||||
parts = re.findall(r"(\w+)\s*:", decl)
|
||||
if parts:
|
||||
selector = "".join(p + ":" for p in parts)
|
||||
pattern = (r"^" + re.escape(kind) + r"\s*\([^)]*\)\s*"
|
||||
+ r".*?".join(re.escape(p) + r"\s*:" for p in parts) + r"[^;{]*\{")
|
||||
else:
|
||||
selector = re.match(r"^[+-]\s*\([^)]*\)\s*(\w+)", decl).group(1)
|
||||
pattern = r"^" + re.escape(kind) + r"\s*\([^)]*\)\s*" + re.escape(selector) + r"\s*\{"
|
||||
if not re.search(pattern, impl, re.M | re.S):
|
||||
missing.append(f"{os.path.basename(impl_path)}: {kind}{selector}")
|
||||
|
||||
if missing:
|
||||
print("FEHLENDE METHODEN (App würde abstürzen):")
|
||||
print("\n".join(" " + m for m in missing))
|
||||
sys.exit(1)
|
||||
print("Methoden-Check: alle deklarierten Methoden sind implementiert.")
|
||||
Reference in new issue
Block a user