├── .github
├── FUNDING.yml
└── workflows
│ └── xcodebuild.yml
├── .gitignore
├── Analytics.xcdatamodeld
└── Analytics.xcdatamodel
│ └── contents
├── CHANGELOG.md
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── Campus-iOS.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved~Stashed changes
└── xcshareddata
│ ├── xcbaselines
│ └── 366F0E9427580CFD0091651D.xcbaseline
│ │ ├── 3439EAFF-1FCF-41B1-96A9-2FD6666B9105.plist
│ │ └── Info.plist
│ └── xcschemes
│ └── Campus-iOS.xcscheme
├── Campus-iOS
├── App.swift
├── Assets.xcassets
│ ├── 3D.imageset
│ │ ├── 3D@2x.png
│ │ ├── 3D@3x.png
│ │ └── Contents.json
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x-1.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x-1.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x-1.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ ├── Icon-App-83.5x83.5@2x.png
│ │ ├── ItunesArtwork@2x.png
│ │ ├── slice1@3x copy 2.png
│ │ ├── slice1@3x copy 3-1.png
│ │ ├── slice1@3x copy 3.png
│ │ ├── slice1@3x copy 4.png
│ │ ├── slice1@3x copy 5.png
│ │ ├── slice1@3x copy 6-1.png
│ │ ├── slice1@3x copy 6.png
│ │ ├── slice1@3x copy 7-1.png
│ │ ├── slice1@3x copy 7.png
│ │ └── slice1@3x copy 8.png
│ ├── Campus
│ │ ├── Contents.json
│ │ ├── campus-freising.imageset
│ │ │ ├── Contents.json
│ │ │ └── campus-freising.jpg
│ │ ├── campus-garching.imageset
│ │ │ ├── Contents.json
│ │ │ └── campus-garching.jpeg
│ │ ├── campus-klinikum.imageset
│ │ │ ├── Contents.json
│ │ │ └── campus-klinikum.jpeg
│ │ ├── campus-olympia.imageset
│ │ │ ├── Contents.json
│ │ │ └── campus-olympia.jpeg
│ │ └── campus-stamm.imageset
│ │ │ ├── Contents.json
│ │ │ └── campus-stamm.jpeg
│ ├── Colors
│ │ ├── Contents.json
│ │ ├── contrastText.colorset
│ │ │ └── Contents.json
│ │ ├── primaryBackground.colorset
│ │ │ └── Contents.json
│ │ ├── primaryText.colorset
│ │ │ └── Contents.json
│ │ ├── secondaryBackground.colorset
│ │ │ └── Contents.json
│ │ ├── tumBlue.colorset
│ │ │ └── Contents.json
│ │ ├── tumBlueWhite.colorset
│ │ │ └── Contents.json
│ │ └── tumBrand.colorset
│ │ │ └── Contents.json
│ ├── Contents.json
│ ├── Error-logo-transparent.imageset
│ │ ├── Contents.json
│ │ └── Error-logo-transparent.png
│ ├── Error-logo.imageset
│ │ ├── 3_Something Went Wrong@2x.svg
│ │ └── Contents.json
│ ├── Logos
│ │ ├── Contents.json
│ │ ├── logo-blue-responsive.imageset
│ │ │ ├── Contents.json
│ │ │ ├── tum-blue-dark.svg
│ │ │ └── tum-blue-light.svg
│ │ ├── logo-blue.imageset
│ │ │ ├── Contents.json
│ │ │ └── TUMLogo_oZ_Vollfl_blau_RGB.png
│ │ ├── logo-rainbow.imageset
│ │ │ ├── Contents.json
│ │ │ └── logo-rainbow.png
│ │ ├── logo-responsive.imageset
│ │ │ ├── Contents.json
│ │ │ ├── logo-black 1.png
│ │ │ ├── logo-black 2.png
│ │ │ ├── logo-black.png
│ │ │ ├── logo-white 1.png
│ │ │ ├── logo-white 2.png
│ │ │ └── logo-white.png
│ │ └── logo-white.imageset
│ │ │ ├── Contents.json
│ │ │ └── TUM2.png
│ ├── darkGray.colorset
│ │ └── Contents.json
│ ├── default.imageset
│ │ ├── Contents.json
│ │ ├── default@2x.png
│ │ └── default@3x.png
│ ├── movie.imageset
│ │ ├── Contents.json
│ │ └── movie-placeholder.jpg
│ ├── outline.imageset
│ │ ├── Contents.json
│ │ ├── outline@2x.png
│ │ └── outline@3x.png
│ ├── placeholder.imageset
│ │ ├── Contents.json
│ │ ├── placeholder@2x.heic
│ │ └── placeholder@3x.heic
│ ├── pride.imageset
│ │ ├── Contents.json
│ │ ├── pride@2x.png
│ │ └── pride@3x.png
│ ├── set-permissions.imageset
│ │ ├── Contents.json
│ │ └── set-permissions.png
│ ├── token_step1.imageset
│ │ ├── Contents.json
│ │ └── token_step1.png
│ ├── token_step2.imageset
│ │ ├── Contents.json
│ │ └── token_step2.png
│ ├── token_step3.imageset
│ │ ├── Contents.json
│ │ └── token_step3.png
│ ├── token_step4.imageset
│ │ ├── Contents.json
│ │ └── token_step4.png
│ ├── token_step5.imageset
│ │ ├── Contents.json
│ │ └── token_step5.png
│ ├── tower.imageset
│ │ ├── Contents.json
│ │ └── image4-94.png
│ ├── tumtower.imageset
│ │ ├── Contents.json
│ │ └── tumtower.png
│ ├── white.imageset
│ │ ├── Contents.json
│ │ ├── white@2x.png
│ │ └── white@3x.png
│ ├── whiteTumBlue.colorset
│ │ └── Contents.json
│ └── widgetColor.colorset
│ │ └── Contents.json
├── Base.lproj
│ ├── Login~.storyboard
│ └── Main.storyboard
├── Base
│ ├── Constants
│ │ ├── APIConstants.swift
│ │ └── Constants.swift
│ ├── Enums
│ │ └── Enums.swift
│ ├── Errors
│ │ ├── AlertErrorHandler.swift
│ │ ├── BackendError.swift
│ │ ├── Environment+Error.swift
│ │ ├── Error+Category.swift
│ │ ├── ErrorCategory.swift
│ │ ├── ErrorEmittingViewModifier.swift
│ │ ├── ErrorHandler.swift
│ │ ├── NetworkingError.swift
│ │ └── View+Error.swift
│ ├── Helpers
│ │ ├── DecoderProtocol.swift
│ │ ├── ImagePicker.swift
│ │ └── XMLSerializer.swift
│ ├── Icons
│ │ ├── 3D@2x.png
│ │ ├── 3D@3x.png
│ │ ├── default@2x.png
│ │ ├── default@3x.png
│ │ ├── outline@2x.png
│ │ ├── outline@3x.png
│ │ ├── pride@2x.png
│ │ ├── pride@3x.png
│ │ ├── white@2x.png
│ │ └── white@3x.png
│ ├── Model
│ │ ├── MockModel.swift
│ │ ├── Model.swift
│ │ └── Persistence.swift
│ ├── Networking
│ │ ├── APIErrors
│ │ │ ├── EatAPIError.swift
│ │ │ ├── MVGAPIError.swift
│ │ │ ├── NavigaTUMAPIError.swift
│ │ │ ├── TUMCabeAPIError.swift
│ │ │ ├── TUMDevAppAPIError.swift
│ │ │ ├── TUMOnlineAPIError.swift
│ │ │ └── TUMSexyAPIError.swift
│ │ ├── APIResponse.swift
│ │ ├── APIs
│ │ │ ├── EatAPI.swift
│ │ │ ├── MVGAPI.swift
│ │ │ ├── MVVDeparturesAPI.swift
│ │ │ ├── NavigaTUMAPI.swift
│ │ │ ├── TUMCabeAPI.swift
│ │ │ ├── TUMDevAppAPI.swift
│ │ │ ├── TUMOnlineAPI.swift
│ │ │ └── TUMSexyAPI.swift
│ │ ├── Cache.swift
│ │ ├── CampusOnlineAPI.swift
│ │ ├── EatAPI.swift
│ │ ├── NetworkingAPI.swift
│ │ ├── Protocols
│ │ │ ├── API.swift
│ │ │ ├── APIError.swift
│ │ │ ├── MainAPI.swift
│ │ │ └── Service.swift
│ │ └── TUMDevAppAPI.swift
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── VideoAssets
│ │ └── token-tutorial.mov
│ └── Views
│ │ ├── Collapsible.swift
│ │ ├── FailedView.swift
│ │ ├── GroupBoxLabelView.swift
│ │ ├── ImageFullScreenView.swift
│ │ ├── LoadingView.swift
│ │ ├── NavigationBarView.swift
│ │ ├── NoDataView.swift
│ │ └── SFSafariViewWrapper.swift
├── CalendarComponent
│ ├── Model
│ │ ├── CalendarEvent+PreviewData.swift
│ │ ├── CalendarEvent.swift
│ │ └── TumCalendarStyle.swift
│ ├── Screen
│ │ └── CalendarScreen.swift
│ ├── Service
│ │ └── CalendarService.swift
│ ├── ViewModel
│ │ └── CalendarViewModel.swift
│ └── Views
│ │ ├── CalendarContentView.swift
│ │ ├── CalendarDisplayView.swift
│ │ ├── CalendarSingleEventView.swift
│ │ └── Widget
│ │ ├── CalendarWidgetEventView.swift
│ │ ├── CalendarWidgetScreen.swift
│ │ └── CalendarWidgetView.swift
├── Campus-iOS.entitlements
├── Campus-iOS
│ ├── Base.lproj
│ │ └── Localizable.strings
│ └── de.lproj
│ │ └── Localizable.strings
├── Crashlytics
│ └── CrashlyticsService.swift
├── DataModels
│ ├── Campus_iOS.xcdatamodeld
│ │ ├── .xccurrentversion
│ │ └── Campus_iOS.xcdatamodel
│ │ │ └── contents
│ ├── DataTypeClassifierV4English.mlmodel
│ ├── DataTypeClassifierV4German.mlmodel
│ └── Old models
│ │ ├── DataTypeClassifierV1.mlmodel
│ │ ├── DataTypeClassifierV2.mlmodel
│ │ └── DataTypeClassifierV3.mlmodel
├── DeparturesComponent
│ ├── Types
│ │ └── Departures.swift
│ ├── View
│ │ ├── DeparturesDetailsRowView.swift
│ │ └── DeparturesDetailsView.swift
│ └── ViewModel
│ │ └── DeparturesWidgetViewModel.swift
├── Design
│ ├── CustomRoundedBorderTextFieldStyle.swift
│ ├── RoundedCorners.swift
│ ├── Theme.swift
│ ├── TrailingIconLabelStyle.swift
│ └── ViewModifiers.swift
├── Extensions
│ ├── Array+Groups.swift
│ ├── Array+Rearrange.swift
│ ├── CLLocation+isInvalid.swift
│ ├── Date+Time.swift
│ ├── Date+daysBetween.swift
│ ├── Extensions.swift
│ ├── NSMutableString+Extensions.swift
│ └── Operators.swift
├── GoogleService-Info.plist
├── GradesComponent
│ ├── Model
│ │ ├── AcademicDegree.swift
│ │ ├── AverageGrade.swift
│ │ ├── Grade+PreviewData.swift
│ │ ├── Grade.swift
│ │ └── Modus.swift
│ ├── Screen
│ │ └── GradesScreen.swift
│ ├── Service
│ │ ├── AverageGradesService.swift
│ │ └── GradesService.swift
│ ├── ViewModel
│ │ ├── GradeColor.swift
│ │ ├── GradesViewModel+ChartData.swift
│ │ ├── GradesViewModel+State.swift
│ │ ├── GradesViewModel.swift
│ │ └── MockGradesViewModel.swift
│ └── Views
│ │ ├── BarChartView.swift
│ │ ├── GlowBorder.swift
│ │ ├── GradeView.swift
│ │ ├── GradesSemesterView.swift
│ │ ├── GradesStudyProgramView.swift
│ │ └── GradesView.swift
├── HomeComponent
│ ├── ContactComponent
│ │ ├── ContactScreen.swift
│ │ └── Views
│ │ │ ├── ContactCardView.swift
│ │ │ └── LinkView.swift
│ ├── HomeScreen.swift
│ ├── WidgetComponent
│ │ └── Views
│ │ │ └── Widget
│ │ │ ├── DeparturesWidgetScreen.swift
│ │ │ └── DeparturesWidgetView.swift
│ └── WidgetScreen.swift
├── Info.plist
├── LectureComponent
│ ├── Model
│ │ ├── Lecture.swift
│ │ └── LectureDetails.swift
│ ├── Screen
│ │ ├── LectureDetailsScreen.swift
│ │ └── LecturesScreen.swift
│ ├── Service
│ │ ├── LectureDetailsService.swift
│ │ └── LecturesService.swift
│ ├── ViewModel
│ │ ├── LectureDetailsViewModel+State.swift
│ │ ├── LectureDetailsViewModel.swift
│ │ ├── LecturesViewModel+State.swift
│ │ └── LecturesViewModel.swift
│ └── Views
│ │ ├── LectureDetailsViews
│ │ ├── LectureDetailsBasicInfoRowView.swift
│ │ ├── LectureDetailsBasicInfoView.swift
│ │ ├── LectureDetailsDetailedInfoRowView.swift
│ │ ├── LectureDetailsDetailedInfoView.swift
│ │ ├── LectureDetailsEventInfoView.swift
│ │ ├── LectureDetailsLinkView.swift
│ │ └── LectureDetailsTitleView.swift
│ │ ├── LectureView.swift
│ │ ├── LecturesDetailView.swift
│ │ └── LecturesView.swift
├── LectureSearchComponent
│ ├── Screen
│ │ └── LectureSearchScreen.swift
│ ├── Service
│ │ └── LectureSearchService.swift
│ ├── View
│ │ └── LectureSearchView.swift
│ └── ViewModel
│ │ └── LectureSearchViewModel.swift
├── LoginComponent
│ ├── Model
│ │ ├── Confirmation.swift
│ │ ├── Credentials.swift
│ │ └── Token.swift
│ ├── Service
│ │ └── AuthenticationHandler.swift
│ ├── ViewModel
│ │ ├── LoginViewModel+LoginState.swift
│ │ ├── LoginViewModel+TokenState.swift
│ │ ├── LoginViewModel.swift
│ │ ├── TokenPermissionsViewModel+PermissionType.swift
│ │ ├── TokenPermissionsViewModel+State.swift
│ │ └── TokenPermissionsViewModel.swift
│ └── Views
│ │ ├── Launch Screen.storyboard
│ │ ├── LoginView.swift
│ │ ├── TUMSplashScreen.swift
│ │ ├── TokenConfirmationView.swift
│ │ └── TokenPermissionsView.swift
├── Map
│ └── MainView
│ │ └── PanelContent.swift
├── MapComponent
│ ├── Model
│ │ └── MapLocation.swift
│ ├── Service
│ │ ├── CafeteriasService.swift
│ │ ├── DishService.swift
│ │ ├── MealPlanService.swift
│ │ └── StudyRoomsService.swift
│ ├── Types
│ │ ├── Cafeterias
│ │ │ ├── Cafeteria+PreviewData.swift
│ │ │ ├── Cafeteria.swift
│ │ │ ├── Dish.swift
│ │ │ ├── DishLabel.swift
│ │ │ ├── MealPlan.swift
│ │ │ ├── MensaCategory.swift
│ │ │ └── MensaMenu.swift
│ │ ├── StudyRooms
│ │ │ ├── RoomImageMapping.swift
│ │ │ ├── StudyRoom.swift
│ │ │ ├── StudyRoomApiResponse+PreviewData.swift
│ │ │ ├── StudyRoomApiResponse.swift
│ │ │ ├── StudyRoomAttribute.swift
│ │ │ └── StudyRoomGroup.swift
│ │ └── TUMLocation.swift
│ ├── View
│ │ ├── AnnotatedMapView.swift
│ │ ├── Cafeterias
│ │ │ ├── CafeteriaView.swift
│ │ │ ├── CafeteriasListView.swift
│ │ │ ├── CafeteriasView.swift
│ │ │ ├── DishView.swift
│ │ │ ├── MealPlanScreen.swift
│ │ │ ├── MealPlanView.swift
│ │ │ ├── MenuView.swift
│ │ │ ├── MenuWeekView.swift
│ │ │ ├── OldCafeteriaComponent
│ │ │ │ ├── DishViewOld.swift
│ │ │ │ └── MenuViewOld.swift
│ │ │ └── Widget
│ │ │ │ ├── CafeteriaWidget.swift
│ │ │ │ ├── CafeteriaWidgetScreen.swift
│ │ │ │ └── MealIngredientsView.swift
│ │ ├── Campuses
│ │ │ ├── AnnotationView.swift
│ │ │ ├── CampusCellView.swift
│ │ │ └── CampusView.swift
│ │ ├── LocationView.swift
│ │ ├── PlacesScreen.swift
│ │ ├── PlacesView.swift
│ │ ├── StudyGroups
│ │ │ ├── StudyRoomGroupListView.swift
│ │ │ ├── StudyRoomGroupView.swift
│ │ │ ├── StudyRoomGroupsView.swift
│ │ │ └── Widget
│ │ │ │ ├── StudyRoomWidgetScreen.swift
│ │ │ │ └── StudyRoomWidgetView.swift
│ │ └── Toolbar.swift
│ └── ViewModel
│ │ ├── AnnotatedMapViewModel.swift
│ │ ├── CafeteriaWidgetViewModel.swift
│ │ ├── DishViewModel.swift
│ │ ├── MapViewModel+State.swift
│ │ ├── MapViewModel.swift
│ │ ├── MealPlanViewModel.swift
│ │ ├── MenuViewModel.swift
│ │ └── StudyRoomWidgetViewModel.swift
├── MoviesComponent
│ ├── Model
│ │ ├── Movie+PreviewData.swift
│ │ └── Movie.swift
│ ├── Screen
│ │ └── MoviesScreen.swift
│ ├── Service
│ │ └── MovieService.swift
│ ├── ViewModel
│ │ ├── MovieViewModel.swift
│ │ └── MoviesViewModel.swift
│ └── Views
│ │ ├── MovieCard.swift
│ │ ├── MovieDetailCellView.swift
│ │ ├── MovieDetailedView.swift
│ │ ├── MovieDetailsBasicInfoRowView.swift
│ │ ├── MovieDetailsBasicInfoView.swift
│ │ ├── MovieDetailsDetailedInfoRowView.swift
│ │ ├── MovieDetailsDetailedInfoView.swift
│ │ ├── MovieView.swift
│ │ ├── MoviesView.swift
│ │ └── Widget
│ │ └── MoviesWidgetView.swift
├── NewsComponent
│ ├── Model
│ │ ├── News+PreviewData.swift
│ │ ├── News.swift
│ │ └── NewsSource.swift
│ ├── NewsScreen
│ │ └── NewsScreen.swift
│ ├── Screen
│ │ └── NewsScreen.swift
│ ├── Service
│ │ └── NewsService.swift
│ ├── ViewModel
│ │ └── NewsViewModel.swift
│ └── Views
│ │ ├── NewsCard.swift
│ │ ├── NewsCardsHorizontalScrollingView.swift
│ │ ├── NewsView.swift
│ │ └── Widget
│ │ └── NewsWidgetView.swift
├── PersonDetailedComponent
│ ├── Entity
│ │ ├── Organization.swift
│ │ ├── PersonDetails.swift
│ │ ├── PhoneExtension.swift
│ │ └── Room.swift
│ ├── Screen
│ │ ├── PersonDetailedScreenSearch.swift
│ │ └── PersonDetailedScreenUser.swift
│ ├── Service
│ │ └── PersonDetailedService.swift
│ ├── View
│ │ ├── AddToContactsView.swift
│ │ ├── PersonDetailedView.swift
│ │ └── PersonDetailedViewUser.swift
│ └── ViewModel
│ │ └── PersonDetailedViewModel.swift
├── PersonSearchComponent
│ ├── Entity
│ │ └── Person.swift
│ ├── Screen
│ │ └── PersonSearchScreen.swift
│ ├── Service
│ │ └── PersonSearchService.swift
│ ├── View
│ │ └── PersonSearchView.swift
│ └── ViewModel
│ │ └── PersonSearchViewModel.swift
├── ProfileComponent
│ ├── Entity
│ │ └── Profile.swift
│ ├── Service
│ │ └── ProfileService.swift
│ ├── View
│ │ ├── ProfileToolbar.swift
│ │ ├── ProfileView.swift
│ │ └── TuitionScreen.swift
│ └── ViewModel
│ │ └── ProfileViewModel.swift
├── RoomFinderComponent
│ ├── Entity
│ │ ├── FoundRoom.swift
│ │ └── RoomFinderLocation.swift
│ ├── Model
│ │ ├── Details
│ │ │ ├── NavigaTumNavigationAdditionalProperties.swift
│ │ │ ├── NavigaTumNavigationCoordinates.swift
│ │ │ ├── NavigaTumNavigationMaps.swift
│ │ │ ├── NavigaTumOverlaysMaps.swift
│ │ │ └── NavigaTumRoomFinderMaps.swift
│ │ ├── NavigaTumNavigationDetails.swift
│ │ ├── NavigaTumNavigationEntity.swift
│ │ ├── NavigaTumNavigationProperty.swift
│ │ ├── NavigaTumOverlayMap.swift
│ │ ├── NavigaTumRoomFinderMap.swift
│ │ └── Search
│ │ │ ├── NavigaTumSearchResponse.swift
│ │ │ └── NavigaTumSearchResponseSection.swift
│ ├── Service
│ │ └── RoomFinderService.swift
│ ├── ViewModel
│ │ ├── NavigaTumDetailsViewModel.swift
│ │ └── NavigaTumViewModel.swift
│ └── Views
│ │ ├── NavigaTumDetailsBaseView.swift
│ │ ├── NavigaTumDetailsView.swift
│ │ ├── NavigaTumListView.swift
│ │ ├── NavigaTumMapImagesView.swift
│ │ ├── NavigaTumMapView.swift
│ │ ├── NavigaTumView.swift
│ │ ├── RoomDetailsScreen.swift
│ │ ├── RoomDetailsView.swift
│ │ └── RoomsView.swift
├── SearchComponent
│ ├── Types and Protocols
│ │ ├── ComparisonToken.swift
│ │ ├── Extensions
│ │ │ ├── String+Keep.swift
│ │ │ └── String+Levenshtein.swift
│ │ ├── GlobalSearch.swift
│ │ ├── SearchError.swift
│ │ ├── SearchState.swift
│ │ └── Searchable.swift
│ ├── ViewModels
│ │ ├── SearchResultViewModel.swift
│ │ └── Searchable ViewModels
│ │ │ ├── CafeteriaSearchResultViewModel.swift
│ │ │ ├── EventSearchResultViewModel.swift
│ │ │ ├── GradeSearchResultViewModel.swift
│ │ │ ├── LectureSearchResultViewModel.swift
│ │ │ ├── MovieSearchResultViewModel.swift
│ │ │ ├── NewsSearchResultViewModel.swift
│ │ │ ├── PersonSearchResultViewModel.swift
│ │ │ ├── RoomFinderSearchResultViewModel.swift
│ │ │ └── StudyRoomSearchResultViewModel.swift
│ └── Views
│ │ ├── Additional Views
│ │ ├── SearchResultBarView.swift
│ │ ├── SearchResultErrorView.swift
│ │ └── SearchResultLoadingView.swift
│ │ ├── Extensions and Custom Views
│ │ ├── ExpandIcon.swift
│ │ └── View+Search.swift
│ │ ├── SearchResultView.swift
│ │ ├── SearchResultViews
│ │ ├── Cafeteria
│ │ │ ├── CafeteriaSearchResultScreen.swift
│ │ │ └── CafeteriaSearchResultView.swift
│ │ ├── Event
│ │ │ ├── EventSearchResultScreen.swift
│ │ │ └── EventSearchResultView.swift
│ │ ├── Grade
│ │ │ ├── GradeSearchResultScreen.swift
│ │ │ └── GradeSearchResultView.swift
│ │ ├── Lecture
│ │ │ ├── LectureSearchResultScreen.swift
│ │ │ └── LectureSearchResultView.swift
│ │ ├── Movie
│ │ │ ├── MovieSearchResultScreen.swift
│ │ │ └── MovieSearchResultView.swift
│ │ ├── News
│ │ │ ├── NewsSearchResultScreen.swift
│ │ │ └── NewsSearchResultView.swift
│ │ ├── Person
│ │ │ ├── PersonSearchResultScreen.swift
│ │ │ └── PersonSearchResultView.swift
│ │ ├── RoomFinder
│ │ │ ├── RoomFinderSearchResultScreen.swift
│ │ │ └── RoomFinderSearchResultView.swift
│ │ └── StudyRoom
│ │ │ ├── StudyRoomSearchResultScreen.swift
│ │ │ └── StudyRoomSearchResultView.swift
│ │ └── SearchView.swift
├── Secrets.xcconfig
├── Styles
│ └── Modifiers
│ │ └── RoundedCorners.swift
├── TUMSexyComponent
│ ├── Model
│ │ └── TUMSexyLink.swift
│ ├── Screen
│ │ └── TUMSexyScreen.swift
│ ├── Service
│ │ └── TUMSexyService.swift
│ ├── ViewModel
│ │ └── TUMSexyViewModel.swift
│ └── Views
│ │ └── TUMSexyView.swift
├── TuitionComponent
│ ├── Model
│ │ └── Tuition.swift
│ └── View
│ │ ├── TuitionCard.swift
│ │ ├── TuitionDetailsView.swift
│ │ ├── TuitionScreen.swift
│ │ └── TuitionView.swift
├── WidgetComponent
│ ├── Recommender
│ │ ├── RecommenderError.swift
│ │ ├── Strategy
│ │ │ ├── LocationStrategy.swift
│ │ │ ├── MLModelDataHandler.swift
│ │ │ ├── SpatioTemporalStrategy.swift
│ │ │ ├── TimeStrategy.swift
│ │ │ └── WidgetRecommenderStrategy.swift
│ │ ├── Widget.swift
│ │ ├── WidgetRecommendation.swift
│ │ └── WidgetRecommender.swift
│ └── View
│ │ └── WidgetLoadingView.swift
└── de.lproj
│ └── Main.strings
├── Campus-iOSTests
└── Campus_iOSTests.swift
├── Campus-iOSUITests
├── Campus_iOSUITests.swift
└── Campus_iOSUITestsLaunchTests.swift
├── LICENSE
├── PULL_REQUEST_TEMPLATE.md
├── README.md
└── default.profraw
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: TUM-Dev
4 | open_collective: tum-dev
5 |
--------------------------------------------------------------------------------
/.github/workflows/xcodebuild.yml:
--------------------------------------------------------------------------------
1 | name: xcodebuild
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: macos-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: actions/cache@v2
17 | with:
18 | path: .cache
19 | key: ${{ runner.os }}-spm-v2-${{ hashFiles('**/Package.resolved') }}
20 | restore-keys: ${{ runner.os }}-spm-v2-
21 | - name: Build
22 | run: xcodebuild -scheme "TUM Campus App" -clonedSourcePackagesDirPath .cache -destination "platform=iOS Simulator,name=iPhone 11 Pro"
23 |
--------------------------------------------------------------------------------
/Analytics.xcdatamodeld/Analytics.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | ## Ehrenamtliche Beitragende
2 | Hier sind alle jene aufgeführt, welche in Ihrer Freizeit sich dem Projekt widmen und auf freiwilliger Basis Beiträge leisten, damit diese App weiter existiert.
3 |
4 | #### Projektmanagement:
5 | + [TUM - Computer Science / F13 Fachgebiet für Betriebssysteme](https://www.os.in.tum.de/startseite/)
6 | + [Prof. Dr. Uwe Baumgarten](https://www.os.in.tum.de/personen/baumgarten/)
7 |
8 | #### Technische Unterstützung:
9 | + Andreas Bernhofer
10 | + Bernhard Blieninger
11 | + [Kordian Bruck](https://github.com/kordianbruck/)
12 |
13 | #### Aktive Entwickler
14 | + [Mathias Quintero](https://github.com/mathiasquintero)
15 | + [Max Muth](https://github.com/mammuth)
16 | + [Tim Gymnich](https://github.com/TG908)
17 |
18 | #### Weitere Entwickler:
19 | + [Lukas Kollmer](https://github.com/lukaskollmer)
20 | + [rodalfus](https://github.com/rodalfus)
21 | + [Larry Zuo](https://github.com/larryzuo)
22 | + [rodalfus](https://github.com/rodalfus)
23 | + [Sandra](https://github.com/melloskitten)
24 |
--------------------------------------------------------------------------------
/Campus-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Campus-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Campus-iOS.xcodeproj/xcshareddata/xcbaselines/366F0E9427580CFD0091651D.xcbaseline/3439EAFF-1FCF-41B1-96A9-2FD6666B9105.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | classNames
6 |
7 | Campus_iOSTests
8 |
9 | testPerformanceExample()
10 |
11 | com.apple.XCTPerformanceMetric_WallClockTime
12 |
13 | baselineAverage
14 | 0.005971
15 | baselineIntegrationDisplayName
16 | Local Baseline
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Campus-iOS.xcodeproj/xcshareddata/xcbaselines/366F0E9427580CFD0091651D.xcbaseline/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | runDestinationsByUUID
6 |
7 | 3439EAFF-1FCF-41B1-96A9-2FD6666B9105
8 |
9 | localComputer
10 |
11 | busSpeedInMHz
12 | 0
13 | cpuCount
14 | 1
15 | cpuKind
16 | Apple M1
17 | cpuSpeedInMHz
18 | 0
19 | logicalCPUCoresPerPackage
20 | 8
21 | modelCode
22 | MacBookAir10,1
23 | physicalCPUCoresPerPackage
24 | 8
25 | platformIdentifier
26 | com.apple.platform.macosx
27 |
28 | targetArchitecture
29 | arm64
30 | targetDevice
31 |
32 | modelCode
33 | iPhone15,2
34 | platformIdentifier
35 | com.apple.platform.iphonesimulator
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/3D.imageset/3D@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/3D.imageset/3D@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/3D.imageset/3D@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/3D.imageset/3D@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/3D.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "3D@2x.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "filename" : "3D@3x.png",
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 2.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3-1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 4.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 5.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6-1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7-1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 8.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-freising.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "campus-freising.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-freising.imageset/campus-freising.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-freising.imageset/campus-freising.jpg
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-garching.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "campus-garching.jpeg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-garching.imageset/campus-garching.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-garching.imageset/campus-garching.jpeg
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-klinikum.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "campus-klinikum.jpeg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-klinikum.imageset/campus-klinikum.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-klinikum.imageset/campus-klinikum.jpeg
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-olympia.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "campus-olympia.jpeg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-olympia.imageset/campus-olympia.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-olympia.imageset/campus-olympia.jpeg
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-stamm.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "campus-stamm.jpeg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Campus/campus-stamm.imageset/campus-stamm.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-stamm.imageset/campus-stamm.jpeg
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/contrastText.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xFF",
9 | "green" : "0xFF",
10 | "red" : "0xFF"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x00",
27 | "green" : "0x00",
28 | "red" : "0x00"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/primaryBackground.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xF7",
9 | "green" : "0xF2",
10 | "red" : "0xF2"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x19",
27 | "green" : "0x19",
28 | "red" : "0x19"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/primaryText.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x00",
9 | "green" : "0x00",
10 | "red" : "0x00"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0xFF",
27 | "green" : "0xFF",
28 | "red" : "0xFF"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/secondaryBackground.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xFF",
9 | "green" : "0xFF",
10 | "red" : "0xFF"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x25",
27 | "green" : "0x25",
28 | "red" : "0x25"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/tumBlue.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xBC",
9 | "green" : "0x64",
10 | "red" : "0x00"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0xB3",
27 | "green" : "0x70",
28 | "red" : "0x30"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/tumBlueWhite.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.741",
9 | "green" : "0.396",
10 | "red" : "0.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "1.000",
27 | "green" : "1.000",
28 | "red" : "1.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Colors/tumBrand.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xB3",
9 | "green" : "0x70",
10 | "red" : "0x30"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0xB3",
27 | "green" : "0x70",
28 | "red" : "0x30"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Error-logo-transparent.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Error-logo-transparent.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Error-logo-transparent.imageset/Error-logo-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Error-logo-transparent.imageset/Error-logo-transparent.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Error-logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "3_Something Went Wrong@2x.svg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-blue-responsive.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "tum-blue-light.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "dark"
13 | }
14 | ],
15 | "filename" : "tum-blue-dark.svg",
16 | "idiom" : "universal",
17 | "scale" : "1x"
18 | },
19 | {
20 | "idiom" : "universal",
21 | "scale" : "2x"
22 | },
23 | {
24 | "appearances" : [
25 | {
26 | "appearance" : "luminosity",
27 | "value" : "dark"
28 | }
29 | ],
30 | "idiom" : "universal",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "universal",
35 | "scale" : "3x"
36 | },
37 | {
38 | "appearances" : [
39 | {
40 | "appearance" : "luminosity",
41 | "value" : "dark"
42 | }
43 | ],
44 | "idiom" : "universal",
45 | "scale" : "3x"
46 | }
47 | ],
48 | "info" : {
49 | "author" : "xcode",
50 | "version" : 1
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-blue-responsive.imageset/tum-blue-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-blue-responsive.imageset/tum-blue-light.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-blue.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "TUMLogo_oZ_Vollfl_blau_RGB.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-blue.imageset/TUMLogo_oZ_Vollfl_blau_RGB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-blue.imageset/TUMLogo_oZ_Vollfl_blau_RGB.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-rainbow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "logo-rainbow.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-rainbow.imageset/logo-rainbow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-rainbow.imageset/logo-rainbow.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "logo-black.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "dark"
13 | }
14 | ],
15 | "filename" : "logo-white.png",
16 | "idiom" : "universal",
17 | "scale" : "1x"
18 | },
19 | {
20 | "filename" : "logo-black 1.png",
21 | "idiom" : "universal",
22 | "scale" : "2x"
23 | },
24 | {
25 | "appearances" : [
26 | {
27 | "appearance" : "luminosity",
28 | "value" : "dark"
29 | }
30 | ],
31 | "filename" : "logo-white 1.png",
32 | "idiom" : "universal",
33 | "scale" : "2x"
34 | },
35 | {
36 | "filename" : "logo-black 2.png",
37 | "idiom" : "universal",
38 | "scale" : "3x"
39 | },
40 | {
41 | "appearances" : [
42 | {
43 | "appearance" : "luminosity",
44 | "value" : "dark"
45 | }
46 | ],
47 | "filename" : "logo-white 2.png",
48 | "idiom" : "universal",
49 | "scale" : "3x"
50 | }
51 | ],
52 | "info" : {
53 | "author" : "xcode",
54 | "version" : 1
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 2.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 2.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-white.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "TUM2.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/Logos/logo-white.imageset/TUM2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-white.imageset/TUM2.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/darkGray.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.288",
9 | "green" : "0.280",
10 | "red" : "0.281"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/default.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "default@2x.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "filename" : "default@3x.png",
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/default.imageset/default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/default.imageset/default@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/default.imageset/default@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/default.imageset/default@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/movie.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "movie-placeholder.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/movie.imageset/movie-placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/movie.imageset/movie-placeholder.jpg
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/outline.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "outline@2x.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "filename" : "outline@3x.png",
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/outline.imageset/outline@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/outline.imageset/outline@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/outline.imageset/outline@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/outline.imageset/outline@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "placeholder@2x.heic",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "filename" : "placeholder@3x.heic",
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@2x.heic:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@2x.heic
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@3x.heic:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@3x.heic
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/pride.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "pride@2x.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "filename" : "pride@3x.png",
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/pride.imageset/pride@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/pride.imageset/pride@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/pride.imageset/pride@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/pride.imageset/pride@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/set-permissions.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "set-permissions.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/set-permissions.imageset/set-permissions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/set-permissions.imageset/set-permissions.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "token_step1.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step1.imageset/token_step1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step1.imageset/token_step1.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "token_step2.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step2.imageset/token_step2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step2.imageset/token_step2.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "token_step3.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step3.imageset/token_step3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step3.imageset/token_step3.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "token_step4.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step4.imageset/token_step4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step4.imageset/token_step4.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "token_step5.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/token_step5.imageset/token_step5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step5.imageset/token_step5.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/tower.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image4-94.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/tumtower.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "tumtower.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/tumtower.imageset/tumtower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/tumtower.imageset/tumtower.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/white.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "white@2x.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "filename" : "white@3x.png",
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/white.imageset/white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/white.imageset/white@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/white.imageset/white@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/white.imageset/white@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/whiteTumBlue.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "1.000",
9 | "green" : "1.000",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.741",
27 | "green" : "0.396",
28 | "red" : "0.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Assets.xcassets/widgetColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "1.000",
9 | "green" : "1.000",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.200",
27 | "green" : "0.200",
28 | "red" : "0.200"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Constants/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | enum Constants {
11 | static let tokenManagementTUMOnlineUrl = URL(string: "https://campus.tum.de/tumonline/ee/ui/ca2/app/desktop/#/pl/ui/$ctx/wbservicesadmin.userTokenManagement?$ctx=")!
12 | }
13 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Errors/BackendError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Errors.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 15.12.21.
6 | //
7 |
8 | enum BackendError: Error {
9 | case network(error: Error)
10 | case AFError(message: String)
11 | case dataSerialization(error: Error)
12 | case jsonSerialization(error: Error)
13 | case xmlSerialization(error: Error)
14 | case objectSerialization(reason: String)
15 | }
16 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Errors/Environment+Error.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Environment+Error.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct ErrorHandlerEnvironmentKey: EnvironmentKey {
12 | static var defaultValue: ErrorHandler = AlertErrorHandler()
13 | }
14 |
15 | extension EnvironmentValues {
16 | var errorHandler: ErrorHandler {
17 | get { self[ErrorHandlerEnvironmentKey.self] }
18 | set { self[ErrorHandlerEnvironmentKey.self] = newValue }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Errors/Error+Category.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Error+Category.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Error {
11 | func resolveCategory() -> ErrorCategory {
12 | guard let categorized = self as? CategorizedError else {
13 | // We could optionally choose to trigger an assertion
14 | // here, if we consider it important that all of our
15 | // errors have categories assigned to them.
16 | return .nonRetryable
17 | }
18 |
19 | return categorized.category
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Errors/ErrorCategory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorCategory.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | enum ErrorCategory {
11 | case nonRetryable
12 | case retryable
13 | case requiresLogout
14 | }
15 |
16 | protocol CategorizedError: Error {
17 | var category: ErrorCategory { get }
18 | }
19 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Errors/ErrorEmittingViewModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorEmittingViewModifier.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct ErrorEmittingViewModifier: ViewModifier {
12 | @EnvironmentObject var customEnvironmentValues: Model
13 | @Environment(\.errorHandler) var handler
14 |
15 | var error: Error?
16 | var retryHandler: () -> Void
17 |
18 | func body(content: Content) -> some View {
19 | handler.handle(error,
20 | in: content,
21 | customEnvironmentValues: customEnvironmentValues,
22 | retryHandler: retryHandler
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Errors/ErrorHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorHandler.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | protocol ErrorHandler {
12 | func handle(
13 | _ error: Error?,
14 | in view: T,
15 | customEnvironmentValues: Model,
16 | retryHandler: @escaping () -> Void
17 | ) -> AnyView
18 | }
19 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Errors/View+Error.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+Error.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | extension View {
12 | func handlingErrors(
13 | using handler: ErrorHandler
14 | ) -> some View {
15 | environment(\.errorHandler, handler)
16 | }
17 | }
18 |
19 | extension View {
20 | func emittingError(
21 | _ error: Error?,
22 | retryHandler: @escaping () -> Void
23 | ) -> some View {
24 | modifier(ErrorEmittingViewModifier(
25 | error: error,
26 | retryHandler: retryHandler
27 | ))
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Helpers/DecoderProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecoderProtocol.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 13.01.22.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 |
11 | protocol DecoderProtocol: AnyObject, DataDecoder {
12 | associatedtype DateDecodingStrategy: DecodingStrategyProtocol
13 | func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable
14 | var userInfo: [CodingUserInfoKey : Any] { get set }
15 | var dateDecodingStrategy: DateDecodingStrategy { get set }
16 | static var contentType: [String] { get }
17 | static func instantiate() -> Self
18 | }
19 |
20 | protocol DecodingStrategyProtocol { }
21 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/3D@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/3D@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/3D@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/3D@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/default@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/default@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/default@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/outline@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/outline@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/outline@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/outline@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/pride@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/pride@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/pride@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/pride@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/white@2x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Icons/white@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/white@3x.png
--------------------------------------------------------------------------------
/Campus-iOS/Base/Model/MockModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MockModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 01.12.21.
6 | //
7 |
8 | import Foundation
9 | import Combine
10 | import SwiftUI
11 |
12 | public class MockModel: Model {
13 |
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIErrors/EatAPIError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EatAPIError.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum EatAPIError: APIError, LocalizedError {
11 | case unknown(String)
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case message
15 | }
16 |
17 | init(from decoder: Decoder) throws {
18 | let container = try decoder.container(keyedBy: CodingKeys.self)
19 | let error = try container.decode(String.self, forKey: .message)
20 |
21 | switch error {
22 | default:
23 | self = .unknown(error)
24 | }
25 | }
26 |
27 | init(message: String) {
28 | self = .unknown(message)
29 | }
30 |
31 | public var errorDescription: String? {
32 | switch self {
33 | case let .unknown(message):
34 | return "\("Unkonw error".localized): \(message)"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIErrors/MVGAPIError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MVGAPIError.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum MVGAPIError: APIError, LocalizedError {
11 | case unknown(String)
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case message
15 | }
16 |
17 | init(from decoder: Decoder) throws {
18 | let container = try decoder.container(keyedBy: CodingKeys.self)
19 | let error = try container.decode(String.self, forKey: .message)
20 |
21 | switch error {
22 | default:
23 | self = .unknown(error)
24 | }
25 | }
26 |
27 | init(message: String) {
28 | self = .unknown(message)
29 | }
30 |
31 | public var errorDescription: String? {
32 | switch self {
33 | case let .unknown(message):
34 | return "\("Unkonw error".localized): \(message)"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIErrors/NavigaTUMAPIError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NAvigaTUMAPIError.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.04.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum NavigaTUMAPIError: APIError, LocalizedError {
11 | case unknown(String)
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case message
15 | }
16 |
17 | init(from decoder: Decoder) throws {
18 | let container = try decoder.container(keyedBy: CodingKeys.self)
19 | let error = try container.decode(String.self, forKey: .message)
20 |
21 | switch error {
22 | default:
23 | self = .unknown(error)
24 | }
25 | }
26 |
27 | init(message: String) {
28 | self = .unknown(message)
29 | }
30 |
31 | public var errorDescription: String? {
32 | switch self {
33 | case let .unknown(message):
34 | return "\("Unkonw error".localized): \(message)"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIErrors/TUMCabeAPIError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMCabeAPIError.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum TUMCabeAPIError: APIError, LocalizedError {
11 | case unknown(String)
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case message
15 | }
16 |
17 | init(from decoder: Decoder) throws {
18 | let container = try decoder.container(keyedBy: CodingKeys.self)
19 | let error = try container.decode(String.self, forKey: .message)
20 |
21 | switch error {
22 | default:
23 | self = .unknown(error)
24 | }
25 | }
26 |
27 | init(message: String) {
28 | self = .unknown(message)
29 | }
30 |
31 | public var errorDescription: String? {
32 | switch self {
33 | case let .unknown(message):
34 | return "\("Unkonw error".localized): \(message)"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIErrors/TUMDevAppAPIError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMDevAppAPI.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum TUMDevAppAPIError: APIError, LocalizedError {
11 | case unknown(String)
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case message
15 | }
16 |
17 | init(from decoder: Decoder) throws {
18 | let container = try decoder.container(keyedBy: CodingKeys.self)
19 | let error = try container.decode(String.self, forKey: .message)
20 |
21 | switch error {
22 | default:
23 | self = .unknown(error)
24 | }
25 | }
26 |
27 | init(message: String) {
28 | self = .unknown(message)
29 | }
30 |
31 | public var errorDescription: String? {
32 | switch self {
33 | case let .unknown(message):
34 | return "\("Unkonw error".localized): \(message)"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIErrors/TUMSexyAPIError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMSexyAPIError.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum TUMSexyAPIError: APIError, LocalizedError {
11 | case unknown(String)
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case message
15 | }
16 |
17 | init(from decoder: Decoder) throws {
18 | let container = try decoder.container(keyedBy: CodingKeys.self)
19 | let error = try container.decode(String.self, forKey: .message)
20 |
21 | switch error {
22 | default:
23 | self = .unknown(error)
24 | }
25 | }
26 |
27 | init(message: String) {
28 | self = .unknown(message)
29 | }
30 |
31 | public var errorDescription: String? {
32 | switch self {
33 | case let .unknown(message):
34 | return "\("Unkonw error".localized): \(message)"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIResponse.swift
3 | // TUM Campus App
4 | //
5 | // Created by Tim Gymnich on 2/27/19.
6 | // Copyright © 2019 TUM. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import FirebaseCrashlytics
11 |
12 | //struct APIResponse: Decodable {
13 | // var response: ResponseType
14 | //
15 | // init(from decoder: Decoder) throws {
16 | // if let error = try? ErrorType(from: decoder) {
17 | // throw error
18 | // } else {
19 | // let response = try ResponseType(from: decoder)
20 | // self.response = response
21 | // }
22 | // }
23 | //}
24 | //
25 | //struct TUMOnlineAPIResponse: Decodable {
26 | // var rows: [T]?
27 | //
28 | // enum CodingKeys: String, CodingKey {
29 | // case rows = "row"
30 | // }
31 | //
32 | // init(from decoder: Decoder) throws {
33 | // let container = try decoder.container(keyedBy: CodingKeys.self)
34 | // self.rows = try container.decode([Throwable].self, forKey: .rows).compactMap {
35 | // do {
36 | // return try $0.result.get()
37 | // }
38 | // catch {
39 | // CrashlyticsService.log(error)
40 | // return nil
41 | // }
42 | // }
43 | // }
44 | //}
45 | //
46 | //struct Throwable: Decodable {
47 | // let result: Result
48 | //
49 | // init(from decoder: Decoder) throws {
50 | // result = Result(catching: { try T(from: decoder) })
51 | // }
52 | //}
53 |
54 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIs/MVVDeparturesAPI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MVVDeparturesAPI.swift
3 | // Campus-iOS
4 | //
5 | // Created by Jakob Paul Körber on 28.02.23.
6 | //
7 |
8 | import Alamofire
9 | import Foundation
10 | import CoreLocation
11 |
12 | struct MVVDeparturesAPI: URLRequestConvertible {
13 |
14 | static let baseURLStringPrefix = "https://efa.mvv-muenchen.de/ng/XML_DM_REQUEST?outputFormat=JSON&language=en&stateless=1&coordOutputFormat=WGS84&type_dm=stop&name_dm="
15 | static let baseURLStringMiddle = "&timeOffset="
16 | static let baseURLStringSufix = "&useRealtime=1&itOptionsActive=1&ptOptionsActive=1&limit=20&mergeDep=1&useAllStops=1&mode=direct"
17 |
18 | let station: String
19 | let walkingTime: Int?
20 |
21 | var method: HTTPMethod {
22 | switch self {
23 | default:
24 | return .get
25 | }
26 | }
27 |
28 | func asURLRequest() throws -> URLRequest {
29 | if let walkingTime {
30 | let url = try ("\(MVVDeparturesAPI.baseURLStringPrefix)\(station)\(MVVDeparturesAPI.baseURLStringMiddle)\(walkingTime)\(MVVDeparturesAPI.baseURLStringSufix)").asURL()
31 | let urlRequest = try URLRequest(url: url, method: method)
32 | return urlRequest
33 | } else {
34 | let url = try ("\(MVVDeparturesAPI.baseURLStringPrefix)\(station)\(MVVDeparturesAPI.baseURLStringSufix)").asURL()
35 | let urlRequest = try URLRequest(url: url, method: method)
36 | return urlRequest
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIs/TUMDevAppAPI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMDevAppAPI.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 |
11 | enum TUMDevAppAPI: API {
12 | case room(roomNr: Int)
13 | case rooms
14 |
15 | static var baseURL: String = "https://www.devapp.it.tum.de/"
16 |
17 | static var baseHeaders: Alamofire.HTTPHeaders = []
18 |
19 | static var error: APIError.Type = TUMDevAppAPIError.self
20 |
21 | var paths: String {
22 | switch self {
23 | case .room, .rooms: return "iris/ris_api.php"
24 | }
25 | }
26 |
27 | var parameters: [String : String] {
28 | switch self {
29 | case .room(roomNr: let roomNr):
30 | return ["format": "json", "raum": String(roomNr)]
31 | case .rooms:
32 | return ["format": "json"]
33 | }
34 | }
35 |
36 | var needsAuth: Bool { false }
37 |
38 | func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
39 | return try JSONDecoder().decode(type, from: data)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/APIs/TUMSexyAPI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMSexyAPI.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 |
11 | enum TUMSexyAPI: API {
12 | case standard
13 |
14 | static var baseURL: String = "https://json.tum.sexy/"
15 |
16 | static var baseHeaders: Alamofire.HTTPHeaders = []
17 |
18 | static var error: APIError.Type = TUMSexyAPIError.self
19 |
20 | var paths: String { "" }
21 |
22 | var parameters: [String : String] { [:] }
23 |
24 | var needsAuth: Bool { false }
25 |
26 | func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
27 | return try JSONDecoder().decode(type, from: data)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/NetworkingAPI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkingAPI.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 22.12.21.
6 | //
7 |
8 | import Foundation
9 | import Combine
10 |
11 | protocol NetworkingAPI {
12 | // Renaming to `DecoderType` as we otherwise have a conflict between the `Decoder` associatedtype of `Decodable` and the `Decoder` associatedtype of `NetworkingAPI`
13 | associatedtype DecoderType: TopLevelDecoder
14 | static var decoder: DecoderType { get }
15 | static var cache: Cache { get }
16 |
17 | static func makeRequest(endpoint: APIConstants, token: String?, forcedRefresh: Bool?) async throws -> T
18 | }
19 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/Protocols/APIError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIErrors.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 19.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol APIError: Error, Decodable {
11 | init(message: String)
12 | }
13 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Networking/Protocols/Service.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ServiceProtocols.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 20.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol ServiceTokenProtocol {
11 | associatedtype T : Decodable
12 | func fetch(token: String, forcedRefresh: Bool) async throws -> [T]
13 | }
14 |
15 | protocol ServiceProtocol {
16 | associatedtype T : Decodable
17 | func fetch(forcedRefresh: Bool) async throws -> [T]
18 | }
19 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/VideoAssets/token-tutorial.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/VideoAssets/token-tutorial.mov
--------------------------------------------------------------------------------
/Campus-iOS/Base/Views/GroupBoxLabelView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GroupBoxLabelView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 24.12.21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct GroupBoxLabelView: View {
11 | var iconName: String
12 | var text: String
13 |
14 | var body: some View {
15 | HStack {
16 | Image(systemName: iconName)
17 | .imageScale(.medium)
18 | .font(.headline.bold())
19 | Text(text)
20 | .font(.headline.bold())
21 | }.foregroundColor(Color("tumBlue"))
22 | }
23 | }
24 |
25 | struct GroupBoxLabelView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | GroupBoxLabelView(iconName: "graduationcap.fill", text: "Wintersemester 2021/22")
28 | }
29 | }
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Views/LoadingView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoadingView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 21.12.21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LoadingView: View {
11 | let text: String
12 | let position: LoadingViewPosition
13 |
14 | init(text: String, position: LoadingViewPosition = .middle) {
15 | self.text = text
16 | self.position = position
17 | }
18 |
19 | var body: some View {
20 | GeometryReader { geo in
21 | VStack {
22 | VStack(spacing: 8) {
23 | ProgressView()
24 | Text(text)
25 | }
26 | }.position(x: geo.size.width/2, y: geo.size.height * position.rawValue)
27 | }
28 | .background(Color.primaryBackground)
29 | }
30 | }
31 |
32 | struct LoadingView_Previews: PreviewProvider {
33 | static var previews: some View {
34 | LoadingView(text: "Fetching Grades")
35 | }
36 | }
37 |
38 | enum LoadingViewPosition: Double {
39 | case middle = 0.5
40 | case middletop = 0.25
41 | }
42 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Views/NoDataView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Mohanned Kandil on 23.10.2023.
6 | //
7 |
8 |
9 | import SwiftUI
10 |
11 | struct NoDataView: View {
12 | let description: String
13 |
14 | init(description: String) {
15 | self.description = description;
16 | }
17 |
18 | var body: some View {
19 | VStack (alignment: .center) {
20 | Text(description.localized)
21 | .multilineTextAlignment(.center)
22 | }
23 | .frame(maxWidth: .infinity, maxHeight: .infinity)
24 | }
25 | }
26 |
27 | struct NoDataView_Previews: PreviewProvider {
28 | static var previews: some View {
29 | NoDataView(description: "No data")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Campus-iOS/Base/Views/SFSafariViewWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SFSafariViewWrapper.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 19.01.22.
6 | //
7 |
8 | import SwiftUI
9 | import SafariServices
10 |
11 | struct SFSafariViewWrapper: UIViewControllerRepresentable {
12 | let url: URL
13 |
14 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController {
15 | return SFSafariViewController(url: url)
16 | }
17 |
18 | func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) {
19 | return
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/CalendarComponent/Service/CalendarService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CalendarService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 20.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol CalendarServiceProtocol {
11 | func fetch(token: String, forcedRefresh: Bool) async throws -> [CalendarEvent]
12 | }
13 |
14 | struct CalendarService: ServiceTokenProtocol, CalendarServiceProtocol {
15 |
16 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [CalendarEvent] {
17 | let response: TUMOnlineAPI.CalendarResponse = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.calendar, token: token, forcedRefresh: forcedRefresh)
18 |
19 | return response.event
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/CalendarComponent/Views/Widget/CalendarWidgetScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CalendarWidgetScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 19.01.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CalendarWidgetScreen: View {
11 |
12 | @StateObject var vm: CalendarViewModel
13 |
14 | var body: some View {
15 | switch self.vm.state {
16 | case .success(data: _):
17 | CalendarWidgetView(vm: self.vm)
18 | case .loading:
19 | ProgressView()
20 | case .failed(error: let error):
21 | EmptyView().onAppear{
22 | print(error)
23 | }
24 | case .na:
25 | EmptyView()
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Campus-iOS/Campus-iOS.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.personal-information.location
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Campus-iOS/Crashlytics/CrashlyticsService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CrashlyticsService.swift
3 | // Campus-iOS
4 | //
5 | // Created by Anton Wyrowski on 28.01.23.
6 | //
7 |
8 | import Foundation
9 | import FirebaseCrashlytics
10 |
11 | class CrashlyticsService {
12 | static private let crashlytics = Crashlytics.crashlytics()
13 |
14 | static func log(_ error: Error) -> Void {
15 | #if !DEBUG
16 | CrashlyticsService.crashlytics.record(error: error)
17 | #endif
18 | }
19 |
20 | static func log(_ value: String) -> Void {
21 | #if !DEBUG
22 | CrashlyticsService.crashlytics.log(value)
23 | #endif
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Campus-iOS/DataModels/Campus_iOS.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | Campus_iOS.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Campus-iOS/DataModels/Campus_iOS.xcdatamodeld/Campus_iOS.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Campus-iOS/DataModels/DataTypeClassifierV4English.mlmodel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/DataTypeClassifierV4English.mlmodel
--------------------------------------------------------------------------------
/Campus-iOS/DataModels/DataTypeClassifierV4German.mlmodel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/DataTypeClassifierV4German.mlmodel
--------------------------------------------------------------------------------
/Campus-iOS/DataModels/Old models/DataTypeClassifierV1.mlmodel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/Old models/DataTypeClassifierV1.mlmodel
--------------------------------------------------------------------------------
/Campus-iOS/DataModels/Old models/DataTypeClassifierV2.mlmodel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/Old models/DataTypeClassifierV2.mlmodel
--------------------------------------------------------------------------------
/Campus-iOS/DataModels/Old models/DataTypeClassifierV3.mlmodel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/Old models/DataTypeClassifierV3.mlmodel
--------------------------------------------------------------------------------
/Campus-iOS/Design/CustomRoundedBorderTextFieldStyle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomRoundedBorderTextFieldStyle.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 09.12.21.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct CustomRoundedTextFieldStyle: TextFieldStyle {
12 | func _body(configuration: TextField) -> some View {
13 | configuration
14 | .padding(5)
15 | .background(RoundedRectangle(cornerRadius: 5).fill(Color(UIColor.systemGray6)))
16 | .overlay(
17 | RoundedRectangle(cornerRadius: 5)
18 | .stroke(lineWidth: 0.5)
19 | .foregroundColor(Color(UIColor.systemGray4))
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Campus-iOS/Design/RoundedCorners.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoundedCorners.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 16.07.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct RoundedCorner: Shape {
12 |
13 | var radius: CGFloat = .infinity
14 | var corners: UIRectCorner = .allCorners
15 |
16 | func path(in rect: CGRect) -> Path {
17 | let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
18 | return Path(path.cgPath)
19 | }
20 | }
21 |
22 | extension View {
23 | func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
24 | clipShape( RoundedCorner(radius: radius, corners: corners) )
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Campus-iOS/Design/Theme.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Theme.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 08.11.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | extension Color {
12 | static let tumBrand = Color("tumBrand")
13 | static let primaryBackground = Color("primaryBackground")
14 | static let secondaryBackground = Color("secondaryBackground")
15 | static let primaryText = Color("primaryText")
16 | static let contrastText = Color("contrastText")
17 | static var highlightText = Color("tumBlue")
18 | static var widget = Color("widgetColor")
19 | }
20 |
21 | extension UIColor {
22 | static let tumBlue = UIColor(red: 0, green: 101/255, blue: 189/255, alpha: 1)
23 | static let primaryBackground = UIColor(Color("primaryBackground"))
24 | static let secondaryBackground = UIColor(Color("secondaryBackground"))
25 | static let primaryText = UIColor(Color("primaryText"))
26 | static let contrastText = UIColor(Color("contrastText"))
27 | }
28 |
29 | struct Radius {
30 | static let regular = CGFloat(10)
31 | }
32 |
33 | struct Size {
34 | static let cardWidth = UIScreen.main.bounds.size.width * 0.9
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Campus-iOS/Design/TrailingIconLabelStyle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrailingIconLabelStyle.swift
3 | // Campus-iOS
4 | //
5 | // Created by Jakob Paul Körber on 28.02.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct TrailingIconLabelStyle: LabelStyle {
11 | func makeBody(configuration: Configuration) -> some View {
12 | HStack(alignment: .center) {
13 | configuration.title
14 | configuration.icon
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/Design/ViewModifiers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewModifiers.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 17.01.23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct Title: ViewModifier {
12 | func body(content: Content) -> some View {
13 | HStack {
14 | content
15 | .font(.headline.bold())
16 | .foregroundColor(Color.highlightText)
17 | Spacer()
18 | }
19 | .padding(.leading)
20 | .padding(.bottom, 10)
21 | }
22 | }
23 |
24 | struct ListSection: ViewModifier {
25 | func body(content: Content) -> some View {
26 | content
27 | .padding()
28 | .frame(width: Size.cardWidth)
29 | .background(Color.secondaryBackground)
30 | .clipShape(RoundedRectangle(cornerRadius: Radius.regular))
31 | }
32 | }
33 |
34 | struct ScrollableCardsViewModifier: ViewModifier { //only used for news credit: Milen Vitanov
35 | func body(content: Content) -> some View {
36 | content
37 | .background(Color.blue.opacity(0.05))
38 | .cornerRadius(20)
39 | .shadow(color: Color.black.opacity(0.2), radius: 20, x: 0, y: 0)
40 | .padding(10)
41 | }
42 | }
43 |
44 | extension View {
45 | func titleStyle() -> some View {
46 | modifier(Title())
47 | }
48 | func sectionStyle() -> some View {
49 | modifier(ListSection())
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Campus-iOS/Extensions/Array+Groups.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+Groups.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 27.09.22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Array {
11 | func groups(where predicate: (Element, Element) -> Bool) -> [[Element]] {
12 |
13 | var result: [[Element]] = []
14 |
15 | if self.isEmpty {
16 | return []
17 | }
18 |
19 | for i in 0.. Bool {
13 | let lat = coordinate.latitude
14 | let lon = coordinate.longitude
15 | return lat < -90 || lat > 90 || lon < -180 || lon > 180
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/Extensions/Date+daysBetween.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date+daysBetween.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 28.09.22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Date{
11 | static func daysBetween(_ date1: Date, _ date2: Date) -> Int {
12 | return abs(Calendar.current.dateComponents([.day], from: date1, to: date2).day!)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Campus-iOS/Extensions/Operators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Operators.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 07.09.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | // Source: https://stackoverflow.com/a/61733134
12 | prefix func ! (value: Binding) -> Binding {
13 | Binding(
14 | get: { !value.wrappedValue },
15 | set: { value.wrappedValue = !$0 }
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 944892355389-qfnnv8c4344dk8ka4904ue35rclf3ipg.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.944892355389-qfnnv8c4344dk8ka4904ue35rclf3ipg
9 | ANDROID_CLIENT_ID
10 | 944892355389-jat7kf35dqlvc2uvlsh3vbuq8ge6s6s5.apps.googleusercontent.com
11 | API_KEY
12 | AIzaSyAcgSYu70ITyg2JyUc26038zhGH1Klo3cg
13 | GCM_SENDER_ID
14 | 944892355389
15 | PLIST_VERSION
16 | 1
17 | BUNDLE_ID
18 | de.tum.tca
19 | PROJECT_ID
20 | tca-backend-0001
21 | STORAGE_BUCKET
22 | tca-backend-0001.appspot.com
23 | IS_ADS_ENABLED
24 |
25 | IS_ANALYTICS_ENABLED
26 |
27 | IS_APPINVITE_ENABLED
28 |
29 | IS_GCM_ENABLED
30 |
31 | IS_SIGNIN_ENABLED
32 |
33 | GOOGLE_APP_ID
34 | 1:944892355389:ios:70b9e0e71c71af4b52db54
35 | DATABASE_URL
36 | https://tca-backend-0001.firebaseio.com
37 |
38 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Model/AcademicDegree.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AcademicDegree.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 19.03.22.
6 | //
7 |
8 | import Foundation
9 |
10 | enum AcademicDegree: String {
11 | case PhD = "Doctor of Philosophy"
12 | case BE = "Bachelor of Education"
13 | case ME = "Master of Education"
14 | case BSc = "Bachelor of Science"
15 | case MSc = "Master of Science"
16 | case MBA = "Master of Business Administration"
17 | case BA = "Bachelor of Arts"
18 | case MA = "Master of Arts"
19 | case MBD = "Master Brewer Diploma"
20 | case BECE = "Bachelor of Engineering in Chemical Engineering"
21 | case BEEDE = "Bachelor of Engineering in Electronics and Data Engineering"
22 | case unknown = ""
23 |
24 | var short: String {
25 | switch self {
26 | case .PhD: return "Ph.D."
27 | case .BE: return "BEd."
28 | case .ME: return "MEd."
29 | case .BSc: return "BSc."
30 | case .MSc: return "MSc."
31 | case .MBA: return "MBA"
32 | case .BA: return "B.A."
33 | case .MA: return "M.A."
34 | case .MBD: return "M.B.D."
35 | case .BECE: return "B.Eng. ChE."
36 | case .BEEDE: return "B.Eng. EDE."
37 | case .unknown: return ""
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Model/AverageGrade.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AverageGrade.swift
3 | // Campus-iOS
4 | //
5 | // Created by Anton Wyrowski on 08.05.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct AverageGradeStudien: Decodable {
11 | let studium: [AverageGrade]
12 | }
13 |
14 | struct AverageGrade: Decodable, Identifiable {
15 | public var id: String {
16 | "\(studyId)-\(averageGradeWeightedByCredits)"
17 | }
18 | public var studyId: String
19 | public var studyDesignation: String
20 | public var averageGradeWeightedByCredits: String
21 |
22 | public var averageGradeRounded: String {
23 | let doubleValue = Double(averageGradeWeightedByCredits.replacingOccurrences(of: ",", with: ".")) ?? 0.0
24 | return String(format: "%.2f", doubleValue)
25 | }
26 |
27 | enum CodingKeys: String, CodingKey {
28 | case studyId = "studidf"
29 | case studyDesignation = "studbez"
30 | case averageGradeWeightedByCredits = "avg_grade_weighted_by_credits"
31 | }
32 | }
33 |
34 | extension AverageGrade {
35 | static let dummyData: [AverageGrade] = [
36 | AverageGrade(studyId: "1630 17 030", studyDesignation: "Informatik", averageGradeWeightedByCredits: "1,777")
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Model/Modus.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Modus.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 24.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | // Not currently is use, as we don't know if this list is exhaustive
11 | enum Modus: String, Decodable {
12 | case written = "Schriftlich"
13 | case graded = "Beurteilt/immanenter Prüfungscharakter"
14 | case wirrtenAndVerbal = "Schriftlich und Mündlich"
15 | case verbal = "Mündlich"
16 |
17 | var short: String {
18 | switch self {
19 | case .written: return "Schriftlich"
20 | case .graded: return "Beurteilt"
21 | case .wirrtenAndVerbal: return "Schriftlich/Mündlich"
22 | case .verbal: return "Mündlich"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Service/AverageGradesService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AverageGradeService.swift
3 | // Campus-iOS
4 | //
5 | // Created by Anton Wyrowski on 08.05.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct AverageGradesService: ServiceTokenProtocol {
11 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [AverageGrade] {
12 | let response: TUMOnlineAPI.AverageGradesResponse = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.averageGrades, token: token, forcedRefresh: forcedRefresh)
13 |
14 | return response.studium
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Service/GradesService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GradesService.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 21.12.21.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 |
11 | protocol GradesServiceProtocol {
12 | func fetch(token: String, forcedRefresh: Bool) async throws -> [Grade]
13 | }
14 |
15 | typealias GradesSemesterDegrees = [(String, [(String, [Grade])])]
16 |
17 | struct GradesService: ServiceTokenProtocol, GradesServiceProtocol {
18 |
19 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [Grade] {
20 | let response: TUMOnlineAPI.Response = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.personalGrades, token: token, forcedRefresh: forcedRefresh)
21 |
22 | return response.row
23 | .sorted { gradeA, gradeB in
24 | return gradeA.date > gradeB.date
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/ViewModel/GradesViewModel+State.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GradesViewModel+State.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 25.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | //extension GradesViewModel {
11 | // enum State {
12 | // case na
13 | // case loading
14 | // case success(data: [Grade])
15 | // case failed(error: Error)
16 | // }
17 | //}
18 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/ViewModel/MockGradesViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MockGradesViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 03.05.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUICharts
10 |
11 | class MockGradesViewModel: GradesViewModel {
12 |
13 | let dummyGradesByDegreeAndSemester: [(String, [(String, [Grade])])] = [
14 | ("1630 17 030", [("Wintersemester 2021/22", Grade.dummyData21W)]),
15 | ("1630 17 030", [("Sommersemester 2021", Grade.dummyData21S)]),
16 | ("1630 17 030", [("Wintersemester 2020/21", Grade.dummyData20W)])
17 | ]
18 |
19 | override init(model: Model, gradesService: GradesService, averageGradesService: AverageGradesService) {
20 | super.init(model: model, gradesService: gradesService, averageGradesService: AverageGradesService())
21 |
22 | self.gradesState = .success(data: Grade.dummyDataAll)
23 | self.averageGradesState = .success(data: AverageGrade.dummyData)
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Views/BarChartView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BarChartView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 24.12.21.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftUICharts
10 |
11 | struct BarChartView: View {
12 | var barChartData: BarChartData
13 |
14 | var body: some View {
15 | BarChart(chartData: barChartData)
16 | .xAxisGrid(chartData: barChartData)
17 | .xAxisLabels(chartData: barChartData)
18 | .yAxisLabels(chartData: barChartData)
19 | .frame(height: UIScreen.main.bounds.size.height/5, alignment: .center)
20 | .padding(.top, 12)
21 | }
22 | }
23 |
24 | struct BarChartView_Previews: PreviewProvider {
25 | static var previews: some View {
26 | BarChartView(
27 | barChartData:
28 | .init(
29 | dataSets: .init(dataPoints: [])
30 | )
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Views/GlowBorder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GlowBorder.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 19.03.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct GlowBorder: ViewModifier {
11 | var color: Color
12 | var lineWidth: Int
13 |
14 | func body(content: Content) -> some View {
15 | applyShadow(content: AnyView(content), lineWidth: lineWidth)
16 | }
17 |
18 | func applyShadow(content: AnyView, lineWidth: Int) -> AnyView {
19 | if lineWidth == 0 {
20 | return content
21 | } else {
22 | return applyShadow(content: AnyView(content.shadow(color: color, radius: 1)), lineWidth: lineWidth - 1)
23 | }
24 | }
25 | }
26 |
27 | extension View {
28 | func glowBorder(color: Color, lineWidth: Int) -> some View {
29 | self.modifier(GlowBorder(color: color, lineWidth: lineWidth))
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Views/GradesSemesterView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GradesSemesterView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Anton Wyrowski on 23.05.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct GradesSemesterView: View {
11 | let semesterName: String
12 | let grades: [Grade]
13 |
14 | var body: some View {
15 | Section(header: Text(semesterName)
16 | .font(.headline.bold())
17 | .foregroundColor(Color("tumBlue"))
18 | .accessibilityHeading(.h2)
19 | ) {
20 | ForEach(grades) { item in
21 | VStack {
22 | GradeView(grade: item)
23 |
24 | if let id = grades.last?.id {
25 | if item.id != id {
26 | Divider()
27 | }
28 | }
29 | }
30 | }
31 | .listRowInsets(
32 | EdgeInsets(
33 | top: 4,
34 | leading: 18,
35 | bottom: 2,
36 | trailing: 18
37 | )
38 | )
39 | }
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/Campus-iOS/GradesComponent/Views/GradesView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GradesView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 24.12.21.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftUICharts
10 |
11 | @available(iOS 16.0, *)
12 | struct GradesView: View {
13 |
14 | @StateObject var vm: GradesViewModel
15 |
16 | var body: some View {
17 | let gradesWithAverage = self.vm.gradesByDegreeAndSemesterWithAverageGrade
18 |
19 | return List {
20 | ForEach(gradesWithAverage.indices, id: \.self) { index in
21 | GradesStudyProgramView(semesterGrades: gradesWithAverage[index], studyProgram: self.vm.getStudyProgram(studyID: gradesWithAverage[index].degree), barChartData: vm.barChartData.count > index ? vm.barChartData[index] : BarChartData(dataSets: BarDataSet(dataPoints: [])))
22 | }
23 | .listRowBackground(Color.secondaryBackground)
24 | }
25 | .background(Color.primaryBackground)
26 | .scrollContentBackground(.hidden)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Campus-iOS/HomeComponent/ContactComponent/ContactScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContactView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 30.12.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @available(iOS 16.0, *)
11 | struct ContactScreen: View {
12 |
13 | @StateObject var model: Model
14 | @StateObject var gradesViewModel: GradesViewModel //provides studyprogram info
15 | @StateObject var profileVm: ProfileViewModel
16 | let profile: Profile
17 |
18 | init (model: Model, profileVm: ProfileViewModel, profile: Profile) {
19 | self._model = StateObject(wrappedValue: model)
20 | self._gradesViewModel = StateObject(wrappedValue: GradesViewModel(model: model, gradesService: GradesService(), averageGradesService: AverageGradesService()))
21 | self._profileVm = StateObject(wrappedValue: profileVm)
22 | self.profile = profile
23 | }
24 |
25 | var body: some View {
26 | VStack(spacing: 0) {
27 | ContactCardView(model: self.model, profile: profile, profileVm: self.profileVm, gradesVm: self.gradesViewModel)
28 | .padding(.bottom, 10)
29 |
30 |
31 | TuitionScreen(vm: self.profileVm)
32 | .padding(.bottom, 10)
33 |
34 | LinkView()
35 |
36 | }.task {
37 | await gradesViewModel.getGrades(forcedRefresh: true)
38 | await gradesViewModel.getAverageGrades(forcedRefresh: true)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Campus-iOS/HomeComponent/WidgetComponent/Views/Widget/DeparturesWidgetScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DeparturesWidgetScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by Jakob Paul Körber on 01.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct DeparturesWidgetScreen: View {
11 |
12 | @StateObject var departuresViewModel: DeparturesWidgetViewModel
13 | @State var showDetailsSheet = false
14 |
15 | var body: some View {
16 | Group {
17 | switch(self.departuresViewModel.state) {
18 | case .success, .failed, .loading:
19 | DeparturesWidgetView(departuresViewModel: departuresViewModel, showDetailsSheet: $showDetailsSheet)
20 | case .noLocation:
21 | EmptyView()
22 | }
23 | }
24 | .background(Color("primaryBackground"))
25 | .onAppear {
26 | departuresViewModel.timer = Timer()
27 | departuresViewModel.setTimerForRefetch()
28 | }
29 | .onDisappear {
30 | if !showDetailsSheet {
31 | departuresViewModel.timer?.invalidate()
32 | }
33 | }
34 | .onTapGesture {
35 | showDetailsSheet.toggle()
36 | }
37 | }
38 | }
39 |
40 | struct DeparturesWidgetScreen_Previews: PreviewProvider {
41 | static var previews: some View {
42 | DeparturesWidgetScreen(departuresViewModel: DeparturesWidgetViewModel())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/Screen/LectureDetailsScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureDetailsScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 25.12.21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LectureDetailsScreen: View {
11 | @StateObject var vm: LectureDetailsViewModel
12 |
13 | init(model: Model, lecture: Lecture) {
14 | self._vm = StateObject(wrappedValue:
15 | LectureDetailsViewModel(
16 | model: model,
17 | service: LectureDetailsService(),
18 | lecture: lecture
19 | )
20 | )
21 | }
22 |
23 | var body: some View {
24 | Group {
25 | switch vm.state {
26 | case .success(let data):
27 | LecturesDetailView(viewModel: vm, lectureDetails: data)
28 | case .loading, .na:
29 | LoadingView(text: "Fetching details of lecture".localized)
30 | case .failed(let error):
31 | FailedView(
32 | errorDescription: error.localizedDescription,
33 | retryClosure: vm.getLectureDetails
34 | )
35 | }
36 | }
37 | .task {
38 | await vm.getLectureDetails()
39 | }
40 | }
41 | }
42 |
43 | struct LectureDetailScreen_Previews: PreviewProvider {
44 | static var previews: some View {
45 | LectureDetailsScreen(
46 | model: MockModel(), lecture: Lecture.dummyData.first!
47 | )
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/Service/LectureDetailsService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureDetailsService.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 25.12.21.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 | import XMLCoder
11 |
12 | protocol LectureDetailsServiceProtocol {
13 | func fetch(token: String, lvNr: UInt64, forcedRefresh: Bool) async throws -> LectureDetails
14 | }
15 |
16 | struct LectureDetailsService: LectureDetailsServiceProtocol {
17 | func fetch(token: String, lvNr: UInt64, forcedRefresh: Bool = false) async throws -> LectureDetails {
18 | let response: TUMOnlineAPI.Response =
19 | try await
20 | MainAPI
21 | .makeRequest(
22 | endpoint: TUMOnlineAPI.lectureDetails(lvNr: String(lvNr)),
23 | token: token,
24 | forcedRefresh: forcedRefresh
25 | )
26 |
27 | guard let data = response.row.first else {
28 | throw NetworkingError.resourceNotFound
29 | }
30 |
31 | return data
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/Service/LecturesService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GradesService.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 21.12.21.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 | import XMLCoder
11 |
12 | protocol LecturesServiceProtocol {
13 | func fetch(token: String, forcedRefresh: Bool) async throws -> [Lecture]
14 | }
15 |
16 | struct LecturesService: LecturesServiceProtocol {
17 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [Lecture] {
18 | let response: TUMOnlineAPI.Response =
19 | try await
20 | MainAPI.makeRequest(endpoint: TUMOnlineAPI.personalLectures, token: token, forcedRefresh: forcedRefresh)
21 |
22 | return response.row
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/ViewModel/LectureDetailsViewModel+State.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureDetailsViewModel+State.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 25.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | extension LectureDetailsViewModel {
11 | enum State {
12 | case na
13 | case loading
14 | case success(data: LectureDetails)
15 | case failed(error: Error)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/ViewModel/LecturesViewModel+State.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LecturesViewModel+State.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 25.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | extension LecturesViewModel {
11 | enum State {
12 | case na
13 | case loading
14 | case success(data: [Lecture])
15 | case failed(error: Error)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsBasicInfoRowView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureDetailsBasicInfoRowView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 26.12.21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LectureDetailsBasicInfoRowView: View {
11 | var iconName: String
12 | var text: String
13 |
14 | var body: some View {
15 | HStack(alignment: .center, spacing: 12) {
16 | Image(systemName: iconName)
17 | .imageScale(.medium)
18 | .frame(width: 25, height: 25, alignment: .center)
19 | Text(text)
20 | .font(.system(size: 16))
21 | .multilineTextAlignment(.leading)
22 | }
23 | }
24 | }
25 |
26 | struct LectureDetailsRowView_Previews: PreviewProvider {
27 | static var previews: some View {
28 | LectureDetailsBasicInfoRowView(
29 | iconName: "number",
30 | text: "LOOOOOOOOOOOOOOOOOOONG (1234.01.001)"
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsDetailedInfoRowView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureDetailsDetailedInfoRowView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 26.12.21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LectureDetailsDetailedInfoRowView: View {
11 | var title: String
12 | var text: String
13 |
14 | var body: some View {
15 | VStack(alignment: .leading, spacing: 8) {
16 | Text(title)
17 | .font(.system(size: 16, weight: .semibold))
18 |
19 | Text(text)
20 | .font(.system(size: 16))
21 | }
22 | }
23 | }
24 |
25 | struct LectureDetailsDetailedInfoRowView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | LectureDetailsDetailedInfoRowView(
28 | title: "number",
29 | text: "test"
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsTitleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureDetailsTitleView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 25.12.21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LectureDetailsTitleView: View {
11 | var lectureDetails: LectureDetails
12 |
13 | var body: some View {
14 | VStack(alignment: .leading, spacing: 5) {
15 | Text(lectureDetails.title)
16 | .font(.title)
17 | .multilineTextAlignment(.leading)
18 | Text(lectureDetails.eventType)
19 | .font(.subheadline)
20 | }
21 | }
22 | }
23 |
24 | struct LectureDetailTitleView_Previews: PreviewProvider {
25 | static var previews: some View {
26 | LectureDetailsTitleView(lectureDetails: LectureDetails.dummyData)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureSearchComponent/Service/LectureSearchService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureSearchService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 20.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct LectureSearchService {
11 | func fetch(for query: String, token: String, forcedRefresh: Bool) async throws -> [Lecture] {
12 | let response : TUMOnlineAPI.Response = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.lectureSearch(search: query), token: token, forcedRefresh: forcedRefresh)
13 |
14 | return response.row
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/LectureSearchComponent/View/LectureSearchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureSearchListView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 13.02.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LectureSearchView: View {
11 | let model: Model
12 | let lectures: [Lecture]
13 |
14 | var body: some View {
15 | List {
16 | ForEach(lectures) { lecture in
17 | NavigationLink(
18 | destination: LectureDetailsScreen(model: self.model, lecture: lecture)
19 | .navigationBarTitleDisplayMode(.inline)
20 | ) {
21 | HStack {
22 | Text(lecture.title)
23 | Spacer()
24 | Text(lecture.eventType)
25 | .foregroundColor(Color(.secondaryLabel))
26 | }
27 | }
28 | }
29 | }
30 | }
31 | }
32 |
33 | struct LectureSearchListView_Previews: PreviewProvider {
34 | static var previews: some View {
35 | LectureSearchView(model: Model(), lectures: [])
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/LoginComponent/Model/Confirmation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Confirmation.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 21.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Confirmation: Decodable {
11 | let value: Bool
12 |
13 | private enum CodingKeys: String, CodingKey {
14 | case value = ""
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/LoginComponent/Model/Token.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Token.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 21.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Token: Decodable {
11 | let value: String
12 |
13 | private enum CodingKeys: String, CodingKey {
14 | case value = ""
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/LoginComponent/ViewModel/LoginViewModel+LoginState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewModel+LoginState.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 17.10.22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension LoginViewModel {
11 | enum LoginState {
12 | case notChecked
13 | case logInError
14 | case loggedIn
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/LoginComponent/ViewModel/LoginViewModel+TokenState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewModel+TokenState.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 17.10.22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension LoginViewModel {
11 | enum TokenState {
12 | case notChecked
13 | case inactive
14 | case active
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+PermissionType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TokenPermissionsViewModel+PermissionType.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 17.10.22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension TokenPermissionsViewModel {
11 | enum PermissionType: String {
12 | case grades = "Grades"
13 | case calendar = "Calendar"
14 | case lectures = "Lectures"
15 | case tuitionFees = "Tuition Fees"
16 | case identification = "Identification (TUM ID and name)"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+State.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TokenPermissionsViewModel+State.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 17.10.22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension TokenPermissionsViewModel {
11 | enum State {
12 | case na
13 | case loading
14 | case success(data: Any?)
15 | case failed(error: Error)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Model/MapLocation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapLocation.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 08.07.22.
6 | //
7 |
8 | import Foundation
9 | import MapKit
10 |
11 | struct MapLocation: Identifiable {
12 | let id = UUID()
13 | let coordinate: CLLocationCoordinate2D
14 | }
15 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Service/DishService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DishService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 23.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct DishService {
11 | func fetch(forcedRefresh: Bool) async -> [String: DishLabel]? {
12 | do {
13 | let response: [DishLabel] = try await MainAPI.makeRequest(endpoint: EatAPI.labels, forcedRefresh: forcedRefresh)
14 | var labels = [String: DishLabel]()
15 | for dishLabel in response {
16 | labels[dishLabel.name] = dishLabel
17 | }
18 |
19 | return labels
20 | } catch {
21 | print(error)
22 | return nil
23 | // No error is thrown, since the labels, can still be displayed, but just as text, instead of an emoji.
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Service/StudyRoomsService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StudyRoomsService.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 09.06.22.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol StudyRoomsServiceProtocol {
11 | func fetch(forcedRefresh: Bool) async throws -> StudyRoomApiRespose
12 | }
13 |
14 | struct StudyRoomsService: StudyRoomsServiceProtocol {
15 | func fetch(forcedRefresh: Bool) async throws -> StudyRoomApiRespose {
16 | let response: StudyRoomApiRespose = try await MainAPI.makeRequest(endpoint: TUMDevAppAPI.rooms, forcedRefresh: forcedRefresh)
17 | return response
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/Cafeterias/Cafeteria+PreviewData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Cafeteria+PreviewData.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 05.05.23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Cafeteria {
11 | static let previewData = [
12 | Cafeteria(location: Location(latitude: 48.147420, longitude: 11.567220, address: "Arcisstraße 17, München"), name: "Mensa Arcisstraße", id: "mensa-arcisstr", queueStatusApi: nil, queue: nil),
13 | Cafeteria(location: Location(latitude: 0.0, longitude: 0.0, address: "xystraße, München"), name: "Mensa XY", id: "mensa-xy", queueStatusApi: nil, queue: nil),
14 | // Mensa Garching has an maximum capacity of 1500 people (calculated reversed with the current and percentage of a day, e.g. current = 2 and percent: 0.13333334 then 0.13333334/100 * x = 2 => 2/(13333334/100) = 1500)
15 | Cafeteria(location: Location(latitude: 48.268132, longitude: 11.672263, address: "Boltzmannstraße 19, Garching"), name: "Mensa Garching", id: "mensa-garching", queueStatusApi: Optional("https://mensa.liste.party/api/"), queue: Queue(current: 150, percent: 0.1))
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/Cafeterias/DishLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Label.swift
3 | // Campus-iOS
4 | //
5 | // Created by August Wittgenstein on 14.01.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct DishLabel: Decodable {
11 |
12 | /*
13 | [
14 | {
15 | "enum_name": "VEGETARIAN",
16 | "text": {
17 | "DE": "Vegetarisch",
18 | "EN": "vegetarian"
19 | },
20 | "abbreviation": "🌽"
21 | }
22 | ]
23 | */
24 |
25 | let name: String
26 | let text: [String: String]
27 | let abbreviation: String
28 |
29 | enum CodingKeys: String, CodingKey {
30 | case name = "enum_name"
31 | case text
32 | case abbreviation
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/Cafeterias/MealPlan.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MealPlan.swift
3 | // Campus-iOS
4 | //
5 | // Created by August Wittgenstein on 17.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MealPlan: Decodable {
11 |
12 | /*
13 | {
14 | "number": 10,
15 | "year": 2020,
16 | "days": [...]
17 | }
18 | */
19 |
20 | let week: Int
21 | let year: Int
22 | let days: [MensaMenu]
23 |
24 | enum CodingKeys: String, CodingKey {
25 | case week = "number"
26 | case year
27 | case days
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/Cafeterias/MensaCategory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MensaCategory.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 02.04.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MenuCategory: Identifiable, Decodable {
11 | var id = UUID()
12 | let name: String
13 | let dishes: [Dish]
14 |
15 | init(name: String, dishes: [Dish]) {
16 | self.name = name
17 | self.dishes = dishes
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/Cafeterias/MensaMenu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Menu.swift
3 | // Campus-iOS
4 | //
5 | // Created by August Wittgenstein on 17.12.21.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MensaMenu: Hashable, Decodable {
11 | /*
12 | "date": "2020-03-02",
13 | "dishes": [...]
14 | */
15 |
16 | let date: Date
17 | let dishes: [Dish]
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case date
21 | case dishes
22 | }
23 |
24 | init(from decoder: Decoder) throws {
25 | let container = try decoder.container(keyedBy: CodingKeys.self)
26 |
27 | self.date = try container.decode(Date.self, forKey: .date)
28 | self.dishes = try container.decode([Dish].self, forKey: .dishes)
29 | }
30 | }
31 |
32 | struct Category: Hashable, Decodable {
33 | var name: String
34 | var dishes: [Dish]
35 |
36 | enum CodingKeys: String, CodingKey {
37 | case name
38 | case dishes
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/StudyRooms/RoomImageMapping.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomImageMapping.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 05.06.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct RoomImageMapping: Codable {
11 | /*
12 | {
13 | "map_id": 54,
14 | "description": "München",
15 | "scale": 200000,
16 | "width": 640,
17 | "height": 603
18 | },
19 | */
20 |
21 | let id: Int
22 | let description: String
23 | let scale: Int
24 | let width: Int
25 | let height: Int
26 |
27 | enum CodingKeys: String, CodingKey {
28 | case id = "map_id"
29 | case description
30 | case scale
31 | case width
32 | case height
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/StudyRooms/StudyRoomApiResponse+PreviewData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StudyRoomApiResponse+PreviewData.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 05.05.23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension StudyRoomApiRespose {
11 | static let previewData: StudyRoomApiRespose = StudyRoomApiRespose(
12 | rooms: [
13 | StudyRoom(buildingCode: "5603", buildingName: "FMI/ Bibliothek", buildingNumber: 832, code: "01.03.010", id: 49770, name: "Windfang", number: "010", occupiedBy: "", occupiedFor: 0, occupiedFrom: "", occupiedIn: 0, occupiedUntil: "", raum_nr_architekt: "01.03.010@5603", res_nr: 27052, status: "frei", attributes: []),
14 | StudyRoom(buildingCode: "5603", buildingName: "FMI/ Bibliothek", buildingNumber: 832, code: "01.03.010", id: 58380, name: "Einzelarbeitsraum", number: "043B", occupiedBy: "", occupiedFor: 0, occupiedFrom: "", occupiedIn: 0, occupiedUntil: "", raum_nr_architekt: "01.03.043B@5603", res_nr: 26714, status: "frei", attributes: [])],
15 | groups: [
16 | StudyRoomGroup(detail: "", id: 46, name: "Fachschaftsräume Mathe/Informatik", sorting: 1, rooms: [49770,58380,58372,58363,58354,58346,58227,58217,58211,58203,58195,58187,58068,58059,58050,58041])])
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/Types/StudyRooms/StudyRoomAttribute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StudyRoomAttribute.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 05.05.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct StudyRoomAttribute: Decodable, Searchable {
11 |
12 | var detail: String?
13 | var name: String?
14 |
15 | enum CodingKeys: String, CodingKey {
16 | case detail
17 | case name
18 | }
19 |
20 | init(from decoder: Decoder) throws {
21 | let container = try decoder.container(keyedBy: CodingKeys.self)
22 |
23 | let detail = try container.decode(String.self, forKey: .detail)
24 | let name = try container.decode(String.self, forKey: .name)
25 |
26 | self.detail = detail
27 | self.name = name
28 | }
29 |
30 | var comparisonTokens: [ComparisonToken] {
31 | return [
32 | ComparisonToken(value: detail ?? ""),
33 | ComparisonToken(value: name ?? "")
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/AnnotatedMapView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnnotatedMapView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 06.06.23.
6 | //
7 |
8 | import SwiftUI
9 | import MapKit
10 |
11 | struct AnnotatedMapView: View {
12 |
13 | @StateObject var vm: AnnotatedMapViewModel
14 | @State private var region: MKCoordinateRegion
15 |
16 | internal init(vm: AnnotatedMapViewModel, centerOfMap: CLLocationCoordinate2D, zoomLevel: Double) {
17 | self._vm = StateObject(wrappedValue: vm)
18 | self.region = MKCoordinateRegion(center: centerOfMap, span: MKCoordinateSpan(latitudeDelta: zoomLevel, longitudeDelta: zoomLevel))
19 | }
20 |
21 | var body: some View {
22 | Label("View On Maps", systemImage: "map").titleStyle()
23 | .padding(.top, 20)
24 |
25 | Map(coordinateRegion: $region, showsUserLocation: true, annotationItems: vm.locations) {item in
26 | MapAnnotation(coordinate: item.coordinate) {
27 | AnnotationView(location: item, vm: self.vm)
28 | }
29 | }
30 | .frame(width: Size.cardWidth, height: Size.cardWidth)
31 | .clipShape(RoundedRectangle(cornerRadius: Radius.regular))
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Cafeterias/CafeteriasListView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CafeteriasView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 05.06.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CafeteriasListView: View {
11 |
12 | @StateObject var vm: AnnotatedMapViewModel
13 |
14 | var body: some View {
15 | Group {
16 | Label("Cafeterias", systemImage: "fork.knife").titleStyle()
17 | .padding(.top, 20)
18 | VStack {
19 | if !vm.cafeterias.isEmpty {
20 | ForEach(vm.cafeterias.indices, id: \.self) { index in
21 | CafeteriaView(cafeteria: vm.cafeterias[index], isListItem: true)
22 | if (index != vm.cafeterias.count - 1) {
23 | Divider().padding(.horizontal)
24 | }
25 | }
26 | } else {
27 | Text("Resource not found") //no cafeterias
28 | }
29 | }.sectionStyle()
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Cafeterias/CafeteriasView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CafeteriaViewNEW.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 25.04.23.
6 | //
7 |
8 | import SwiftUI
9 | import MapKit
10 |
11 | struct CafeteriasView: View {
12 | @StateObject var vm: MapViewModel
13 | @State var sortedCafeterias: [Cafeteria]
14 | var vmAnno = AnnotatedMapViewModel()
15 | var locationManager = CLLocationManager()
16 |
17 | init(vm: MapViewModel) {
18 | self._vm = StateObject(wrappedValue: vm)
19 | //sorts cafeterias based on distance from user
20 | if let location = self.locationManager.location {
21 | self.sortedCafeterias = vm.cafeterias.sorted {
22 | $0.coordinate.location.distance(from: location) < $1.coordinate.location.distance(from: location)
23 | }
24 | } else {
25 | self.sortedCafeterias = vm.cafeterias
26 | }
27 | vmAnno.addCafeterias(cafeterias: self.sortedCafeterias)
28 | }
29 |
30 | var body: some View {
31 | ScrollView {
32 | VStack {
33 | AnnotatedMapView(vm: vmAnno, centerOfMap: CLLocationCoordinate2D(latitude: 48.1372, longitude: 11.5755), zoomLevel: 0.3)
34 |
35 | CafeteriasListView(vm: self.vmAnno)
36 | }
37 | }
38 | .scrollContentBackground(.hidden)
39 | .background(Color.primaryBackground)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Cafeterias/MealPlanScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MealPlanScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 10.02.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MealPlanScreen: View {
11 | @Environment(\.colorScheme) var colorScheme
12 | @StateObject var vm: MealPlanViewModel
13 |
14 | init(cafeteria: Cafeteria) {
15 | self._vm = StateObject(wrappedValue: MealPlanViewModel(cafeteria: cafeteria))
16 | }
17 |
18 | var body: some View {
19 | Group {
20 | switch vm.state {
21 | case .success(let menus):
22 | if let firstMenu = menus.first {
23 | VStack {
24 | MealPlanView(menus: menus, cafeteria: vm.cafeteria, selectedMenu: firstMenu).refreshable {
25 | await vm.getMenus()
26 | }
27 | }
28 | }
29 | case .loading, .na:
30 | LoadingView(text: "Fetching Menus")
31 | case .failed(_):
32 | VStack {
33 | Spacer()
34 | // Since some cafeterias do not update their menus this is how we handle error here. There could be a better differentiation.
35 | Text("No Menu available").foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray))
36 | Spacer()
37 | }
38 | }
39 | }.task {
40 | await vm.getMenus()
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Cafeterias/MenuView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuViewNEW.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 30.04.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MenuView: View { //wird grad ned genutzt evtl delete
11 |
12 | let menu: cafeteriaMenu
13 |
14 | var body: some View {
15 | VStack{
16 | ForEach(menu.categories.sorted { $0.name < $1.name }) { category in
17 | Text(category.name)
18 | ScrollView(.horizontal, showsIndicators: false) {
19 | HStack {
20 | ForEach(category.dishes, id: \.self) { dish in
21 | DishView(dish: dish)
22 | }
23 | }.padding(.horizontal)
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Cafeterias/OldCafeteriaComponent/MenuViewOld.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuViewOld.swift
3 | // Campus-iOS
4 | //
5 | // Created by August Wittgenstein on 15.01.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MenuViewOld: View {
11 | let menu: cafeteriaMenu
12 |
13 | var body: some View {
14 | List {
15 | ForEach(menu.categories.sorted { $0.name < $1.name }) { category in
16 | Section(category.name) {
17 | ForEach(category.dishes, id: \.self) { dish in
18 | DishViewOld(dish: dish)
19 | }
20 | }
21 | }
22 | }.listStyle(GroupedListStyle()) // make sections non-collapsible
23 | }
24 | }
25 |
26 | //struct MenuView_Previews: PreviewProvider {
27 | // static var previews: some View {
28 | // MenuView(viewModel: MenuViewModel(date: Date(), categories: []))
29 | // }
30 | //}
31 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Cafeterias/Widget/CafeteriaWidgetScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CafeteriaWidgetScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 08.02.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CafeteriaWidgetScreen: View {
11 |
12 | @StateObject var viewModel: CafeteriaWidgetViewModel
13 |
14 | var body: some View {
15 | Group {
16 | switch(viewModel.status) {
17 | case .error:
18 | EmptyView() // -> keine cafeterias nearby
19 | case .loading:
20 | LoadingView(text: "Searching nearby cafeteria")
21 | default:
22 | CafeteriaWidget(cafeteriaWidgetVM: self.viewModel, dishes: viewModel.menu?.getDishes() ?? [])
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Cafeterias/Widget/MealIngredientsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MealIngredientsView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 20.02.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MealIngredientsView: View {
11 |
12 | var mealTitle: String
13 | var labels: [String]
14 | var price: String
15 | @State private var showingAlert = false
16 |
17 | var body: some View {
18 | Image(systemName: "info.circle")
19 | .foregroundColor(Color.highlightText)
20 | .frame(width: 5)
21 | .onTapGesture {
22 | showingAlert.toggle()
23 | }
24 | .alert(isPresented: $showingAlert) {
25 | Alert(title: Text(mealTitle), message: Text("\(labels.joined(separator: "\n"))\n\n\(price)"), dismissButton: .default(Text("Got it!")))
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/Campuses/CampusCellView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CampusCellView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 25.04.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CampusCellView: View {
11 | let campus: Campus
12 |
13 | var body: some View {
14 | VStack {
15 | campus.image
16 | .resizable()
17 | .scaledToFill()
18 | .frame(maxHeight: 100)
19 | .cornerRadius(Radius.regular, corners: [.topLeft, .topRight])
20 | .clipped()
21 | .padding([.horizontal,.top], 5)
22 |
23 | HStack() {
24 | Text(campus.rawValue)
25 | .fontWeight(.semibold)
26 | .foregroundColor(.primaryText)
27 | Spacer()
28 | NavigationLink(destination: LocationView(location: TUMLocation(campus: self.campus))) {
29 | Image(systemName: "mappin.circle")
30 | .font(.system(size: 20))
31 | .foregroundColor(.highlightText)
32 | }
33 | .padding(7)
34 | .cornerRadius(5)
35 | }
36 | .padding(12)
37 | }
38 | .background(Color.secondaryBackground)
39 | .cornerRadius(Radius.regular)
40 | .frame(width: Size.cardWidth)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/StudyGroups/StudyRoomGroupsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StudyRoomViewNEW.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 25.04.23.
6 | //
7 |
8 | import SwiftUI
9 | import MapKit
10 |
11 | struct StudyRoomGroupsView: View {
12 |
13 | @StateObject var vm: MapViewModel
14 | var vmAnno = AnnotatedMapViewModel()
15 |
16 | init(vm: MapViewModel, studyRoomGroups: [StudyRoomGroup]? = nil) {
17 | self._vm = StateObject(wrappedValue: vm)
18 | if (studyRoomGroups != nil) {
19 | vmAnno.addStudyRoomGroups(studyRoomGroups: studyRoomGroups!)
20 | }
21 | }
22 |
23 | var body: some View {
24 | ScrollView {
25 | VStack {
26 |
27 | AnnotatedMapView(vm: vmAnno, centerOfMap: CLLocationCoordinate2D(latitude: 48.1372, longitude: 11.5755), zoomLevel: 0.3)
28 |
29 | StudyRoomGroupListView(vmAnno: self.vmAnno, vm: self.vm)
30 |
31 | }
32 | .padding(.bottom, 20)
33 | }
34 | .scrollContentBackground(.hidden)
35 | .background(Color.primaryBackground)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/View/StudyGroups/Widget/StudyRoomWidgetScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StudyRoomWidgetScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 06.02.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct StudyRoomWidgetScreen: View {
11 |
12 | @StateObject var studyRoomWidgetVM : StudyRoomWidgetViewModel
13 |
14 | var body: some View {
15 | Group {
16 | switch(self.studyRoomWidgetVM.status) {
17 | case .error:
18 | EmptyView() // -> keine study rooms nearby
19 | case .loading:
20 | LoadingView(text: "Searching nearby study rooms")
21 | case .success:
22 | StudyRoomWidgetView(studyRoomWidgetVM: self.studyRoomWidgetVM)
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/ViewModel/AnnotatedMapViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnnotatedMapViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 05.06.23.
6 | //
7 |
8 | import Foundation
9 |
10 | @MainActor
11 | class AnnotatedMapViewModel: ObservableObject {
12 |
13 | @Published var locations = [TUMLocation]()
14 | @Published var cafeterias = [Cafeteria]()
15 | @Published var studyRoomGroups = [StudyRoomGroup]()
16 | @Published var rooms = [NavigaTumNavigationEntity]()
17 | @Published var annotationCloser = false //remove
18 | @Published var openAnnotation: UUID?
19 |
20 | func addCafeterias(cafeterias: [Cafeteria]) {
21 | locations.append(contentsOf: cafeterias.map {TUMLocation(cafeteria: $0)})
22 | self.cafeterias.append(contentsOf: cafeterias)
23 | }
24 |
25 | func addStudyRoomGroups(studyRoomGroups: [StudyRoomGroup]) {
26 | locations.append(contentsOf: studyRoomGroups.map {TUMLocation(studyRoomGroup: $0)})
27 | self.studyRoomGroups.append(contentsOf: studyRoomGroups)
28 | }
29 |
30 | func addRooms(rooms: [NavigaTumNavigationEntity]) async {
31 | for room in rooms {
32 | let tempVM = NavigaTumDetailsViewModel(id: room.id)
33 | await tempVM.fetchDetails()
34 | if let details = tempVM.details {
35 | locations.append(TUMLocation(room: room, details: details))
36 | }
37 | }
38 | self.rooms.append(contentsOf: rooms)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/ViewModel/MapViewModel+State.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapViewModel+State.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 14.05.22.
6 | //
7 |
8 | import Foundation
9 |
10 | extension MapViewModel {
11 | enum CafeteriasNetworkState {
12 | // to make it Equitable
13 | // static func == (lhs: MapViewModel.CafeteriasNetworkState, rhs: MapViewModel.CafeteriasNetworkState) -> Bool {
14 | // switch (lhs, rhs) {
15 | // case (.na, .na):
16 | // return true
17 | // case (.loading, .loading):
18 | // return true
19 | // case (.failed(error: _), .failed(error: _)):
20 | // return true
21 | // case (.success(data: let lhsData), .success(data: let rhsData)):
22 | // return lhsData == rhsData
23 | // default:
24 | // return false
25 | // }
26 | // }
27 |
28 | case na
29 | case loading
30 | case success(data: [Cafeteria])
31 | case failed(error: Error)
32 | }
33 |
34 | enum StudyRoomsNetworkState {
35 | case na
36 | case loading
37 | case success(data: StudyRoomApiRespose)
38 | case failed(error: Error)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/ViewModel/MealPlanViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | // Campus-iOS
4 | //
5 | // Created by Tim Gymnich on 19.01.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 | import Alamofire
11 |
12 | @MainActor
13 | class MealPlanViewModel: ObservableObject {
14 | @Published var state: APIState<[cafeteriaMenu]> = .na
15 | @Published var hasError: Bool = false
16 |
17 | let service = MealPlanService()
18 | let cafeteria: Cafeteria
19 |
20 | init(cafeteria: Cafeteria) {
21 | self.cafeteria = cafeteria
22 | }
23 |
24 | func getMenus(forcedRefresh: Bool = false) async {
25 | if !forcedRefresh {
26 | self.state = .loading
27 | }
28 | self.hasError = false
29 |
30 | do {
31 | let data = try await service.fetch(cafeteria: self.cafeteria, forcedRefresh: forcedRefresh)
32 | print(data)
33 | self.state = .success(
34 | data: data
35 | )
36 | } catch {
37 | self.state = .failed(error: error)
38 | self.hasError = true
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/Campus-iOS/MapComponent/ViewModel/MenuViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MealPlanViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by Tim Gymnich on 19.01.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | final class cafeteriaMenu: Identifiable, Decodable {
12 | var id = UUID()
13 | let date: Date
14 | var categories: [MenuCategory]
15 |
16 | init(date: Date, categories: [MenuCategory]) {
17 | self.date = date
18 | self.categories = categories
19 | }
20 |
21 | func getDishes() -> [Dish] {
22 | var dishes: [Dish] = []
23 | for category in categories {
24 | for dish in category.dishes {
25 | dishes.append(dish)
26 | }
27 | }
28 |
29 | return dishes.sorted(by: { $0.name < $1.name })
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Model/Movie+PreviewData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Movie+PreviewData.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 05.05.23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Movie: Identifiable {
11 | static let dummyData: Movie = .init(
12 | id: 123,
13 | actors: "Morgan Freeman",
14 | cover: URL(string:"https://www.google.com"),
15 | created: Date.now,
16 | date: Date.now,
17 | director: "Frank Darapant",
18 | genre: "Crime",
19 | link: URL(string:"https://www.google.com"),
20 | movieDescription: "Yes",
21 | rating: "11/10",
22 | runtime: "194",
23 | title: "Shawshank Redemption",
24 | year: "2020"
25 | )
26 |
27 | static let previewData: [Movie] = [dummyData]
28 | }
29 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Service/MovieService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MovieService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 14.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol MovieServiceProtocol {
11 | func fetch(forcedRefresh: Bool) async throws -> [Movie]
12 | }
13 |
14 | struct MovieService: ServiceProtocol, MovieServiceProtocol {
15 | func fetch(forcedRefresh: Bool = false) async throws -> [Movie] {
16 |
17 | let response: [Movie] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.movie, forcedRefresh: forcedRefresh)
18 |
19 | return response
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Views/MovieDetailCellView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MovieDetailCellView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 27.01.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MovieDetailCellView: View {
11 | var property: (key: LocalizedStringKey, value: [String])
12 |
13 | var body: some View {
14 | HStack {
15 | Text(property.key)
16 | .font(.system(size: 18, weight: .semibold))
17 | .foregroundColor(Color("tumBlue"))
18 | .multilineTextAlignment(.leading)
19 | .frame(maxWidth: .infinity, alignment: .leading)
20 | Spacer().frame(width: 20)
21 | VStack(alignment: .leading) {
22 | ForEach(property.value, id: \.self) { propVal in
23 | Text(propVal)
24 | .font(.system(size: 16))
25 | .multilineTextAlignment(.leading)
26 | }
27 | }.frame(maxWidth: .infinity, alignment: .leading)
28 | }.font(.caption)
29 | .foregroundColor(.secondary)
30 | }
31 | }
32 |
33 | struct MovieDetailCellView_Previews: PreviewProvider {
34 | static var previews: some View {
35 | MovieDetailCellView(property: ("Description", ["Some Description"]))
36 | .previewLayout(.sizeThatFits)
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Views/MovieDetailsBasicInfoRowView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MovieDetaislBasicInfoRowView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Atharva Mathapati on 23.06.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MovieDetailsBasicInfoRowView: View {
11 | var iconName: String
12 | var text: String
13 |
14 | var body: some View {
15 | HStack(alignment: .center, spacing: 12) {
16 | Image(systemName: iconName)
17 | .imageScale(.medium)
18 | .frame(width: 25, height: 25, alignment: .center)
19 | Text(text)
20 | .font(.system(size: 16))
21 | }
22 | }
23 | }
24 |
25 | struct MovieDetaislBasicInfoRowView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | MovieDetailsBasicInfoRowView(
28 | iconName: "number",
29 | text: "test"
30 | )
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Views/MovieDetailsDetailedInfoRowView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MovieDetailsDetailedInfoRowView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Atharva Mathapati on 29.06.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MovieDetailsDetailedInfoRowView: View {
11 | var title: String
12 | var text: String
13 |
14 | var body: some View {
15 | VStack(alignment: .leading, spacing: 8) {
16 | Text(title)
17 | .font(.system(size: 16, weight: .semibold))
18 |
19 | Text(text)
20 | .font(.system(size: 16))
21 | }
22 | }
23 | }
24 |
25 | struct MovieDetailsDetailedInfoRowView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | MovieDetailsDetailedInfoRowView(
28 | title: "number",
29 | text: "test"
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Views/MovieView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MoviesView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 27.01.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MovieView: View {
11 | let movies: [Movie]
12 | @State private var selectedMovie: Movie? = nil
13 |
14 | var items: [GridItem] {
15 | Array(repeating: .init(.adaptive(minimum: 120)), count: 2)
16 | }
17 |
18 | var body: some View {
19 | ZStack {
20 | Text("No more movies this semester 😢\nGet excited for the next season!")
21 | .foregroundColor(Color(UIColor.lightGray))
22 | ScrollView(.vertical) {
23 | LazyVGrid(columns: items, spacing: 10) {
24 | ForEach(self.movies, id: \.id ) { movie in
25 | MovieCard(movie: movie).padding(7)
26 | .onTapGesture {
27 | selectedMovie = movie
28 | }
29 | }
30 | .sheet(item: $selectedMovie) { movie in
31 | MovieDetailedView(movie: movie)
32 | }
33 | }
34 | .padding(10)
35 | .background(Color.systemsBackground)
36 | }
37 | }
38 | }
39 | }
40 |
41 | //struct MoviesView_Previews: PreviewProvider {
42 | // static var previews: some View {
43 | // MoviesView()
44 | // }
45 | //}
46 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Views/MoviesView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MoviesView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 27.01.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MoviesView: View {
11 | let movies: [Movie]
12 | @State private var selectedMovie: Movie? = nil
13 |
14 | var items: [GridItem] {
15 | Array(repeating: .init(.adaptive(minimum: 120)), count: 2)
16 | }
17 |
18 | var body: some View {
19 | ZStack {
20 | Text("No more movies this semester 😢\nGet excited for the next season!")
21 | .foregroundColor(Color(UIColor.lightGray))
22 | ScrollView(.vertical) {
23 | LazyVGrid(columns: items, spacing: 10) {
24 | ForEach(self.movies, id: \.id ) { movie in
25 | MovieCard(movie: movie).padding(7)
26 | .onTapGesture {
27 | selectedMovie = movie
28 | }
29 | }
30 | .sheet(item: $selectedMovie) { movie in
31 | MovieDetailedView(movie: movie)
32 | }
33 | }
34 | .padding(10)
35 | .background(Color.systemsBackground)
36 | }
37 | }
38 | }
39 | }
40 |
41 | //struct MoviesView_Previews: PreviewProvider {
42 | // static var previews: some View {
43 | // MoviesView()
44 | // }
45 | //}
46 |
--------------------------------------------------------------------------------
/Campus-iOS/MoviesComponent/Views/Widget/MoviesWidgetView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MoviesWidgetView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 17.01.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MoviesWidgetView: View {
11 |
12 | let movies: [Movie]
13 | @State private var selectedMovie: Movie? = nil
14 |
15 | var items: [GridItem] {
16 | Array(repeating: .init(.adaptive(minimum: 120)), count: 2)
17 | }
18 |
19 | var body: some View {
20 | VStack(spacing: 0) {
21 | Text("TU Film").titleStyle()
22 |
23 | ScrollView(.horizontal, showsIndicators: false) {
24 | HStack(spacing: 15) {
25 | ForEach(movies, id: \.id ) { movie in
26 | MovieCard(movie: movie)
27 | .onTapGesture {
28 | selectedMovie = movie
29 | }
30 | }
31 | .sheet(item: $selectedMovie) { movie in
32 | MovieDetailedView(movie: movie)
33 | }
34 | }
35 | .padding(.horizontal, 20)
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Campus-iOS/NewsComponent/Model/News+PreviewData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // News+PreviewData.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 05.05.23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension News {
11 | static let previewData: [News] = [
12 | News(id: "test", sourceId: 1, date: Date.now, created: Date.now, title: "Testing news 1", link: URL(string: "https://www.tum.de"), imageURL: nil),
13 | News(id: "test1", sourceId: 1, date: Date.now, created: Date.now, title: "Testing news 2", link: URL(string: "https://www.moodle.tum.de"), imageURL: nil),
14 | News(id: "test2", sourceId: 1, date: Date.now, created: Date.now, title: "Testing news 3", link: URL(string: "https://www.live.rgb.tum.de"), imageURL: nil)
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/NewsComponent/Screen/NewsScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NewsScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 22.01.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct NewsScreen: View {
11 | @StateObject var vm = NewsViewModel()
12 | let isWidget: Bool
13 |
14 | var body: some View {
15 | Group {
16 | switch vm.state {
17 | case .success(let newsSources):
18 | if isWidget {
19 | NewsWidgetView(latestFiveNews: vm.latestFiveNews)
20 | } else {
21 | VStack {
22 | NewsView(latestFiveNews: vm.latestFiveNews, newsSources: newsSources)
23 | .refreshable {
24 | await vm.getNewsSources(forcedRefresh: true)
25 | }
26 | }
27 | }
28 | case .loading, .na:
29 | LoadingView(text: "Fetching News")
30 | .padding(.vertical)
31 | case .failed(let error):
32 | if isWidget {
33 | EmptyView()
34 | } else {
35 | FailedView(
36 | errorDescription: error.localizedDescription,
37 | retryClosure: vm.getNewsSources
38 | )
39 | }
40 | }
41 | }.task {
42 | await vm.getNewsSources(forcedRefresh: true)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Campus-iOS/NewsComponent/Service/NewsService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NewsService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 13.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol NewsServiceProtocol {
11 | func fetch(forcedRefresh: Bool) async throws -> [NewsSource]
12 |
13 | func fetch(forcedRefresh: Bool, source: String) async throws -> [News]
14 | }
15 |
16 | struct NewsService: ServiceProtocol, NewsServiceProtocol {
17 | func fetch(forcedRefresh: Bool = false) async throws -> [NewsSource] {
18 |
19 | var newsSourceResponse: [NewsSource] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.newsSources, forcedRefresh: forcedRefresh)
20 |
21 | for i in newsSourceResponse.indices {
22 | guard let idDescription = newsSourceResponse[i].id?.description else {
23 | break
24 | }
25 |
26 | let news: [News] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.news(source: String(idDescription)))
27 |
28 | newsSourceResponse[i].news = news
29 | }
30 |
31 | return newsSourceResponse
32 | }
33 |
34 | func fetch(forcedRefresh: Bool, source: String) async throws -> [News] {
35 | let news: [News] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.news(source: "1"))
36 |
37 | return news
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Campus-iOS/PersonDetailedComponent/Entity/Organization.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Organization.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 06.02.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Organisation: Decodable, Identifiable {
11 | let name: String
12 | let id: String
13 | let number: String
14 | let title: String
15 | let description: String
16 |
17 | enum CodingKeys: String, CodingKey {
18 | case name = "org"
19 | case id = "kennung"
20 | case number = "org_nr"
21 | case title = "titel"
22 | case description = "beschreibung"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Campus-iOS/PersonDetailedComponent/Entity/PhoneExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhoneExtension.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 06.02.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct PhoneExtension: Decodable, Identifiable {
11 | let id = UUID()
12 | let phoneNumber: String
13 | let countryCode: String
14 | let areaCode: String
15 | let equipmentNumber: String
16 | let branchNumber: String
17 |
18 | enum CodingKeys: String, CodingKey {
19 | case phoneNumber = "telefonnummer"
20 | case countryCode = "tum_anlage_land"
21 | case areaCode = "tum_anlage_ortsvorwahl"
22 | case equipmentNumber = "tum_anlage_nummer"
23 | case branchNumber = "tum_nebenstelle"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Campus-iOS/PersonDetailedComponent/Entity/Room.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Room.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 06.02.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Room: Decodable, Identifiable {
11 | let number: String
12 | let buildingName: String
13 | let buildingNumber: String
14 | let floorName: String
15 | let floorNumber: String
16 | let id: String
17 | let locationDescription: String
18 | let shortLocationDescription: String
19 | let longLocationDescription: String
20 |
21 | enum CodingKeys: String, CodingKey {
22 | case number = "nummer"
23 | case buildingName = "gebaeudename"
24 | case buildingNumber = "gebaeudenummer"
25 | case floorName = "stockwerkname"
26 | case floorNumber = "stockwerknummer"
27 | case id = "architekt"
28 | case locationDescription = "ortsbeschreibung"
29 | case shortLocationDescription = "kurz"
30 | case longLocationDescription = "lang"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Campus-iOS/PersonDetailedComponent/Service/PersonDetailedService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PersonDetailedService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 21.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct PersonDetailedService {
11 | func fetch(for id: String, token: String, forcedRefresh: Bool) async throws -> PersonDetails {
12 | let response : PersonDetails = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.personDetails(identNumber: id), token: token, forcedRefresh: forcedRefresh)
13 |
14 | return response
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/PersonSearchComponent/Service/PersonSearchService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PersonSearchService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 21.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct PersonSearchService {
11 | func fetch(for query: String, token: String, forcedRefresh: Bool) async throws -> [Person] {
12 | let response : TUMOnlineAPI.Response = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.personSearch(search: query), token: token, forcedRefresh: forcedRefresh)
13 |
14 | return response.row
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/PersonSearchComponent/View/PersonSearchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PersonSearchListView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 13.02.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct PersonSearchView: View {
11 | let model: Model
12 | let persons: [Person]
13 |
14 | var body: some View {
15 | List {
16 | ForEach(self.persons, id: \.nr) { person in
17 | NavigationLink(
18 | destination:
19 | PersonDetailedScreenSearch(model: model, person: person)
20 | .navigationBarTitleDisplayMode(.inline)
21 | ) {
22 | Text(person.fullName)
23 | }
24 | }
25 | }
26 | }
27 | }
28 |
29 | struct PersonSearchView_Previews: PreviewProvider {
30 | static var previews: some View {
31 | PersonSearchView(model: Model(), persons: [])
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Campus-iOS/PersonSearchComponent/ViewModel/PersonSearchViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PersonSearchViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 06.02.22.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 | import XMLCoder
11 |
12 | @MainActor
13 | class PersonSearchViewModel: ObservableObject {
14 | @Published var state: APIState<[Person]> = .na
15 | @Published var hasError: Bool = false
16 |
17 | let model: Model
18 | let service: PersonSearchService
19 |
20 | init(model: Model, service: PersonSearchService) {
21 | self.model = model
22 | self.service = service
23 | }
24 |
25 | func getPersons(for query: String, forcedRefresh: Bool) async {
26 | if !forcedRefresh {
27 | self.state = .loading
28 | }
29 | self.hasError = false
30 |
31 | guard let token = self.model.token else {
32 | self.state = .failed(error: NetworkingError.unauthorized)
33 | self.hasError = true
34 | return
35 | }
36 |
37 | do {
38 | self.state = .success(
39 | data: try await service.fetch(for: query, token: token, forcedRefresh: forcedRefresh)
40 | )
41 |
42 | } catch {
43 | self.state = .failed(error: error)
44 | self.hasError = true
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Campus-iOS/ProfileComponent/View/ProfileToolbar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfileToolbarGroup.swift
3 | // Campus-iOS
4 | //
5 | // Created by Anton Wyrowski on 11.12.21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ProfileToolbar: View {
11 | @StateObject var model: Model
12 | @State var showProfile = false
13 |
14 | var body: some View {
15 |
16 | Button(action: {self.showProfile.toggle()}) {
17 | Image(systemName: "person.crop.circle")
18 | }
19 | .sheet(isPresented: $showProfile) {
20 | ProfileView(model: model)
21 | }
22 |
23 | }
24 | }
25 |
26 | struct ProfileToolbarGroup_Previews: PreviewProvider {
27 | static var previews: some View {
28 | ProfileToolbar(model: Model())
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Entity/FoundRoom.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Room.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 17.05.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct FoundRoom: Codable, Hashable {
11 | /*
12 | {
13 | "room_id": "59773",
14 | "room_code": "5302.TP.208",
15 | "building_nr": "5302",
16 | "arch_id": "-1.208@5302",
17 | "info": "-1.208, Cleanroom\/EI. Mikroskop",
18 | "address": "Lichtenbergstr. 2(5302), Tiefparterre",
19 | "purpose": "Labor - Physik",
20 | "campus": "G",
21 | "name": "Garching"
22 | }
23 | */
24 |
25 | let roomId: String
26 | let roomCode: String
27 | let buildingNumber: String
28 | let id: String
29 | let info: String
30 | let address: String
31 | let purpose: String
32 | let campus: String?
33 | let name: String?
34 |
35 | enum CodingKeys: String, CodingKey {
36 | case roomId = "room_id"
37 | case roomCode = "room_code"
38 | case buildingNumber = "building_nr"
39 | case id = "arch_id"
40 | case info
41 | case address
42 | case purpose
43 | case campus
44 | case name
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Entity/RoomFinderLocation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumLocation.swift
3 | // Campus-iOS
4 | //
5 | // Created by Jakob Paul Körber on 06.10.23.
6 | //
7 |
8 | import Foundation
9 | import MapKit
10 |
11 | struct RoomFinderLocation: Identifiable {
12 | let id = UUID()
13 | let coordinate: CLLocationCoordinate2D
14 | }
15 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumNavigationAdditionalProperties.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationAdditionalProperties.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumNavigationAdditionalProperties: Codable {
10 | let properties: [NavigaTumNavigationProperty]
11 |
12 | enum CodingKeys: String, CodingKey {
13 | case properties = "computed"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumNavigationCoordinates.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationCoordinates.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumNavigationCoordinates: Codable {
10 | let latitude: Double
11 | let longitude: Double
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case latitude = "lat"
15 | case longitude = "lon"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumNavigationMaps.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationMaps.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumNavigationMaps: Codable {
10 | let `default`: String
11 | let roomfinder: NavigaTumRoomFinderMaps?
12 | let overlays: NavigaTumOverlaysMaps?
13 | }
14 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumOverlaysMaps.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumOverlaysMaps.swift
3 | // Campus-iOS
4 | //
5 | // Created by Atharva Mathapati on 07.03.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumOverlaysMaps: Codable {
10 | let available: [NavigaTumOverlayMap]
11 | }
12 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumRoomFinderMaps.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomFinderMaps.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumRoomFinderMaps: Codable {
10 | let available: [NavigaTumRoomFinderMap]
11 | let defaultMapId: String
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case available
15 | case defaultMapId = "default"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/NavigaTumNavigationDetails.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationDetails.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumNavigationDetails: Codable {
10 | let id: String
11 | let name: String
12 | let parentNames: [String]
13 | let type: String
14 | let typeCommonName: String
15 | let additionalProperties: NavigaTumNavigationAdditionalProperties
16 | let coordinates: NavigaTumNavigationCoordinates
17 | let maps: NavigaTumNavigationMaps
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case id, name, type, maps
21 | case typeCommonName = "type_common_name"
22 | case additionalProperties = "props"
23 | case coordinates = "coords"
24 | case parentNames = "parent_names"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/NavigaTumNavigationProperty.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationProperty.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumNavigationProperty: Codable {
10 | let name: String
11 | let text: String
12 | }
13 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/NavigaTumOverlayMap.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumOverlayMap.swift
3 | // Campus-iOS
4 | //
5 | // Created by Atharva Mathapati on 07.03.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumOverlayMap: Codable, Identifiable {
10 | let id: Int
11 | let floor: String
12 | let imageUrl: String
13 | let name: String
14 |
15 | enum CodingKeys: String, CodingKey {
16 | case id, floor, name
17 | case imageUrl = "file"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/NavigaTumRoomFinderMap.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomFinderMap.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumRoomFinderMap: Codable, Identifiable {
10 | let id: String
11 | let name: String
12 | let imageUrl: String // let baseMapUrl = "https://nav.tum.sexy/cdn/maps/roomfinder/"
13 | let height: Int
14 | let width: Int
15 | let x: Int
16 | let y: Int
17 | let scale: String
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case id, name, height, width, x, y, scale
21 | case imageUrl = "file"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/Search/NavigaTumSearchResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumSearchResponse.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumSearchResponse: Codable {
10 | let id = UUID()
11 | let sections: [NavigaTumSearchResponseSection]
12 |
13 | enum CodingKeys: CodingKey {
14 | case sections
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Model/Search/NavigaTumSearchResponseSection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumSearchResponseSection.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 | import Foundation
8 |
9 | struct NavigaTumSearchResponseSection: Codable {
10 | let type: String
11 | let entries: [NavigaTumNavigationEntity]
12 |
13 | enum CodingKeys: String, CodingKey {
14 | case type = "facet"
15 | case entries
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Service/RoomFinderService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomFinderService.swift
3 | // Campus-iOS
4 | //
5 | // Created by Philipp Zagar on 01.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol RoomFinderServiceProtocol {
11 | func search(query: String) async throws -> NavigaTumSearchResponse
12 | func details(id: String) async throws -> NavigaTumNavigationDetails
13 | }
14 |
15 | struct RoomFinderService: RoomFinderServiceProtocol {
16 |
17 | func search(query: String) async throws -> NavigaTumSearchResponse {
18 | return try await MainAPI.makeRequest(endpoint: NavigaTUMAPI.search(query: query))
19 | }
20 |
21 | func details(id: String) async throws -> NavigaTumNavigationDetails {
22 | let language = (Locale.current.language.languageCode?.identifier == "de") ? "de" : "en"
23 | return try await MainAPI.makeRequest(endpoint: NavigaTUMAPI.details(id: id, language: language))
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/ViewModel/NavigaTumDetailsViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumDetailsViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by Atharva Mathapati on 07.01.23.
6 | //
7 | import Foundation
8 |
9 | import Foundation
10 | import Alamofire
11 | import XMLCoder
12 |
13 | class NavigaTumDetailsViewModel: ObservableObject {
14 | @Published var details: NavigaTumNavigationDetails?
15 | @Published var errorMessage = ""
16 | let id: String
17 |
18 | init(id: String) {
19 | self.id = id
20 | }
21 |
22 | @MainActor func fetchDetails() async {
23 | guard !id.isEmpty else {
24 | self.errorMessage = "Couldn't fetch room details"
25 | return
26 | }
27 | do {
28 | self.details = try await RoomFinderService().details(id: id)
29 | } catch {
30 | self.errorMessage = "Room finder service failed"
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/ViewModel/NavigaTumViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by Atharva Mathapati on 07.01.23.
6 | //
7 | import Foundation
8 | import Alamofire
9 | import XMLCoder
10 |
11 | class NavigaTumViewModel: ObservableObject {
12 | @Published var searchResults: [NavigaTumNavigationEntity] = []
13 | @Published var errorMessage: String = ""
14 |
15 | @MainActor func fetch(searchString: String) async {
16 | guard !searchString.isEmpty else {
17 | self.errorMessage = ""
18 | return
19 | }
20 | do {
21 | let results = try await RoomFinderService().search(query: searchString)
22 | self.searchResults = results.sections.flatMap(\.entries)
23 | } catch {
24 | self.errorMessage = "Room Service Failed"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Views/NavigaTumView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigaTumView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Atharva Mathapati on 06.01.23.
6 | //
7 | import SwiftUI
8 |
9 | struct NavigaTumView: View {
10 | @ObservedObject var model: Model
11 | @StateObject var viewModel = NavigaTumViewModel()
12 | @State var searchText = ""
13 |
14 | var body: some View {
15 | NavigaTumListView(model: self.model, viewModel: self.viewModel)
16 | .background(Color(.systemGroupedBackground))
17 | .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
18 | .onChange(of: self.searchText) { searchValue in
19 | if searchValue.count > 3 {
20 | Task {
21 | await search(searchValue)
22 | }
23 | }
24 | }
25 | .task {
26 | if !searchText.isEmpty {
27 | await search(searchText)
28 | }
29 | }
30 | .animation(.default, value: self.viewModel.searchResults)
31 | }
32 |
33 | func search(_ searchValue: String) async {
34 | await self.viewModel.fetch(searchString: searchValue)
35 | }
36 | }
37 |
38 | struct NavigaTumView_Previews: PreviewProvider {
39 | static var previews: some View {
40 | NavigaTumView(model: MockModel())
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Campus-iOS/RoomFinderComponent/Views/RoomDetailsScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomDetailsScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by Timothy Summers on 09.06.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct RoomDetailsScreen: View {
11 |
12 | var room: NavigaTumNavigationEntity
13 | @StateObject var vm: NavigaTumDetailsViewModel
14 |
15 | init(room: NavigaTumNavigationEntity) {
16 | self.room = room
17 | self._vm = StateObject(wrappedValue: NavigaTumDetailsViewModel(id: room.id))
18 | }
19 |
20 | var body: some View {
21 | Group {
22 | if let details = vm.details {
23 | LocationView(location: TUMLocation(room: self.room, details: details))
24 | } else {
25 | ProgressView()
26 | }
27 | }.task {
28 | await vm.fetchDetails()
29 | }
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Types and Protocols/ComparisonToken.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ComparisonToken.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 27.12.22.
6 | //
7 |
8 | import Foundation
9 |
10 | infix operator =/
11 |
12 | struct ComparisonToken: Hashable {
13 | var value: String
14 | var type: ComparisonTokenType = .tokenized
15 |
16 | enum ComparisonTokenType {
17 | case tokenized
18 | case raw
19 | }
20 |
21 | static func =/ (lhs: Self, rhs: Self) -> Bool {
22 | guard lhs.value.count == rhs.value.count else {
23 | return false
24 | }
25 |
26 | for i in 0..) -> String {
12 | return String(self.filter {validChars.contains($0)})
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Types and Protocols/SearchError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchError.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum SearchError: Error, CustomStringConvertible{
11 | case empty(searchQuery: String)
12 | case unexpected
13 |
14 | public var description: String {
15 | switch self {
16 | case .empty(let searchQuery):
17 | return "No search results were found for: \"\(searchQuery)\"."
18 | case .unexpected:
19 | return "An unexpected error occurred."
20 | }
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Types and Protocols/SearchState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 05.05.23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum SearchState {
11 | case na
12 | case loading
13 | case success(data: [(T, Distances)])
14 | case failed(error: Error)
15 | }
16 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/ViewModels/Searchable ViewModels/CafeteriaSearchResultViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CafeteriaSearchResultViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 27.12.22.
6 | //
7 |
8 | import Foundation
9 |
10 | @MainActor
11 | class CafeteriaSearchResultViewModel: ObservableObject {
12 |
13 | @Published var state: SearchState = .na
14 | @Published var hasError: Bool = false
15 | let service: CafeteriasServiceProtocol
16 |
17 | init(service: CafeteriasServiceProtocol) {
18 | self.service = service
19 | }
20 |
21 | func cafeteriasSearch(for query: String, forcedRefresh: Bool = false) async {
22 | if !forcedRefresh {
23 | self.state = .loading
24 | }
25 | self.hasError = false
26 |
27 | do {
28 | let data = try await service.fetch(forcedRefresh: forcedRefresh)
29 | if let optionalResults = GlobalSearch.tokenSearch(for: query, in: data) {
30 | self.state = .success(data: optionalResults)
31 | } else {
32 | self.state = .failed(error: SearchError.empty(searchQuery: query))
33 | }
34 |
35 | } catch {
36 | self.state = .failed(error: error)
37 | self.hasError = true
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/ViewModels/Searchable ViewModels/MovieSearchResultViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MovieSearchResultView.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import Foundation
9 |
10 | @MainActor
11 | class MovieSearchResultViewModel: ObservableObject {
12 | @Published var state: SearchState = .na
13 | @Published var hasError: Bool = false
14 |
15 | let service: MovieServiceProtocol
16 |
17 | init(service: MovieServiceProtocol) {
18 | self.service = service
19 | }
20 |
21 | func movieSearch(for query: String, forcedRefresh: Bool = false) async {
22 |
23 | if !forcedRefresh {
24 | self.state = .loading
25 | }
26 | self.hasError = false
27 |
28 | do {
29 | let data = try await service.fetch(forcedRefresh: forcedRefresh)
30 | let filteredMovies = data.filter { movie in
31 | return (movie.date ?? Date.distantPast) >= Date()
32 | }
33 |
34 | if let optionalResults = GlobalSearch.tokenSearch(for: query, in: filteredMovies) {
35 | self.state = .success(data: optionalResults)
36 | } else {
37 | self.state = .failed(error: SearchError.empty(searchQuery: query))
38 | self.hasError = true
39 | }
40 |
41 | } catch {
42 | self.state = .failed(error: error)
43 | self.hasError = true
44 | }
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/ViewModels/Searchable ViewModels/RoomFinderSearchResultViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomFinderSearchViewModel.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 13.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | @MainActor
11 | class RoomFinderSearchResultViewModel: ObservableObject {
12 | @Published var state: APIState<[NavigaTumNavigationEntity]> = .na
13 | @Published var hasError: Bool = false
14 |
15 | func roomFinderSearch(for query: String, forcedRefresh: Bool = false) async {
16 | if !forcedRefresh {
17 | self.state = .loading
18 | }
19 | self.hasError = false
20 |
21 | do {
22 | self.state = .success(data: try await RoomFinderService().search(query: query).sections.flatMap(\.entries))
23 | } catch {
24 | self.state = .failed(error: error)
25 | self.hasError = true
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/Additional Views/SearchResultErrorView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchResultErrorView.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SearchResultErrorView: View {
11 | @State var title: String
12 | @State var error: String
13 |
14 | var body: some View {
15 | VStack {
16 | HStack {
17 | Image(systemName: "exclamationmark.triangle")
18 | .fontWeight(.semibold)
19 | .font(.title2)
20 | .foregroundColor(Color.highlightText)
21 | Text(title)
22 | .fontWeight(.semibold)
23 | .font(.title2)
24 | .foregroundColor(Color.highlightText)
25 | Spacer()
26 | }
27 | Divider()
28 | Text("Error searching: \(error)")
29 | }
30 | }
31 | }
32 |
33 | struct SearchResultErrorView_Previews: PreviewProvider {
34 | static var previews: some View {
35 | SearchResultErrorView(title: "Grades", error: "No internet connection.")
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/Additional Views/SearchResultLoadingView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchResultLoadingView.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SearchResultLoadingView: View {
11 | @State var title: String
12 |
13 | var body: some View {
14 | VStack {
15 | HStack {
16 | Image(systemName: "arrow.triangle.2.circlepath")
17 | .fontWeight(.semibold)
18 | .font(.title2)
19 | .foregroundColor(Color.highlightText)
20 | Text(title)
21 | .fontWeight(.semibold)
22 | .font(.title2)
23 | .foregroundColor(Color.highlightText)
24 | Spacer()
25 | }
26 | Divider()
27 | LoadingView(text: "Searching...")
28 | }
29 | }
30 | }
31 |
32 | struct SearchResultLoadingView_Previews: PreviewProvider {
33 | static var previews: some View {
34 | SearchResultLoadingView(title: "Grades")
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/Extensions and Custom Views/ExpandIcon.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExpandIcon.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 06.05.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ExpandIcon: View {
11 | @Binding var size: ResultSize
12 |
13 | var body: some View {
14 | HStack(alignment: .center) {
15 | Spacer()
16 | Button {
17 | withAnimation {
18 | switch size {
19 | case .big:
20 | self.size = .small
21 | case .small:
22 | self.size = .big
23 | }
24 | }
25 | } label: {
26 | if self.size == .small {
27 | Image(systemName: "arrow.up.left.and.arrow.down.right")
28 | } else {
29 | Image(systemName: "arrow.down.right.and.arrow.up.left")
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/Extensions and Custom Views/View+Search.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+Search.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 06.05.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | extension View {
11 | func searchStyle() -> some View {
12 | modifier(Search())
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/SearchResultViews/Grade/GradeSearchResultScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GradeSearchResultScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct GradesSearchResultScreen: View {
11 | @StateObject var vm: GradesSearchResultViewModel
12 | @Binding var query: String
13 | @State var size: ResultSize = .small
14 |
15 | var body: some View {
16 | Group {
17 | switch vm.state {
18 | case .success(let data):
19 | GradesSearchResultView(allResults: data, size: self.size)
20 | case .loading, .na:
21 | SearchResultLoadingView(title: "Grades")
22 | case .failed(let error):
23 | SearchResultErrorView(title: "Grades", error: error.localizedDescription)
24 | }
25 | }.onChange(of: query) { newQuery in
26 | Task {
27 | await vm.gradesSearch(for: newQuery)
28 | }
29 | }
30 | .task {
31 | await vm.gradesSearch(for: query)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/SearchResultViews/Lecture/LectureSearchResultScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LectureSearchResultScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LectureSearchResultScreen: View {
11 | @StateObject var vm: LectureSearchResultViewModel
12 | @Binding var query: String
13 | @State var size: ResultSize = .small
14 |
15 | var body: some View {
16 | Group {
17 | switch vm.state {
18 | case .success(let data):
19 | LectureSearchResultView(allResults: data, model: vm.model, size: self.size)
20 | case .loading, .na:
21 | SearchResultLoadingView(title: "Lectures")
22 | case .failed(let error):
23 | SearchResultErrorView(title: "Lectures", error: error.localizedDescription)
24 | }
25 | }.onChange(of: query) { newQuery in
26 | Task {
27 | await vm.lectureSearch(for: newQuery)
28 | }
29 | }.task {
30 | await vm.lectureSearch(for: query)
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/SearchResultViews/Person/PersonSearchResultScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PersonSearchResultScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @MainActor
11 | struct PersonSearchResultScreen: View {
12 | @StateObject var vm: PersonSearchResultViewModel
13 | @Binding var query: String
14 | @State var size: ResultSize = .small
15 |
16 | var body: some View {
17 | Group {
18 | switch vm.state {
19 | case .success(let data):
20 | PersonSearchResultView(allResults: data, model: vm.model, size: self.size)
21 | case .loading, .na:
22 | SearchResultLoadingView(title: "Person Search")
23 | case .failed(let error):
24 | SearchResultErrorView(title: "Person Search", error: error.localizedDescription)
25 | }
26 | }.onChange(of: query) { newQuery in
27 | Task {
28 | await vm.personSearch(for: newQuery)
29 | }
30 | }
31 | .task {
32 | await vm.personSearch(for: query)
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/SearchResultViews/RoomFinder/RoomFinderSearchResultScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomFinderSearchResultScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 07.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @MainActor
11 | struct RoomFinderSearchResultScreen: View {
12 | @StateObject var vm: RoomFinderSearchResultViewModel
13 | @Binding var query: String
14 | @State var size: ResultSize = .small
15 |
16 | var body: some View {
17 | Group {
18 | switch vm.state {
19 | case .success(let data):
20 | RoomFinderSearchResultView(allResults: data, size: self.size)
21 | case .loading, .na:
22 | SearchResultLoadingView(title: "RoomFinder")
23 | case .failed(let error):
24 | SearchResultErrorView(title: "RoomFinder", error: error.localizedDescription)
25 | }
26 | }.onChange(of: query) { newQuery in
27 | Task {
28 | await vm.roomFinderSearch(for: newQuery)
29 | }
30 | }.task {
31 | await vm.roomFinderSearch(for: query)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Campus-iOS/SearchComponent/Views/SearchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchView.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 27.12.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SearchView : View {
11 |
12 | @ObservedObject var model: Model
13 | @Binding var query: String
14 | @Environment(\.isSearching) var isSearching
15 | @ViewBuilder let content: () -> Content
16 |
17 | var body: some View {
18 | if isSearching {
19 | SearchResultView(vm: SearchResultViewModel(model: self.model))
20 | } else {
21 | content()
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Campus-iOS/Secrets.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Secrets.xcconfig
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 10.09.22.
6 | //
7 |
8 | // Configuration settings file format documentation can be found at:
9 | // https://help.apple.com/xcode/#/dev745c5c974
10 |
11 | ANALYTICS_API =
12 | ANALYTICS_POST_TOKEN =
13 |
--------------------------------------------------------------------------------
/Campus-iOS/Styles/Modifiers/RoundedCorners.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoundedCorners.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 16.07.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct RoundedCorner: Shape {
12 |
13 | var radius: CGFloat = .infinity
14 | var corners: UIRectCorner = .allCorners
15 |
16 | func path(in rect: CGRect) -> Path {
17 | let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
18 | return Path(path.cgPath)
19 | }
20 | }
21 |
22 | extension View {
23 | func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
24 | clipShape( RoundedCorner(radius: radius, corners: corners) )
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Campus-iOS/TUMSexyComponent/Model/TUMSexyLink.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMSexyLink.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 23.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct TUMSexyLink: Decodable, Identifiable {
11 | var id = UUID()
12 | var description: String?
13 | var target: String?
14 | var moodleID: String?
15 |
16 | enum CodingKeys: String, CodingKey {
17 | case description = "description"
18 | case target = "target"
19 | case moodleID = "moodleID"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Campus-iOS/TUMSexyComponent/Screen/TUMSexyScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMSexyScreen.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 23.01.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct TUMSexyScreen: View {
11 | @StateObject var vm = TUMSexyViewModel()
12 |
13 | var body: some View {
14 | Group {
15 | switch vm.state {
16 | case .success(let links):
17 | VStack {
18 | TUMSexyView(links: links)
19 | .refreshable {
20 | await vm.getLinks(forcedRefresh: true)
21 | }
22 | }
23 | case .loading, .na:
24 | LoadingView(text: "Fetching Links")
25 | case .failed(let error):
26 | FailedView(
27 | errorDescription: error.localizedDescription,
28 | retryClosure: vm.getLinks
29 | )
30 | }
31 | }.task {
32 | await vm.getLinks()
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Campus-iOS/TUMSexyComponent/Service/TUMSexyService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMSexyService.swift
3 | // Campus-iOS
4 | //
5 | // Created by David Lin on 23.01.23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct TUMSexyService: ServiceProtocol {
11 | typealias T = TUMSexyLink
12 |
13 | func fetch(forcedRefresh: Bool) async throws -> [TUMSexyLink] {
14 | let response: [String : TUMSexyLink] = try await MainAPI.makeRequest(endpoint: TUMSexyAPI.standard, forcedRefresh: forcedRefresh)
15 |
16 | var links = [TUMSexyLink]()
17 | response.values.forEach {
18 | if $0.target != nil && $0.description != nil {
19 | links.append($0)
20 | }
21 | }
22 |
23 | return links
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Campus-iOS/TUMSexyComponent/ViewModel/TUMSexyViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TUMSexyLink.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 13.01.22.
6 | //
7 |
8 | import Foundation
9 |
10 | @MainActor
11 | class TUMSexyViewModel: ObservableObject {
12 | @Published var state: APIState<[TUMSexyLink]> = .na
13 | @Published var hasError: Bool = false
14 |
15 | let service = TUMSexyService()
16 |
17 | func getLinks(forcedRefresh: Bool = false) async {
18 | if !forcedRefresh {
19 | self.state = .loading
20 | }
21 | self.hasError = false
22 |
23 | do {
24 | self.state = .success(
25 | data: try await service.fetch(forcedRefresh: forcedRefresh)
26 | )
27 | } catch {
28 | self.state = .failed(error: error)
29 | self.hasError = true
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Campus-iOS/TuitionComponent/View/TuitionView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TuitionView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Milen Vitanov on 08.02.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct TuitionView: View {
11 |
12 | let tuition: Tuition
13 |
14 | var body: some View {
15 | List {
16 | VStack(alignment: .center) {
17 | Spacer(minLength: 0.10 * UIScreen.main.bounds.width)
18 | TuitionCard(tuition: self.tuition)
19 | }
20 | .listRowBackground(Color.primaryBackground)
21 | }
22 | .scrollContentBackground(.hidden)
23 | .background(Color.primaryBackground)
24 | }
25 | }
26 |
27 | //struct TuitionView_Previews: PreviewProvider {
28 | //
29 | // static var previews: some View {
30 | // TuitionView(viewModel: ProfileViewModel())
31 | // TuitionView(viewModel: ProfileViewModel())
32 | // .preferredColorScheme(.dark)
33 | // }
34 | //}
35 |
--------------------------------------------------------------------------------
/Campus-iOS/WidgetComponent/Recommender/RecommenderError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecommenderError.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 28.09.22.
6 | //
7 |
8 | import Foundation
9 |
10 | enum RecommenderError: Error {
11 | case missingData, missingModel, modelCreationFailed, impossiblePrediction, badRecommendation, missingPermissions
12 | }
13 |
--------------------------------------------------------------------------------
/Campus-iOS/WidgetComponent/Recommender/Strategy/SpatioTemporalStrategy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SpatioTemporalStrategy.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 02.08.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct SpatioTemporalStrategy: WidgetRecommenderStrategy {
11 |
12 | func getRecommendation() async throws -> [WidgetRecommendation] {
13 | let timeStrategy = TimeStrategy()
14 | let locationStrategy = LocationStrategy()
15 |
16 | let timeRecommendations = try await timeStrategy.getRecommendation()
17 | let locationRecommendations = try await locationStrategy.getRecommendation()
18 |
19 | let recommendations = Widget.allCases.map {
20 | WidgetRecommendation(
21 | widget: $0,
22 | priority: priority(for: $0, in: timeRecommendations) + priority(for: $0, in: locationRecommendations)
23 | )
24 | }
25 |
26 | return recommendations.filter { $0.priority > 0 }
27 | }
28 |
29 | private func priority(for widget: Widget, in array: [WidgetRecommendation]) -> Int {
30 | return array.first(where: { $0.widget == widget } )?.priority ?? 0
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Campus-iOS/WidgetComponent/Recommender/Strategy/WidgetRecommenderStrategy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetRecommenderStrategy.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 24.07.22.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol WidgetRecommenderStrategy {
11 | func getRecommendation() async throws -> [WidgetRecommendation]
12 | }
13 |
--------------------------------------------------------------------------------
/Campus-iOS/WidgetComponent/Recommender/Widget.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Widget.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 24.07.22.
6 | //
7 |
8 | import Foundation
9 |
10 | enum Widget: CaseIterable {
11 | case cafeteria, studyRoom, calendar, tuition, grades, departures
12 |
13 | // Views associated to the widget in some way.
14 | // We can use this to make assumptions for widget recommendations, based on the views that the user visited.
15 | func associatedViews() -> [CampusAppView] {
16 | switch self {
17 | case .cafeteria:
18 | return [.cafeterias, .cafeteria]
19 | case .studyRoom:
20 | return [.studyRooms, .studyRoom]
21 | case .calendar:
22 | return [.calendar, .calendarEvent]
23 | case .tuition:
24 | return [.tuition]
25 | case .grades:
26 | return [.grades]
27 | case .departures:
28 | return [.departures]
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Campus-iOS/WidgetComponent/Recommender/WidgetRecommendation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetRecommendation.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 24.07.22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct WidgetRecommendation {
11 | let widget: Widget
12 | let priority: Int
13 | let id = UUID()
14 | }
15 |
--------------------------------------------------------------------------------
/Campus-iOS/WidgetComponent/Recommender/WidgetRecommender.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetRecommender.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 24.07.22.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | @MainActor
12 | class WidgetRecommender: ObservableObject {
13 |
14 | @Published var status: WidgetRecommenderStatus
15 | @Published var recommendations: [WidgetRecommendation]
16 |
17 | private let strategy: WidgetRecommenderStrategy
18 | private let model: Model
19 |
20 | init(strategy: WidgetRecommenderStrategy, model: Model) {
21 | self.strategy = strategy
22 | self.model = model
23 | self.status = .loading
24 | self.recommendations = []
25 | }
26 |
27 | func fetchRecommendations() async throws {
28 | let recommendations = try await strategy.getRecommendation().sorted(by: { $0.priority > $1.priority })
29 | self.recommendations = recommendations
30 | self.status = .success
31 | }
32 | }
33 |
34 | enum WidgetRecommenderStatus {
35 | case loading, success
36 | }
37 |
--------------------------------------------------------------------------------
/Campus-iOS/WidgetComponent/View/WidgetLoadingView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetLoadingView.swift
3 | // Campus-iOS
4 | //
5 | // Created by Robyn Kölle on 05.07.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct WidgetLoadingView: View {
11 |
12 | @Environment(\.colorScheme) private var colorScheme
13 | let text: String
14 |
15 | var body: some View {
16 | Rectangle()
17 | .foregroundColor(.secondaryBackground)
18 | .overlay {
19 | VStack {
20 | Text(text)
21 | ProgressView()
22 | }
23 | }
24 | }
25 | }
26 |
27 | struct WidgetLoadingView_Previews: PreviewProvider {
28 | static var previews: some View {
29 | WidgetLoadingView(text: "Loading Preview")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Campus-iOSUITests/Campus_iOSUITestsLaunchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Campus_iOSUITestsLaunchTests.swift
3 | // Campus-iOSUITests
4 | //
5 | // Created by Milen Vitanov on 01.12.21.
6 | //
7 |
8 | import XCTest
9 |
10 | class Campus_iOSUITestsLaunchTests: XCTestCase {
11 |
12 | override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 | true
14 | }
15 |
16 | override func setUpWithError() throws {
17 | continueAfterFailure = false
18 | }
19 |
20 | func testLaunch() throws {
21 | let app = XCUIApplication()
22 | app.launch()
23 |
24 | // Insert steps here to perform after app launch but before taking a screenshot,
25 | // such as logging into a test account or navigating somewhere in the app
26 |
27 | let attachment = XCTAttachment(screenshot: app.screenshot())
28 | attachment.name = "Launch Screen"
29 | attachment.lifetime = .keepAlways
30 | add(attachment)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Issue
2 |
3 |
4 | This fixes the following issue(s):
5 |
6 |
7 | ### Screenshots
8 |
9 |
--------------------------------------------------------------------------------
/default.profraw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/default.profraw
--------------------------------------------------------------------------------