Commit a7e8fd9a authored by domenicw's avatar domenicw
Browse files

More networking work, including users, improvements to sessions, event signups and more

parent 6f8a842f
......@@ -82,6 +82,15 @@
B0F5B95A2171624D005E4591 /* GenericInfoViewControllerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B9592171624D005E4591 /* GenericInfoViewControllerAction.swift */; };
B0F5B95C2171786C005E4591 /* AMIVApiUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B95B2171786C005E4591 /* AMIVApiUser.swift */; };
B0F5B95F217179D9005E4591 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B95E217179D9005E4591 /* User.swift */; };
B0F5B9612171FD00005E4591 /* AmivMicroAppCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B9602171FD00005E4591 /* AmivMicroAppCellView.swift */; };
B0F5B963217205E7005E4591 /* EventTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B962217205E7005E4591 /* EventTableViewCell.swift */; };
B0F5B9652172132B005E4591 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B9642172132B005E4591 /* SessionManager.swift */; };
B0F5B967217217F3005E4591 /* EventsSignupResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B966217217F3005E4591 /* EventsSignupResponse.swift */; };
B0F5B9692172181C005E4591 /* EventSignup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B9682172181C005E4591 /* EventSignup.swift */; };
B0F5B96B217225EF005E4591 /* Gender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B96A217225EF005E4591 /* Gender.swift */; };
B0F5B96D217226CD005E4591 /* AMIVMemebership.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B96C217226CD005E4591 /* AMIVMemebership.swift */; };
B0F5B96F21722731005E4591 /* ETHDepartment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B96E21722731005E4591 /* ETHDepartment.swift */; };
B0F5B97121722CDA005E4591 /* UsersResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5B97021722CDA005E4591 /* UsersResponse.swift */; };
B0FE2EFC2154179600F3D073 /* JobsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FE2EFB2154179600F3D073 /* JobsViewController.swift */; };
B0FE2EFF21541A2B00F3D073 /* EventsNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FE2EFE21541A2B00F3D073 /* EventsNavigator.swift */; };
B0FE2F0221541A6300F3D073 /* EventsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FE2F0121541A6300F3D073 /* EventsViewController.swift */; };
......@@ -191,6 +200,15 @@
B0F5B9592171624D005E4591 /* GenericInfoViewControllerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericInfoViewControllerAction.swift; sourceTree = "<group>"; };
B0F5B95B2171786C005E4591 /* AMIVApiUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AMIVApiUser.swift; sourceTree = "<group>"; };
B0F5B95E217179D9005E4591 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
B0F5B9602171FD00005E4591 /* AmivMicroAppCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmivMicroAppCellView.swift; sourceTree = "<group>"; };
B0F5B962217205E7005E4591 /* EventTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTableViewCell.swift; sourceTree = "<group>"; };
B0F5B9642172132B005E4591 /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
B0F5B966217217F3005E4591 /* EventsSignupResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsSignupResponse.swift; sourceTree = "<group>"; };
B0F5B9682172181C005E4591 /* EventSignup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventSignup.swift; sourceTree = "<group>"; };
B0F5B96A217225EF005E4591 /* Gender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gender.swift; sourceTree = "<group>"; };
B0F5B96C217226CD005E4591 /* AMIVMemebership.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AMIVMemebership.swift; sourceTree = "<group>"; };
B0F5B96E21722731005E4591 /* ETHDepartment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ETHDepartment.swift; sourceTree = "<group>"; };
B0F5B97021722CDA005E4591 /* UsersResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersResponse.swift; sourceTree = "<group>"; };
B0FE2EFB2154179600F3D073 /* JobsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobsViewController.swift; sourceTree = "<group>"; };
B0FE2EFE21541A2B00F3D073 /* EventsNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsNavigator.swift; sourceTree = "<group>"; };
B0FE2F0121541A6300F3D073 /* EventsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsViewController.swift; sourceTree = "<group>"; };
......@@ -453,6 +471,7 @@
B050E170215179A30090CB79 /* AmivMicroAppsViewController.swift */,
B050E17421517E1B0090CB79 /* AmivMicroAppViewControllerDelegate.swift */,
B050E179215180D20090CB79 /* AmivMicroAppCell.swift */,
B0F5B9602171FD00005E4591 /* AmivMicroAppCellView.swift */,
);
path = "Amiv Micro Apps";
sourceTree = "<group>";
......@@ -633,6 +652,8 @@
children = (
B0AF91462157D36E008F3B80 /* EventsResponse.swift */,
B0AF914A2157DE2A008F3B80 /* AMIVEvent.swift */,
B0F5B966217217F3005E4591 /* EventsSignupResponse.swift */,
B0F5B9682172181C005E4591 /* EventSignup.swift */,
);
path = Events;
sourceTree = "<group>";
......@@ -667,6 +688,10 @@
isa = PBXGroup;
children = (
B0F5B95E217179D9005E4591 /* User.swift */,
B0F5B96A217225EF005E4591 /* Gender.swift */,
B0F5B96C217226CD005E4591 /* AMIVMemebership.swift */,
B0F5B96E21722731005E4591 /* ETHDepartment.swift */,
B0F5B97021722CDA005E4591 /* UsersResponse.swift */,
);
path = User;
sourceTree = "<group>";
......@@ -684,6 +709,7 @@
children = (
B0FE2F0121541A6300F3D073 /* EventsViewController.swift */,
B0FE2F1321550C4400F3D073 /* EventsViewControllerDelegate.swift */,
B0F5B962217205E7005E4591 /* EventTableViewCell.swift */,
);
path = Events;
sourceTree = "<group>";
......@@ -731,6 +757,7 @@
B0FE2F1C21552AC800F3D073 /* KeychainSwiftAccessOptions.swift */,
B0FE2F1E21552AF700F3D073 /* KeychainSwiftConstants.swift */,
B0AF91412157D192008F3B80 /* KeychainKey.swift */,
B0F5B9642172132B005E4591 /* SessionManager.swift */,
);
path = Keychain;
sourceTree = "<group>";
......@@ -856,6 +883,7 @@
B07A89FE2152316C003CC2D8 /* InfoViewController.swift in Sources */,
B0FE2EFF21541A2B00F3D073 /* EventsNavigator.swift in Sources */,
B0FE2F1B21552A9D00F3D073 /* KeychainSwift.swift in Sources */,
B0F5B9612171FD00005E4591 /* AmivMicroAppCellView.swift in Sources */,
B0FE2F0221541A6300F3D073 /* EventsViewController.swift in Sources */,
B050E17D2151910F0090CB79 /* LoginViewController.swift in Sources */,
B050E171215179A30090CB79 /* AmivMicroAppsViewController.swift in Sources */,
......@@ -880,6 +908,7 @@
B0E22FE1216E93EF002317D6 /* StudyDocumentResponse.swift in Sources */,
B050E15C215171F70090CB79 /* HomeNavigator.swift in Sources */,
B050E1972151AAC40090CB79 /* SettingsNavigator.swift in Sources */,
B0F5B97121722CDA005E4591 /* UsersResponse.swift in Sources */,
B0E22FDA216DD2E0002317D6 /* AMIVApiJobs.swift in Sources */,
B050E1942151A9750090CB79 /* Bundle+Extension.swift in Sources */,
B0F5B95F217179D9005E4591 /* User.swift in Sources */,
......@@ -888,15 +917,19 @@
B0FE2F0A2154237C00F3D073 /* GenericInfoViewControllerModel.swift in Sources */,
B050E18121519B390090CB79 /* LoginViewControllerDelegate.swift in Sources */,
B0845924215B78C700479D27 /* AmivMicroAppCheckin.swift in Sources */,
B0F5B96F21722731005E4591 /* ETHDepartment.swift in Sources */,
B0FE2F0D21543E6600F3D073 /* ImageViewerViewController.swift in Sources */,
B050E18E2151A5660090CB79 /* SettingsCellType.swift in Sources */,
B0AF91362157B4C7008F3B80 /* URLParameterEncoder.swift in Sources */,
B0FE2F0621541C5C00F3D073 /* GenericInfoViewController.swift in Sources */,
B0F5B967217217F3005E4591 /* EventsSignupResponse.swift in Sources */,
B0AF91472157D36E008F3B80 /* EventsResponse.swift in Sources */,
B0FE2F1221550C0100F3D073 /* JobsViewControllerDelegate.swift in Sources */,
B0AF914B2157DE2A008F3B80 /* AMIVEvent.swift in Sources */,
B0F5B963217205E7005E4591 /* EventTableViewCell.swift in Sources */,
B0845926215B797200479D27 /* AmivMicroApp.swift in Sources */,
B0F5B94F217137EC005E4591 /* Local+Extension.swift in Sources */,
B0F5B9652172132B005E4591 /* SessionManager.swift in Sources */,
B0FE2F082154230500F3D073 /* GenericInfoViewControllerDelegate.swift in Sources */,
B050E1852151A3700090CB79 /* SettingsViewController.swift in Sources */,
B0FE2F102154495100F3D073 /* BlurButton.swift in Sources */,
......@@ -908,10 +941,12 @@
B050E15921516E230090CB79 /* EventViewModel.swift in Sources */,
B0D3F92B21552E8E005209FF /* UIButton+Extension.swift in Sources */,
B0AF91322157B38F008F3B80 /* ParameterEncoding.swift in Sources */,
B0F5B96B217225EF005E4591 /* Gender.swift in Sources */,
B050E168215176D50090CB79 /* AmivMicroAppDelegate.swift in Sources */,
B0AF91292157B0A3008F3B80 /* EndPointType.swift in Sources */,
B050E144215169950090CB79 /* Navigator.swift in Sources */,
B050E17A215180D20090CB79 /* AmivMicroAppCell.swift in Sources */,
B0F5B96D217226CD005E4591 /* AMIVMemebership.swift in Sources */,
B07A8A0B21524474003CC2D8 /* LoginModel.swift in Sources */,
B048377E21582D4E00AFA689 /* String+Extension.swift in Sources */,
B0AF91382157B632008F3B80 /* JSONParameterEncoder.swift in Sources */,
......@@ -925,6 +960,7 @@
B07A8A022152384F003CC2D8 /* InfoItemModel.swift in Sources */,
B0FE2EFC2154179600F3D073 /* JobsViewController.swift in Sources */,
B050E17321517A050090CB79 /* AmivMicroAppType.swift in Sources */,
B0F5B9692172181C005E4591 /* EventSignup.swift in Sources */,
B0AF91402157CF50008F3B80 /* AMIVApiEvents.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
......
......@@ -15,13 +15,16 @@ public struct AmivMicroAppModel {
public let title: String
public let subtitle: String
public let image: UIImage?
// MARK: - Initializers
public init(app: AmivMicroAppType, title: String, image: UIImage?) {
public init(app: AmivMicroAppType, title: String, subtitle: String, image: UIImage?) {
self.app = app
self.title = title
self.subtitle = subtitle
self.image = image
}
......@@ -35,11 +38,11 @@ extension AmivMicroAppModel {
}
public static func createCheckinModel() -> AmivMicroAppModel {
return self.init(app: .checkin, title: "Checkin", image: nil)
return self.init(app: .checkin, title: "Checkin", subtitle: "Checkin AMIV Members in and out of AMIV events.", image: nil)
}
public static func createBarcodeModel() -> AmivMicroAppModel {
return self.init(app: .barcode, title: "Barcode", image: nil)
return self.init(app: .barcode, title: "Barcode", subtitle: "Display your legi barcode digitally.", image: nil)
}
}
......@@ -13,7 +13,7 @@ public enum KeychainKey: String {
case username
case password
case authToken
case sessionID
case userID
}
......
//
// SessionManager.swift
// Amiv
//
// Created by Domenic Wüthrich on 13.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
public class SessionManager {
/// Authentication Token for current session
public static var authToken: String? {
let keychain = KeychainSwift()
return keychain.get(KeychainKey.authToken.rawValue)
}
public static var userID: String? {
let keychain = KeychainSwift()
return keychain.get(KeychainKey.userID.rawValue)
}
/// Bool indicating if user is currently logged in
public static var isLoggedIn: Bool {
return SessionManager.authToken != nil
}
/// Destroys authentication token and session id from keychain store
public static func logout() {
let keychain = KeychainSwift()
keychain.delete(KeychainKey.authToken.rawValue)
keychain.delete(KeychainKey.userID.rawValue)
}
public static func save(_ session: AuthenticationResponse) {
let keychain = KeychainSwift()
keychain.set(session.token, forKey: KeychainKey.authToken.rawValue)
keychain.set(session.userID, forKey: KeychainKey.userID.rawValue)
keychain.synchronizable = true
}
}
......@@ -68,13 +68,8 @@ extension OnboardingNavigator: LoginViewControllerDelegate {
return
}
debugPrint(response.token)
// Save token into secure and encrypted keychain
let keychain = KeychainSwift()
keychain.set(response.token, forKey: KeychainKey.authToken.rawValue)
keychain.set(response.id, forKey: KeychainKey.sessionID.rawValue)
keychain.synchronizable = true
SessionManager.save(response)
DispatchQueue.main.async {
self.delegate?.onboardingFinished()
......
......@@ -21,8 +21,11 @@ public class AmivRootNavigator: RootNavigator {
public init(window: UIWindow) {
self.window = window
self.goToOnboarding()
if SessionManager.isLoggedIn {
self.goToApp()
} else {
self.goToOnboarding()
}
}
// MARK: - Navigation
......
......@@ -11,6 +11,7 @@ import Foundation
public enum AMIVApiEvents {
case events
case eventSignups
case media(_ path: String)
}
......@@ -21,6 +22,8 @@ extension AMIVApiEvents: EndPointType {
switch self {
case .events:
return "/events"
case .eventSignups:
return "/eventsignups"
case .media(let path):
return path
}
......@@ -28,21 +31,21 @@ extension AMIVApiEvents: EndPointType {
public var httpMethod: HTTPMethod {
switch self {
case .events, .media:
case .events, .media, .eventSignups:
return .get
}
}
public var task: HTTPTask {
switch self {
case .events, .media:
case .events, .media, .eventSignups:
return .request
}
}
public var headers: HTTPHeaders? {
switch self {
case .events, .media:
case .events, .media, .eventSignups:
return nil
}
}
......@@ -51,6 +54,8 @@ extension AMIVApiEvents: EndPointType {
switch self {
case .events, .media:
return false
case .eventSignups:
return true
}
}
......
......@@ -23,7 +23,7 @@ extension AMIVApiSession {
return "/sessions"
case .logout:
let keychain = KeychainSwift()
guard let id = keychain.get(KeychainKey.sessionID.rawValue) else {
guard let id = keychain.get(KeychainKey.userID.rawValue) else {
return "/sessions"
}
return "/sessions/\(id)"
......
......@@ -11,6 +11,7 @@ import Foundation
public enum AMIVApiUser {
case userInfo
case allUsers
}
......@@ -19,43 +20,41 @@ extension AMIVApiUser: EndPointType {
public var path: String {
switch self {
case .userInfo:
let keychain = KeychainSwift()
guard let id = keychain.get(KeychainKey.sessionID.rawValue) else {
return "/users"
if let id = SessionManager.userID {
return "/users/\(id)"
}
return "/users/\(id)"
return "/users/0"
case .allUsers:
return "/users"
}
}
public var httpMethod: HTTPMethod {
switch self {
case .userInfo:
case .userInfo, .allUsers:
return .get
}
}
public var task: HTTPTask {
switch self {
case .userInfo:
case .userInfo, .allUsers:
return .request
}
}
public var headers: HTTPHeaders? {
switch self {
case .userInfo:
case .userInfo, .allUsers:
return nil
}
}
public var isAuthenticationRequired: Bool {
switch self {
case .userInfo:
case .userInfo, .allUsers:
return true
}
}
}
......@@ -100,6 +100,31 @@ extension NetworkManager where EndPoint == AMIVApiEvents {
}
}
public func getEventSignups(_ completion: @escaping (_ image: Data?, _ error: String?) -> Void) {
router.request(.eventSignups) { (data, response, error) in
guard error == nil else {
completion(nil, error?.localizedDescription)
return
}
guard let response = response as? HTTPURLResponse else {
return
}
let result = self.handleNetworkRequest(response)
switch result {
case .success:
guard let responseData = data else {
completion(nil, NetworkResponse.noData.rawValue)
return
}
completion(responseData, nil)
case .failure(let error):
completion(nil, error)
}
}
}
}
extension NetworkManager where EndPoint == AMIVApiJobs {
......@@ -270,7 +295,35 @@ extension NetworkManager where EndPoint == AMIVApiSession {
extension NetworkManager where EndPoint == AMIVApiUser {
public func user(_ completion: @escaping (_ response: User?, _ error: String?) -> Void) {
public func getAllUsers(_ completion: @escaping (_ response: [User]?, _ error: String?) -> Void) {
router.request(.allUsers) { (data, response, error) in
guard error == nil else {
completion(nil, "Please check your network connection.")
return
}
if let response = response as? HTTPURLResponse {
let result = self.handleNetworkRequest(response)
switch result {
case .success:
guard let responseData = data else {
completion(nil, NetworkResponse.noData.rawValue)
return
}
do {
let apiResponse = try JSONDecoder().decode(UsersResponse.self, from: responseData)
completion(apiResponse.users, nil)
} catch {
completion(nil, NetworkResponse.unableToDecode.rawValue)
}
case .failure(let error):
completion(nil, error)
}
}
}
}
public func getUserInfo(_ completion: @escaping (_ response: User?, _ error: String?) -> Void) {
router.request(.userInfo) { (data, response, error) in
guard error == nil else {
completion(nil, "Please check your network connection.")
......
......@@ -10,23 +10,29 @@ import Foundation
public struct AuthenticationResponse {
public var userID: String
public var token: String
public var id: String
public var sessionId: String
public var etag: String
}
extension AuthenticationResponse: Decodable {
private enum AuthenticationResponseCodingKeys: String, CodingKey {
case userID = "user"
case token = "token"
case id = "_id"
case sessionId = "_id"
case etag = "_etag"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: AuthenticationResponseCodingKeys.self)
self.userID = try container.decode(String.self, forKey: .userID)
self.token = try container.decode(String.self, forKey: .token)
self.id = try container.decode(String.self, forKey: .id)
self.sessionId = try container.decode(String.self, forKey: .sessionId)
self.etag = try container.decode(String.self, forKey: .etag)
}
}
......@@ -17,7 +17,7 @@ public struct AMIVEvent {
public let startTime: Date
public let endTime: Date
public let description: String
public let price: Double
public let price: Int
public let spots: Int
public let signupCount: Int
public let image: AMIVMedia?
......@@ -74,7 +74,7 @@ extension AMIVEvent: Decodable {
let descriptionDe = try container.decode(String.self, forKey: .descriptionDe)
self.description = Locale.getLocalizedString(english: descriptionEn, german: descriptionDe)
self.price = try container.decode(Double.self, forKey: .price)
self.price = try container.decode(Int.self, forKey: .price)
self.spots = try container.decode(Int.self, forKey: .spots)
self.signupCount = try container.decode(Int.self, forKey: .signupCount)
......
//
// EventSignup.swift
// Amiv
//
// Created by Domenic Wüthrich on 13.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
public struct EventSignup {
public var event: String
public var user: String
public var email: String
public var isConfirmed: Bool
public var isAccepted: Bool
public var id: String
}
extension EventSignup: Decodable {
private enum EventSignupCodingKeys: String, CodingKey {
case event = "event"
case user = "user"
case email = "email"
case isConfirmed = "confirmed"
case isAccepted = "accepted"
case id = "_id"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: EventSignupCodingKeys.self)
self.event = try container.decode(String.self, forKey: .event)
self.user = try container.decode(String.self, forKey: .user)
self.email = try container.decode(String.self, forKey: .email)
self.isConfirmed = try container.decode(Bool.self, forKey: .isConfirmed)
self.isAccepted = try container.decode(Bool.self, forKey: .isAccepted)
self.id = try container.decode(String.self, forKey: .id)
}
}
......@@ -16,12 +16,12 @@ public struct EventsResponse {
extension EventsResponse: Decodable {
private enum EventsResponseCodingKeys: String, CodingKey {
private enum EventsSignupResponseCodingKeys: String, CodingKey {
case events = "_items"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: EventsResponseCodingKeys.self)
let container = try decoder.container(keyedBy: EventsSignupResponseCodingKeys.self)
self.events = try container.decode([AMIVEvent].self, forKey: .events)
}
......
//
// EventsSignupResponse.swift
// Amiv
//
// Created by Domenic Wüthrich on 13.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
public struct EventsSignupResponse {
public var eventSignups: [EventSignup]
}
extension EventsSignupResponse: Decodable {
private enum EventsSignupResponseCodingKeys: String, CodingKey {
case eventSignups = "_items"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: EventsSignupResponseCodingKeys.self)
self.eventSignups = try container.decode([EventSignup].self, forKey: .eventSignups)
}
}
//
// AMIVMemebership.swift
// Amiv
//
// Created by Domenic Wüthrich on 13.10.18.