Compare commits

..

No commits in common. "06b66a50a59258045cf5ce0aa6a2b0d71048fcee" and "d7f078cc88eb8d8359e38b1e672273322c654bf7" have entirely different histories.

10 changed files with 56 additions and 359 deletions

View File

@ -16,7 +16,6 @@
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 */; };
@ -70,8 +69,6 @@
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>"; };
@ -182,7 +179,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */, CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */,
CD9E3F0E25DC466100C77D10 /* SettingsPushNotifications.swift */,
); );
path = SettingsSubViews; path = SettingsSubViews;
sourceTree = "<group>"; sourceTree = "<group>";
@ -210,7 +206,6 @@
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 */,
@ -480,7 +475,6 @@
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 */,
@ -681,11 +675,8 @@
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;
@ -705,11 +696,8 @@
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;

View File

@ -6,14 +6,12 @@
// //
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()
@ -22,21 +20,3 @@ 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)
}
}

View File

@ -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="20E5172i" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D62" 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,11 +20,10 @@
<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="89"/> <element name="User" positionX="-428.6358032226562" positionY="2.067169189453125" width="128" height="74"/>
</elements> </elements>
</model> </model>

View File

@ -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,18 +90,6 @@ 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

View File

@ -1,8 +0,0 @@
<?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>

View File

@ -20,20 +20,14 @@
<false/> <false/>
</dict> </dict>
</dict> </dict>
</dict> <key>CFBundlePrimaryIcon</key>
<key>CFBundleIcons~ipad</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict> <dict>
<key>Alpaca-Alt-Icon</key> <key>CFBundleIconFiles</key>
<dict> <array>
<key>CFBundleIconFiles</key> <string>dhbw-standard-icon</string>
<array> </array>
<string>alpaca-alt-icon</string> <key>UIPrerenderedIcon</key>
</array> <false/>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict> </dict>
</dict> </dict>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@ -47,9 +41,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>1</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>

View File

@ -10,21 +10,14 @@ 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 {
@ -91,14 +84,7 @@ 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] = ""
} }
} }
@ -158,57 +144,6 @@ 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)
}
} }
} }
@ -221,124 +156,36 @@ 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
} }
// List for new UIDs let newEventUIDs = eventObjects.map{$0.uid}
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
if(event.isRecurring){ let evt: RaPlaEvent
// Create as many events as we need if existingEventsDict.keys.contains(event.uid) {
// If we e.g. need 12 events, we create 0...11 evt = existingEventsDict[event.uid]!
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 {
// Generate UID evt = RaPlaEvent(context: PersistenceController.shared.context)
let newUID = event.uid + "---0" // Appending ---0 to distinguish between recurring events
// Create or update existing CoreData object // Set default values for new object
let evt: RaPlaEvent evt.isHidden = false
if existingEventsDict.keys.contains(newUID) { }
evt = existingEventsDict[newUID]! evt.startDate = event.startDate
} else { evt.endDate = event.endDate
evt = RaPlaEvent(context: PersistenceController.shared.context) evt.summary = event.summary
evt.descr = event.description
// Set default values for new object evt.location = event.location
evt.isHidden = false evt.category = event.category
} evt.uid = event.uid
for lecturer in event.lecturers {
// Populate fields let lect = Lecturer(context: PersistenceController.shared.context)
evt.startDate = event.startDate lect.name = lecturer.name
evt.endDate = event.endDate lect.email = lecturer.email
evt.summary = event.summary lect.event = evt
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)
} }
} }

View File

@ -11,10 +11,8 @@ 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
@ -30,18 +28,10 @@ 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.setCourseInfo() self.setDirector()
self.course = self.course.uppercased() self.course = self.course.uppercased()
}) })
.foregroundColor(invalidInputCourse ? .red : .primary) .foregroundColor(invalidInputCourse ? .red : .primary)
@ -75,13 +65,11 @@ struct FirstOpeningSettings: View {
} }
extension FirstOpeningSettings{ extension FirstOpeningSettings{
func setCourseInfo() { func setDirector() {
// TODO: Replace this with some database query or stuff like this to load actual data if (course == "TINF19B4") {
switch course { director = "Jörn Eisenbiegler"
case "TINF19B4": } else {
director = "Jörn Eisenbiegler" director = ""
default:
director = ""
} }
} }

View File

@ -20,11 +20,6 @@ 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: {

View File

@ -1,72 +0,0 @@
//
// 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()
}
}