mirror of
				https://github.com/Mueller-Patrick/DHBW-Service-App.git
				synced 2025-11-04 02:35:47 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			d7f078cc88
			...
			06b66a50a5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 06b66a50a5 | |||
| 02cd3a0db9 | 
| 
						 | 
					@ -16,6 +16,7 @@
 | 
				
			||||||
		CD730A35259A860E00E0BB69 /* SettingsAcknowledgements.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */; };
 | 
							CD730A35259A860E00E0BB69 /* SettingsAcknowledgements.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */; };
 | 
				
			||||||
		CD8555BE25C47AE500C4ACD6 /* RaPlaFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8555BD25C47AE500C4ACD6 /* RaPlaFetcher.swift */; };
 | 
							CD8555BE25C47AE500C4ACD6 /* RaPlaFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8555BD25C47AE500C4ACD6 /* RaPlaFetcher.swift */; };
 | 
				
			||||||
		CD8555C325C47B5300C4ACD6 /* ApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8555C225C47B5300C4ACD6 /* ApiService.swift */; };
 | 
							CD8555C325C47B5300C4ACD6 /* ApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8555C225C47B5300C4ACD6 /* ApiService.swift */; };
 | 
				
			||||||
 | 
							CD9E3F0F25DC466100C77D10 /* SettingsPushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9E3F0E25DC466100C77D10 /* SettingsPushNotifications.swift */; };
 | 
				
			||||||
		CD9FAB81258EC60200D6D0C5 /* DHBW_ServiceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9FAB80258EC60200D6D0C5 /* DHBW_ServiceApp.swift */; };
 | 
							CD9FAB81258EC60200D6D0C5 /* DHBW_ServiceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9FAB80258EC60200D6D0C5 /* DHBW_ServiceApp.swift */; };
 | 
				
			||||||
		CD9FAB83258EC60200D6D0C5 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9FAB82258EC60200D6D0C5 /* ContentView.swift */; };
 | 
							CD9FAB83258EC60200D6D0C5 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9FAB82258EC60200D6D0C5 /* ContentView.swift */; };
 | 
				
			||||||
		CD9FAB85258EC60600D6D0C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD9FAB84258EC60600D6D0C5 /* Assets.xcassets */; };
 | 
							CD9FAB85258EC60600D6D0C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD9FAB84258EC60600D6D0C5 /* Assets.xcassets */; };
 | 
				
			||||||
| 
						 | 
					@ -69,6 +70,8 @@
 | 
				
			||||||
		CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAcknowledgements.swift; sourceTree = "<group>"; };
 | 
							CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAcknowledgements.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		CD8555BD25C47AE500C4ACD6 /* RaPlaFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RaPlaFetcher.swift; sourceTree = "<group>"; };
 | 
							CD8555BD25C47AE500C4ACD6 /* RaPlaFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RaPlaFetcher.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		CD8555C225C47B5300C4ACD6 /* ApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiService.swift; sourceTree = "<group>"; };
 | 
							CD8555C225C47B5300C4ACD6 /* ApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiService.swift; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							CD9E3F0A25DC3C9A00C77D10 /* DHBW-Service.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "DHBW-Service.entitlements"; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							CD9E3F0E25DC466100C77D10 /* SettingsPushNotifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPushNotifications.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		CD9FAB7D258EC60200D6D0C5 /* DHBW-Service.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DHBW-Service.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							CD9FAB7D258EC60200D6D0C5 /* DHBW-Service.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DHBW-Service.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		CD9FAB80258EC60200D6D0C5 /* DHBW_ServiceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DHBW_ServiceApp.swift; sourceTree = "<group>"; };
 | 
							CD9FAB80258EC60200D6D0C5 /* DHBW_ServiceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DHBW_ServiceApp.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		CD9FAB82258EC60200D6D0C5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
 | 
							CD9FAB82258EC60200D6D0C5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
| 
						 | 
					@ -179,6 +182,7 @@
 | 
				
			||||||
			isa = PBXGroup;
 | 
								isa = PBXGroup;
 | 
				
			||||||
			children = (
 | 
								children = (
 | 
				
			||||||
				CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */,
 | 
									CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */,
 | 
				
			||||||
 | 
									CD9E3F0E25DC466100C77D10 /* SettingsPushNotifications.swift */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			path = SettingsSubViews;
 | 
								path = SettingsSubViews;
 | 
				
			||||||
			sourceTree = "<group>";
 | 
								sourceTree = "<group>";
 | 
				
			||||||
| 
						 | 
					@ -206,6 +210,7 @@
 | 
				
			||||||
		CD9FAB7F258EC60200D6D0C5 /* DHBW-Service */ = {
 | 
							CD9FAB7F258EC60200D6D0C5 /* DHBW-Service */ = {
 | 
				
			||||||
			isa = PBXGroup;
 | 
								isa = PBXGroup;
 | 
				
			||||||
			children = (
 | 
								children = (
 | 
				
			||||||
 | 
									CD9E3F0A25DC3C9A00C77D10 /* DHBW-Service.entitlements */,
 | 
				
			||||||
				CDCD720F25912D3C00FBF2F5 /* App */,
 | 
									CDCD720F25912D3C00FBF2F5 /* App */,
 | 
				
			||||||
				CDCD721025912D4900FBF2F5 /* Views */,
 | 
									CDCD721025912D4900FBF2F5 /* Views */,
 | 
				
			||||||
				CDDCF4792591FE410027CDC5 /* Utility */,
 | 
									CDDCF4792591FE410027CDC5 /* Utility */,
 | 
				
			||||||
| 
						 | 
					@ -475,6 +480,7 @@
 | 
				
			||||||
				CD9FAB8A258EC60600D6D0C5 /* Persistence.swift in Sources */,
 | 
									CD9FAB8A258EC60600D6D0C5 /* Persistence.swift in Sources */,
 | 
				
			||||||
				CD730A35259A860E00E0BB69 /* SettingsAcknowledgements.swift in Sources */,
 | 
									CD730A35259A860E00E0BB69 /* SettingsAcknowledgements.swift in Sources */,
 | 
				
			||||||
				CD9FAB83258EC60200D6D0C5 /* ContentView.swift in Sources */,
 | 
									CD9FAB83258EC60200D6D0C5 /* ContentView.swift in Sources */,
 | 
				
			||||||
 | 
									CD9E3F0F25DC466100C77D10 /* SettingsPushNotifications.swift in Sources */,
 | 
				
			||||||
				CDCD72242591316500FBF2F5 /* LocalSettings.swift in Sources */,
 | 
									CDCD72242591316500FBF2F5 /* LocalSettings.swift in Sources */,
 | 
				
			||||||
				CDD970E125D453D90061755E /* RaPlaEvent+CoreDataClass.swift in Sources */,
 | 
									CDD970E125D453D90061755E /* RaPlaEvent+CoreDataClass.swift in Sources */,
 | 
				
			||||||
				CDD970DD25D453D90061755E /* User+CoreDataClass.swift in Sources */,
 | 
									CDD970DD25D453D90061755E /* User+CoreDataClass.swift in Sources */,
 | 
				
			||||||
| 
						 | 
					@ -675,8 +681,11 @@
 | 
				
			||||||
		CD9FABA8258EC60600D6D0C5 /* Debug */ = {
 | 
							CD9FABA8258EC60600D6D0C5 /* Debug */ = {
 | 
				
			||||||
			isa = XCBuildConfiguration;
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
			buildSettings = {
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
				
			||||||
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
									ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
				
			||||||
 | 
									CODE_SIGN_ENTITLEMENTS = "DHBW-Service/DHBW-Service.entitlements";
 | 
				
			||||||
				CODE_SIGN_STYLE = Automatic;
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
 | 
									CURRENT_PROJECT_VERSION = 1.0.1;
 | 
				
			||||||
				DEVELOPMENT_ASSET_PATHS = "\"DHBW-Service/Preview Content\"";
 | 
									DEVELOPMENT_ASSET_PATHS = "\"DHBW-Service/Preview Content\"";
 | 
				
			||||||
				DEVELOPMENT_TEAM = HS7KNT4MZ2;
 | 
									DEVELOPMENT_TEAM = HS7KNT4MZ2;
 | 
				
			||||||
				ENABLE_PREVIEWS = YES;
 | 
									ENABLE_PREVIEWS = YES;
 | 
				
			||||||
| 
						 | 
					@ -696,8 +705,11 @@
 | 
				
			||||||
		CD9FABA9258EC60600D6D0C5 /* Release */ = {
 | 
							CD9FABA9258EC60600D6D0C5 /* Release */ = {
 | 
				
			||||||
			isa = XCBuildConfiguration;
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
			buildSettings = {
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
				
			||||||
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
									ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
				
			||||||
 | 
									CODE_SIGN_ENTITLEMENTS = "DHBW-Service/DHBW-Service.entitlements";
 | 
				
			||||||
				CODE_SIGN_STYLE = Automatic;
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
 | 
									CURRENT_PROJECT_VERSION = 1.0.1;
 | 
				
			||||||
				DEVELOPMENT_ASSET_PATHS = "\"DHBW-Service/Preview Content\"";
 | 
									DEVELOPMENT_ASSET_PATHS = "\"DHBW-Service/Preview Content\"";
 | 
				
			||||||
				DEVELOPMENT_TEAM = HS7KNT4MZ2;
 | 
									DEVELOPMENT_TEAM = HS7KNT4MZ2;
 | 
				
			||||||
				ENABLE_PREVIEWS = YES;
 | 
									ENABLE_PREVIEWS = YES;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,12 +6,14 @@
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import SwiftUI
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					import UserNotifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@main
 | 
					@main
 | 
				
			||||||
struct DHBW_ServiceApp: App {
 | 
					struct DHBW_ServiceApp: App {
 | 
				
			||||||
 | 
					    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
 | 
				
			||||||
    let persistenceController = PersistenceController.shared
 | 
					    let persistenceController = PersistenceController.shared
 | 
				
			||||||
    let settings = LocalSettings()
 | 
					    let settings = LocalSettings()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    var body: some Scene {
 | 
					    var body: some Scene {
 | 
				
			||||||
        WindowGroup {
 | 
					        WindowGroup {
 | 
				
			||||||
            ContentView()
 | 
					            ContentView()
 | 
				
			||||||
| 
						 | 
					@ -20,3 +22,21 @@ struct DHBW_ServiceApp: App {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*** Implement App delegate ***//
 | 
				
			||||||
 | 
					class AppDelegate: NSObject, UIApplicationDelegate {
 | 
				
			||||||
 | 
					    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
 | 
				
			||||||
 | 
					        return true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    //No callback in simulator
 | 
				
			||||||
 | 
					    //-- must use device to get valid push token
 | 
				
			||||||
 | 
					    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)          {
 | 
				
			||||||
 | 
					        let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
 | 
				
			||||||
 | 
					        let token = tokenParts.joined()
 | 
				
			||||||
 | 
					        print("Device Token: \(token)")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
 | 
				
			||||||
 | 
					        print(error.localizedDescription)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 | 
					<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 | 
				
			||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D62" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
 | 
					<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20E5172i" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
 | 
				
			||||||
    <entity name="Lecturer" representedClassName="Lecturer" syncable="YES">
 | 
					    <entity name="Lecturer" representedClassName="Lecturer" syncable="YES">
 | 
				
			||||||
        <attribute name="email" optional="YES" attributeType="String"/>
 | 
					        <attribute name="email" optional="YES" attributeType="String"/>
 | 
				
			||||||
        <attribute name="name" optional="YES" attributeType="String"/>
 | 
					        <attribute name="name" optional="YES" attributeType="String"/>
 | 
				
			||||||
| 
						 | 
					@ -20,10 +20,11 @@
 | 
				
			||||||
        <attribute name="course" optional="YES" attributeType="String"/>
 | 
					        <attribute name="course" optional="YES" attributeType="String"/>
 | 
				
			||||||
        <attribute name="director" optional="YES" attributeType="String"/>
 | 
					        <attribute name="director" optional="YES" attributeType="String"/>
 | 
				
			||||||
        <attribute name="name" optional="YES" attributeType="String"/>
 | 
					        <attribute name="name" optional="YES" attributeType="String"/>
 | 
				
			||||||
 | 
					        <attribute name="raplaLink" optional="YES" attributeType="String"/>
 | 
				
			||||||
    </entity>
 | 
					    </entity>
 | 
				
			||||||
    <elements>
 | 
					    <elements>
 | 
				
			||||||
        <element name="Lecturer" positionX="-351" positionY="99" width="128" height="74"/>
 | 
					        <element name="Lecturer" positionX="-351" positionY="99" width="128" height="74"/>
 | 
				
			||||||
        <element name="RaPlaEvent" positionX="-271.3642578125" positionY="83.64776611328125" width="128" height="164"/>
 | 
					        <element name="RaPlaEvent" positionX="-271.3642578125" positionY="83.64776611328125" width="128" height="164"/>
 | 
				
			||||||
        <element name="User" positionX="-428.6358032226562" positionY="2.067169189453125" width="128" height="74"/>
 | 
					        <element name="User" positionX="-428.6358032226562" positionY="2.067169189453125" width="128" height="89"/>
 | 
				
			||||||
    </elements>
 | 
					    </elements>
 | 
				
			||||||
</model>
 | 
					</model>
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ import CoreData
 | 
				
			||||||
struct PersistenceController {
 | 
					struct PersistenceController {
 | 
				
			||||||
    // Singleton
 | 
					    // Singleton
 | 
				
			||||||
    static let shared = PersistenceController()
 | 
					    static let shared = PersistenceController()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    // Cloud Kit container
 | 
					    // Cloud Kit container
 | 
				
			||||||
    let container: NSPersistentCloudKitContainer
 | 
					    let container: NSPersistentCloudKitContainer
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ struct PersistenceController {
 | 
				
			||||||
            return self.container.viewContext
 | 
					            return self.container.viewContext
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    // MARK: - Constructor
 | 
					    // MARK: - Constructor
 | 
				
			||||||
    init(inMemory: Bool = false) {
 | 
					    init(inMemory: Bool = false) {
 | 
				
			||||||
        container = NSPersistentCloudKitContainer(name: "DHBW_Service")
 | 
					        container = NSPersistentCloudKitContainer(name: "DHBW_Service")
 | 
				
			||||||
| 
						 | 
					@ -31,15 +31,15 @@ struct PersistenceController {
 | 
				
			||||||
            if let error = error as NSError? {
 | 
					            if let error = error as NSError? {
 | 
				
			||||||
                // Replace this implementation with code to handle the error appropriately.
 | 
					                // Replace this implementation with code to handle the error appropriately.
 | 
				
			||||||
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
 | 
					                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
                /*
 | 
					                /*
 | 
				
			||||||
                Typical reasons for an error here include:
 | 
					                 Typical reasons for an error here include:
 | 
				
			||||||
                * The parent directory does not exist, cannot be created, or disallows writing.
 | 
					                 * The parent directory does not exist, cannot be created, or disallows writing.
 | 
				
			||||||
                * The persistent store is not accessible, due to permissions or data protection when the device is locked.
 | 
					                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
 | 
				
			||||||
                * The device is out of space.
 | 
					                 * The device is out of space.
 | 
				
			||||||
                * The store could not be migrated to the current model version.
 | 
					                 * The store could not be migrated to the current model version.
 | 
				
			||||||
                Check the error message to determine what the actual problem was.
 | 
					                 Check the error message to determine what the actual problem was.
 | 
				
			||||||
                */
 | 
					                 */
 | 
				
			||||||
                fatalError("Unresolved error \(error), \(error.userInfo)")
 | 
					                fatalError("Unresolved error \(error), \(error.userInfo)")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -90,6 +90,18 @@ struct PersistenceController {
 | 
				
			||||||
        normalEvent2.category = "Lehrveranstaltung"
 | 
					        normalEvent2.category = "Lehrveranstaltung"
 | 
				
			||||||
        examEvent.category = "Prüfung"
 | 
					        examEvent.category = "Prüfung"
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let lecturer1 = Lecturer(context: PersistenceController.shared.context)
 | 
				
			||||||
 | 
					        let lecturer2 = Lecturer(context: PersistenceController.shared.context)
 | 
				
			||||||
 | 
					        lecturer1.name = "Mustermann, Prof. Dr."
 | 
				
			||||||
 | 
					        lecturer1.email = "mustermann@dhbw-karlsruhe.de"
 | 
				
			||||||
 | 
					        lecturer2.name = "Musterfrau, Prof. Dr."
 | 
				
			||||||
 | 
					        lecturer2.email = "musterfrau@dhbw-karlsruhe.de"
 | 
				
			||||||
 | 
					        normalEvent1.addToLecturers(lecturer1)
 | 
				
			||||||
 | 
					        normalEvent2.addToLecturers(lecturer2)
 | 
				
			||||||
 | 
					        examEvent.addToLecturers(lecturer1)
 | 
				
			||||||
 | 
					        examEvent.addToLecturers(lecturer2)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        var currentDate = Date()
 | 
					        var currentDate = Date()
 | 
				
			||||||
        currentDate.addTimeInterval(1*60*60);normalEvent1.startDate = currentDate
 | 
					        currentDate.addTimeInterval(1*60*60);normalEvent1.startDate = currentDate
 | 
				
			||||||
        currentDate.addTimeInterval(1*60*60);normalEvent2.startDate = currentDate
 | 
					        currentDate.addTimeInterval(1*60*60);normalEvent2.startDate = currentDate
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										8
									
								
								DHBW-Service/DHBW-Service.entitlements
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								DHBW-Service/DHBW-Service.entitlements
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
				
			||||||
 | 
					<plist version="1.0">
 | 
				
			||||||
 | 
					<dict>
 | 
				
			||||||
 | 
						<key>aps-environment</key>
 | 
				
			||||||
 | 
						<string>development</string>
 | 
				
			||||||
 | 
					</dict>
 | 
				
			||||||
 | 
					</plist>
 | 
				
			||||||
| 
						 | 
					@ -20,14 +20,20 @@
 | 
				
			||||||
				<false/>
 | 
									<false/>
 | 
				
			||||||
			</dict>
 | 
								</dict>
 | 
				
			||||||
		</dict>
 | 
							</dict>
 | 
				
			||||||
		<key>CFBundlePrimaryIcon</key>
 | 
						</dict>
 | 
				
			||||||
 | 
						<key>CFBundleIcons~ipad</key>
 | 
				
			||||||
 | 
						<dict>
 | 
				
			||||||
 | 
							<key>CFBundleAlternateIcons</key>
 | 
				
			||||||
		<dict>
 | 
							<dict>
 | 
				
			||||||
			<key>CFBundleIconFiles</key>
 | 
								<key>Alpaca-Alt-Icon</key>
 | 
				
			||||||
			<array>
 | 
								<dict>
 | 
				
			||||||
				<string>dhbw-standard-icon</string>
 | 
									<key>CFBundleIconFiles</key>
 | 
				
			||||||
			</array>
 | 
									<array>
 | 
				
			||||||
			<key>UIPrerenderedIcon</key>
 | 
										<string>alpaca-alt-icon</string>
 | 
				
			||||||
			<false/>
 | 
									</array>
 | 
				
			||||||
 | 
									<key>UIPrerenderedIcon</key>
 | 
				
			||||||
 | 
									<false/>
 | 
				
			||||||
 | 
								</dict>
 | 
				
			||||||
		</dict>
 | 
							</dict>
 | 
				
			||||||
	</dict>
 | 
						</dict>
 | 
				
			||||||
	<key>CFBundleIdentifier</key>
 | 
						<key>CFBundleIdentifier</key>
 | 
				
			||||||
| 
						 | 
					@ -41,7 +47,9 @@
 | 
				
			||||||
	<key>CFBundleShortVersionString</key>
 | 
						<key>CFBundleShortVersionString</key>
 | 
				
			||||||
	<string>1.0</string>
 | 
						<string>1.0</string>
 | 
				
			||||||
	<key>CFBundleVersion</key>
 | 
						<key>CFBundleVersion</key>
 | 
				
			||||||
	<string>1</string>
 | 
						<string>$(CURRENT_PROJECT_VERSION)</string>
 | 
				
			||||||
 | 
						<key>ITSAppUsesNonExemptEncryption</key>
 | 
				
			||||||
 | 
						<false/>
 | 
				
			||||||
	<key>LSRequiresIPhoneOS</key>
 | 
						<key>LSRequiresIPhoneOS</key>
 | 
				
			||||||
	<true/>
 | 
						<true/>
 | 
				
			||||||
	<key>UIApplicationSceneManifest</key>
 | 
						<key>UIApplicationSceneManifest</key>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,14 +10,21 @@ import CoreData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RaPlaFetcher {
 | 
					class RaPlaFetcher {
 | 
				
			||||||
    public class iCalEvent {
 | 
					    public class iCalEvent {
 | 
				
			||||||
        var startDate: Date = Date()    //DTSTART
 | 
					        var startDate: Date = Date()    // DTSTART
 | 
				
			||||||
        var endDate: Date = Date()      //DTEND
 | 
					        var endDate: Date = Date()      // DTEND
 | 
				
			||||||
        var summary: String = ""        //SUMMARY
 | 
					        var summary: String = ""        // SUMMARY
 | 
				
			||||||
        var description: String = ""    //DESCRIPTION
 | 
					        var description: String = ""    // DESCRIPTION
 | 
				
			||||||
        var location: String = ""       //LOCATION
 | 
					        var location: String = ""       // LOCATION
 | 
				
			||||||
        var category: String = ""       //CATEGORIES
 | 
					        var category: String = ""       // CATEGORIES
 | 
				
			||||||
        var uid: String = ""            //UID
 | 
					        var uid: String = ""            // UID
 | 
				
			||||||
        var lecturers: [LecturerObj] = []  //ATTENDEE
 | 
					        var lecturers: [LecturerObj] = []  // ATTENDEE
 | 
				
			||||||
 | 
					        var excludedDates: [Date] = []  // EXDATE
 | 
				
			||||||
 | 
					        var isRecurring: Bool = false   // If the event is recurring
 | 
				
			||||||
 | 
					        var frequency: String = ""      // Frequence in case of recurring events, e.g. DAILY or WEEKLY
 | 
				
			||||||
 | 
					        var recCount: Int = 0           // How often the event occurs in case of recurring events
 | 
				
			||||||
 | 
					        var recInterval: Int = 0        // Interval of the recurring event, e.g. 1 for every week / day / ...
 | 
				
			||||||
 | 
					        var recDay: String = ""         // The day of the recurrence, e.g. FR for friday
 | 
				
			||||||
 | 
					        var recUntil: Date = Date()     // Until when the event has to be repeated
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public class LecturerObj {
 | 
					    public class LecturerObj {
 | 
				
			||||||
| 
						 | 
					@ -84,7 +91,14 @@ class RaPlaFetcher {
 | 
				
			||||||
            for lineNr in 0...lines.count-1 {
 | 
					            for lineNr in 0...lines.count-1 {
 | 
				
			||||||
                if(lines[lineNr].hasPrefix(" ")){
 | 
					                if(lines[lineNr].hasPrefix(" ")){
 | 
				
			||||||
                    lines[lineNr] = String(lines[lineNr].dropFirst())
 | 
					                    lines[lineNr] = String(lines[lineNr].dropFirst())
 | 
				
			||||||
                    lines[lineNr-1].append(lines[lineNr])
 | 
					                    
 | 
				
			||||||
 | 
					                    // If there are more than 2 lines that have to be merged, we need to find out how many lines we have to go up
 | 
				
			||||||
 | 
					                    var goUp = 1
 | 
				
			||||||
 | 
					                    while(lines[lineNr-goUp] == ""){
 | 
				
			||||||
 | 
					                        goUp += 1
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    lines[lineNr-goUp].append(lines[lineNr])
 | 
				
			||||||
                    lines[lineNr] = ""
 | 
					                    lines[lineNr] = ""
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -144,6 +158,57 @@ class RaPlaFetcher {
 | 
				
			||||||
                    lecturer.email = lecturerEmail
 | 
					                    lecturer.email = lecturerEmail
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    evt.lecturers.append(lecturer)
 | 
					                    evt.lecturers.append(lecturer)
 | 
				
			||||||
 | 
					                } else if(line.hasPrefix("RRULE")) {
 | 
				
			||||||
 | 
					                    // This line normally looks like this:  RRULE:FREQ=WEEKLY;COUNT=12;INTERVAL=1;BYDAY=TU
 | 
				
			||||||
 | 
					                    // Can also look smth like this though: RRULE:FREQ=WEEKLY;COUNT=12
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    evt.isRecurring = true
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    let params = lineWithoutPrefix.components(separatedBy: ";")
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    for param in params {
 | 
				
			||||||
 | 
					                        let keyword = param[param.startIndex..<param.firstIndex(of: "=")!]
 | 
				
			||||||
 | 
					                        let value = param[param.index(param.firstIndex(of: "=")!, offsetBy: 1)..<param.endIndex]
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        switch keyword {
 | 
				
			||||||
 | 
					                        case "FREQ":
 | 
				
			||||||
 | 
					                            evt.frequency = String(value)
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					                        case "COUNT":
 | 
				
			||||||
 | 
					                            evt.recCount = Int(value)!
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					                        case "INTERVAL":
 | 
				
			||||||
 | 
					                            evt.recInterval = Int(value)!
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					                        case "BYDAY":
 | 
				
			||||||
 | 
					                            evt.recDay = String(value)
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					                        case "UNTIL":
 | 
				
			||||||
 | 
					                            let dateFormatter = DateFormatter()
 | 
				
			||||||
 | 
					                            dateFormatter.dateFormat = "yyyyMMdd"
 | 
				
			||||||
 | 
					                            let date = dateFormatter.date(from: String(value))!
 | 
				
			||||||
 | 
					                            evt.recUntil = date
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            print("Unknown parameter found: " + line)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else if (line.hasPrefix("EXDATE")) {
 | 
				
			||||||
 | 
					                    // Excluded from recurring events
 | 
				
			||||||
 | 
					                    // Format: 20210401T133000,20210408T133000,20210513T133000
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    let exclDateStrings = lineWithoutPrefix.components(separatedBy: ",")
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    for exclDate in exclDateStrings {
 | 
				
			||||||
 | 
					                        let dateFormatter = DateFormatter()
 | 
				
			||||||
 | 
					                        if(lineWithoutPrefix.contains("Z")){
 | 
				
			||||||
 | 
					                            dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss"
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        let date = dateFormatter.date(from: exclDate)!
 | 
				
			||||||
 | 
					                        evt.excludedDates.append(date)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
| 
						 | 
					@ -156,36 +221,124 @@ class RaPlaFetcher {
 | 
				
			||||||
    // Save the given iCalEvent objects to CoreData
 | 
					    // Save the given iCalEvent objects to CoreData
 | 
				
			||||||
    // Updates the events if they already exist and deletes old (/invalid) ones
 | 
					    // Updates the events if they already exist and deletes old (/invalid) ones
 | 
				
			||||||
    private class func saveToCoreData(eventObjects: [iCalEvent]) -> Bool{
 | 
					    private class func saveToCoreData(eventObjects: [iCalEvent]) -> Bool{
 | 
				
			||||||
 | 
					        // Get known UIDs
 | 
				
			||||||
        let existingEvents: [RaPlaEvent] = RaPlaEvent.getAll()
 | 
					        let existingEvents: [RaPlaEvent] = RaPlaEvent.getAll()
 | 
				
			||||||
        var existingEventsDict: [String:RaPlaEvent] = [:]
 | 
					        var existingEventsDict: [String:RaPlaEvent] = [:]
 | 
				
			||||||
        for event in existingEvents {
 | 
					        for event in existingEvents {
 | 
				
			||||||
            existingEventsDict[event.uid!] = event
 | 
					            existingEventsDict[event.uid!] = event
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let newEventUIDs = eventObjects.map{$0.uid}
 | 
					        // List for new UIDs
 | 
				
			||||||
 | 
					        var newEventUIDs: [String] = []
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        for event in eventObjects {
 | 
					        for event in eventObjects {
 | 
				
			||||||
            // If the event already exists locally, update it. Otherwise, create a new record
 | 
					            // If the event already exists locally, update it. Otherwise, create a new record
 | 
				
			||||||
            let evt: RaPlaEvent
 | 
					            if(event.isRecurring){
 | 
				
			||||||
            if existingEventsDict.keys.contains(event.uid) {
 | 
					                // Create as many events as we need
 | 
				
			||||||
                evt = existingEventsDict[event.uid]!
 | 
					                // If we e.g. need 12 events, we create 0...11
 | 
				
			||||||
 | 
					                for iteration in 0..<event.recCount {
 | 
				
			||||||
 | 
					                    // Calculate start- and enddate
 | 
				
			||||||
 | 
					                    // Calculate offset
 | 
				
			||||||
 | 
					                    let offsetType: Calendar.Component
 | 
				
			||||||
 | 
					                    let offsetAmount: Int
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    switch event.frequency {
 | 
				
			||||||
 | 
					                    case "DAILY":
 | 
				
			||||||
 | 
					                        offsetType = Calendar.Component.day
 | 
				
			||||||
 | 
					                        offsetAmount = 1
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    case "WEEKLY":
 | 
				
			||||||
 | 
					                        offsetType = Calendar.Component.day
 | 
				
			||||||
 | 
					                        offsetAmount = 7
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    case "MONTHLY":
 | 
				
			||||||
 | 
					                        offsetType = Calendar.Component.month
 | 
				
			||||||
 | 
					                        offsetAmount = 1
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    case "YEARLY":
 | 
				
			||||||
 | 
					                        offsetType = Calendar.Component.year
 | 
				
			||||||
 | 
					                        offsetAmount = 1
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        offsetType = Calendar.Component.day
 | 
				
			||||||
 | 
					                        offsetAmount = 0
 | 
				
			||||||
 | 
					                        print("Found unknown frequency: " + event.frequency)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    let startDate = Calendar.current.date(byAdding: offsetType, value: (offsetAmount * iteration), to: event.startDate)!
 | 
				
			||||||
 | 
					                    let endDate = Calendar.current.date(byAdding: offsetType, value: (offsetAmount * iteration), to: event.endDate)!
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Check if this recurrence should be excluded
 | 
				
			||||||
 | 
					                    if(event.excludedDates.contains(startDate)){
 | 
				
			||||||
 | 
					                        continue
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Generate UID
 | 
				
			||||||
 | 
					                    // Appending iteration to distinguish between recurring events
 | 
				
			||||||
 | 
					                    let newUID = event.uid + "---" + String(iteration)
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Create or update existing CoreData object
 | 
				
			||||||
 | 
					                    let evt: RaPlaEvent
 | 
				
			||||||
 | 
					                    if existingEventsDict.keys.contains(newUID) {
 | 
				
			||||||
 | 
					                        evt = existingEventsDict[newUID]!
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        evt = RaPlaEvent(context: PersistenceController.shared.context)
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        // Set default values for new object
 | 
				
			||||||
 | 
					                        evt.isHidden = false
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Populate fields
 | 
				
			||||||
 | 
					                    // (offsetAmount * iteration) because for the 1st event, we dont want to add an offset, and
 | 
				
			||||||
 | 
					                    // for every event after that we want to add e.g. 1 week, 2 weeks, 3 weeks etc.
 | 
				
			||||||
 | 
					                    evt.startDate = startDate
 | 
				
			||||||
 | 
					                    evt.endDate = endDate
 | 
				
			||||||
 | 
					                    evt.summary = event.summary
 | 
				
			||||||
 | 
					                    evt.descr = event.description
 | 
				
			||||||
 | 
					                    evt.location = event.location
 | 
				
			||||||
 | 
					                    evt.category = event.category
 | 
				
			||||||
 | 
					                    evt.uid = newUID
 | 
				
			||||||
 | 
					                    for lecturer in event.lecturers {
 | 
				
			||||||
 | 
					                        let lect = Lecturer(context: PersistenceController.shared.context)
 | 
				
			||||||
 | 
					                        lect.name = lecturer.name
 | 
				
			||||||
 | 
					                        lect.email = lecturer.email
 | 
				
			||||||
 | 
					                        lect.event = evt
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Add UID to new UIDs list
 | 
				
			||||||
 | 
					                    newEventUIDs.append(newUID)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                evt = RaPlaEvent(context: PersistenceController.shared.context)
 | 
					                // Generate UID
 | 
				
			||||||
 | 
					                let newUID = event.uid + "---0"  // Appending ---0 to distinguish between recurring events
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Set default values for new object
 | 
					                // Create or update existing CoreData object
 | 
				
			||||||
                evt.isHidden = false
 | 
					                let evt: RaPlaEvent
 | 
				
			||||||
            }
 | 
					                if existingEventsDict.keys.contains(newUID) {
 | 
				
			||||||
            evt.startDate = event.startDate
 | 
					                    evt = existingEventsDict[newUID]!
 | 
				
			||||||
            evt.endDate = event.endDate
 | 
					                } else {
 | 
				
			||||||
            evt.summary = event.summary
 | 
					                    evt = RaPlaEvent(context: PersistenceController.shared.context)
 | 
				
			||||||
            evt.descr = event.description
 | 
					                    
 | 
				
			||||||
            evt.location = event.location
 | 
					                    // Set default values for new object
 | 
				
			||||||
            evt.category = event.category
 | 
					                    evt.isHidden = false
 | 
				
			||||||
            evt.uid = event.uid
 | 
					                }
 | 
				
			||||||
            for lecturer in event.lecturers {
 | 
					                
 | 
				
			||||||
                let lect = Lecturer(context: PersistenceController.shared.context)
 | 
					                // Populate fields
 | 
				
			||||||
                lect.name = lecturer.name
 | 
					                evt.startDate = event.startDate
 | 
				
			||||||
                lect.email = lecturer.email
 | 
					                evt.endDate = event.endDate
 | 
				
			||||||
                lect.event = evt
 | 
					                evt.summary = event.summary
 | 
				
			||||||
 | 
					                evt.descr = event.description
 | 
				
			||||||
 | 
					                evt.location = event.location
 | 
				
			||||||
 | 
					                evt.category = event.category
 | 
				
			||||||
 | 
					                evt.uid = newUID
 | 
				
			||||||
 | 
					                for lecturer in event.lecturers {
 | 
				
			||||||
 | 
					                    let lect = Lecturer(context: PersistenceController.shared.context)
 | 
				
			||||||
 | 
					                    lect.name = lecturer.name
 | 
				
			||||||
 | 
					                    lect.email = lecturer.email
 | 
				
			||||||
 | 
					                    lect.event = evt
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Add UID to new UIDs list
 | 
				
			||||||
 | 
					                newEventUIDs.append(newUID)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,8 +11,10 @@ import CoreData
 | 
				
			||||||
struct FirstOpeningSettings: View {
 | 
					struct FirstOpeningSettings: View {
 | 
				
			||||||
    @EnvironmentObject var settings: LocalSettings
 | 
					    @EnvironmentObject var settings: LocalSettings
 | 
				
			||||||
    @State private var name = ""
 | 
					    @State private var name = ""
 | 
				
			||||||
 | 
					    @State private var dhbwLocation = ""
 | 
				
			||||||
    @State private var course = ""
 | 
					    @State private var course = ""
 | 
				
			||||||
    @State private var director = ""
 | 
					    @State private var director = ""
 | 
				
			||||||
 | 
					    @State private var raplaLink = ""
 | 
				
			||||||
    @State private var invalidInputName = false
 | 
					    @State private var invalidInputName = false
 | 
				
			||||||
    @State private var invalidInputCourse = false
 | 
					    @State private var invalidInputCourse = false
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					@ -28,10 +30,18 @@ struct FirstOpeningSettings: View {
 | 
				
			||||||
                .frame(minWidth: 200, idealWidth: nil, maxWidth: 500, minHeight: nil, idealHeight: nil, maxHeight: nil, alignment: .center)
 | 
					                .frame(minWidth: 200, idealWidth: nil, maxWidth: 500, minHeight: nil, idealHeight: nil, maxHeight: nil, alignment: .center)
 | 
				
			||||||
                .padding(.horizontal)
 | 
					                .padding(.horizontal)
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					            Picker(selection: self.$dhbwLocation, label: Text("Location")){
 | 
				
			||||||
 | 
					                Text("Karlsruhe")
 | 
				
			||||||
 | 
					                Text("Mannheim")
 | 
				
			||||||
 | 
					                Text("Stuttgart")
 | 
				
			||||||
 | 
					                Text("Mosbach")
 | 
				
			||||||
 | 
					            }.frame(maxWidth: 500, alignment: .center)
 | 
				
			||||||
 | 
					            .pickerStyle(SegmentedPickerStyle())
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            TextField("course".localized(tableName: "General"), text: self.$course)
 | 
					            TextField("course".localized(tableName: "General"), text: self.$course)
 | 
				
			||||||
                .overlay(RoundedRectangle(cornerRadius: 10).stroke(invalidInputCourse ? Color.red : Color.secondary, lineWidth: 1))
 | 
					                .overlay(RoundedRectangle(cornerRadius: 10).stroke(invalidInputCourse ? Color.red : Color.secondary, lineWidth: 1))
 | 
				
			||||||
                .onChange(of: course, perform: { value in
 | 
					                .onChange(of: course, perform: { value in
 | 
				
			||||||
                    self.setDirector()
 | 
					                    self.setCourseInfo()
 | 
				
			||||||
                    self.course = self.course.uppercased()
 | 
					                    self.course = self.course.uppercased()
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                .foregroundColor(invalidInputCourse ? .red : .primary)
 | 
					                .foregroundColor(invalidInputCourse ? .red : .primary)
 | 
				
			||||||
| 
						 | 
					@ -65,11 +75,13 @@ struct FirstOpeningSettings: View {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extension FirstOpeningSettings{
 | 
					extension FirstOpeningSettings{
 | 
				
			||||||
    func setDirector() {
 | 
					    func setCourseInfo() {
 | 
				
			||||||
        if (course == "TINF19B4") {
 | 
					        // TODO: Replace this with some database query or stuff like this to load actual data
 | 
				
			||||||
            director = "Jörn Eisenbiegler"
 | 
					        switch course {
 | 
				
			||||||
        } else {
 | 
					            case "TINF19B4":
 | 
				
			||||||
            director = ""
 | 
					                director = "Jörn Eisenbiegler"
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                director = ""
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,11 @@ struct SettingsMain: View {
 | 
				
			||||||
                        label: {
 | 
					                        label: {
 | 
				
			||||||
                            Text("Acknowledgements")
 | 
					                            Text("Acknowledgements")
 | 
				
			||||||
                        })
 | 
					                        })
 | 
				
			||||||
 | 
					                    NavigationLink(
 | 
				
			||||||
 | 
					                        destination: SettingsPushNotifications(),
 | 
				
			||||||
 | 
					                        label: {
 | 
				
			||||||
 | 
					                            Text("Push Notifications")
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
                    Button(action: {
 | 
					                    Button(action: {
 | 
				
			||||||
                        self.showLogoutConfirmationAlert = true
 | 
					                        self.showLogoutConfirmationAlert = true
 | 
				
			||||||
                    }, label: {
 | 
					                    }, label: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,72 @@
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  SettingsPushNotifications.swift
 | 
				
			||||||
 | 
					//  DHBW-Service
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by Patrick Müller on 16.02.21.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SettingsPushNotifications: View {
 | 
				
			||||||
 | 
					    @State private var notificationsEnabled = false
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        VStack {
 | 
				
			||||||
 | 
					            Toggle(isOn: $notificationsEnabled) {
 | 
				
			||||||
 | 
					                Text("Receive push notifications")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            .frame(maxWidth: 500, alignment: .center)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Button(action: {
 | 
				
			||||||
 | 
					                scheduleTestNotification()
 | 
				
			||||||
 | 
					            }){
 | 
				
			||||||
 | 
					                Text("Test Notification")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .onChange(of: notificationsEnabled) { newVal in
 | 
				
			||||||
 | 
					            if(newVal) {
 | 
				
			||||||
 | 
					                enablePushService()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension SettingsPushNotifications {
 | 
				
			||||||
 | 
					    private func enablePushService() {
 | 
				
			||||||
 | 
					        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { (allowed, error) in
 | 
				
			||||||
 | 
					            //This callback does not trigger on main loop be careful
 | 
				
			||||||
 | 
					            if allowed {
 | 
				
			||||||
 | 
					                print("Allowed")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					                // Get token
 | 
				
			||||||
 | 
					                DispatchQueue.main.async {
 | 
				
			||||||
 | 
					                    UIApplication.shared.registerForRemoteNotifications()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                print("Error")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private func scheduleTestNotification() {
 | 
				
			||||||
 | 
					        let content = UNMutableNotificationContent()
 | 
				
			||||||
 | 
					        content.title = "Upcoming Exam"
 | 
				
			||||||
 | 
					        content.subtitle = "Your exam in theoretical computer science 3 is one week from now."
 | 
				
			||||||
 | 
					        content.sound = UNNotificationSound.default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // show this notification five seconds from now
 | 
				
			||||||
 | 
					        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // choose a random identifier
 | 
				
			||||||
 | 
					        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // add our notification request
 | 
				
			||||||
 | 
					        UNUserNotificationCenter.current().add(request)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SettingsPushNotifications_Previews: PreviewProvider {
 | 
				
			||||||
 | 
					    static var previews: some View {
 | 
				
			||||||
 | 
					        SettingsPushNotifications()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user