Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

profiling API get requests #110

Merged
merged 3 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mobile/mobile.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = voynow.mobile;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down Expand Up @@ -310,7 +310,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = voynow.mobile;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
30 changes: 25 additions & 5 deletions mobile/mobile/APIManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,24 @@ import Foundation

class APIManager {
static let shared = APIManager()
private init() {}
private init() {
// Configure session for connection reuse
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
config.timeoutIntervalForResource = 300
config.httpMaximumConnectionsPerHost = 6
config.waitsForConnectivity = true
session = URLSession(configuration: config)
}

private let session: URLSession
private let baseURL = "https://lwg77yq7dd.execute-api.us-east-1.amazonaws.com/prod/signup"

func fetchProfileData(token: String, completion: @escaping (Result<ProfileData, Error>) -> Void) {
let startTime = Date()
let body: [String: Any] = ["jwt_token": token, "method": "get_profile"]
performRequest(body: body, responseType: ProfileResponse.self) { result in
let totalTime = Date().timeIntervalSince(startTime)
switch result {
case .success(let response):
if response.success, let profile = response.profile {
Expand All @@ -29,8 +40,11 @@ class APIManager {
func fetchTrainingWeekData(
token: String, completion: @escaping (Result<TrainingWeekData, Error>) -> Void
) {
let startTime = Date()
let body: [String: Any] = ["jwt_token": token, "method": "get_training_week"]

performRequest(body: body, responseType: TrainingWeekResponse.self) { result in
let totalTime = Date().timeIntervalSince(startTime)
switch result {
case .success(let response):
if response.success, let trainingWeekString = response.trainingWeek,
Expand Down Expand Up @@ -118,8 +132,11 @@ class APIManager {
func fetchWeeklySummaries(
token: String, completion: @escaping (Result<[WeekSummary], Error>) -> Void
) {
let startTime = Date()
let body: [String: Any] = ["jwt_token": token, "method": "get_weekly_summaries"]

performRequest(body: body, responseType: WeeklySummariesResponse.self) { result in
let totalTime = Date().timeIntervalSince(startTime)
switch result {
case .success(let response):
if response.success, let summariesStrings = response.weekly_summaries {
Expand Down Expand Up @@ -161,7 +178,8 @@ class APIManager {
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: body)

URLSession.shared.dataTask(with: request) { data, response, error in
// Use shared session instead of URLSession.shared
session.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
Expand Down Expand Up @@ -193,7 +211,9 @@ class APIManager {
completion(.success(()))
} else {
let errorMessage = response.message ?? "Failed to start onboarding"
completion(.failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: errorMessage])))
completion(
.failure(
NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: errorMessage])))
}
case .failure(let error):
completion(.failure(error))
Expand All @@ -203,6 +223,6 @@ class APIManager {
}

struct GenericResponse: Codable {
let success: Bool
let message: String?
let success: Bool
let message: String?
}
13 changes: 2 additions & 11 deletions mobile/mobile/AppState.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import SwiftUI

class AppState: ObservableObject {
@Published var status: AppStateStatus = .loggedOut {
didSet {
print("AppState status changed to: \(status)")
}
}

@Published var jwtToken: String? = nil {
didSet {
print("AppState jwtToken changed to: \(jwtToken ?? "nil")")
}
}
@Published var status: AppStateStatus = .loggedOut
@Published var jwtToken: String? = nil
}
30 changes: 11 additions & 19 deletions mobile/mobile/DashboardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import SwiftUI
struct DashboardView: View {
@EnvironmentObject var appState: AppState
@State private var trainingWeekData: TrainingWeekData?
@State private var isLoadingTrainingWeek: Bool = true
@State private var showProfile: Bool = false
@State private var weeklySummaries: [WeekSummary]?
@State private var isLoadingTrainingWeek = true
@State private var showProfile: Bool = false
@State private var showOnboarding: Bool = false
@State private var showErrorAlert: Bool = false
@State private var errorMessage: String = ""
Expand All @@ -19,17 +19,18 @@ struct DashboardView: View {
.zIndex(1)

ScrollView {
if isLoadingTrainingWeek {
if let data = trainingWeekData {
// Show training week as soon as it's available
TrainingWeekView(
trainingWeekData: data,
weeklySummaries: weeklySummaries // Can be nil
)
} else if isLoadingTrainingWeek {
DashboardSkeletonView()
} else if let data = trainingWeekData, let summaries = weeklySummaries {
TrainingWeekView(trainingWeekData: data, weeklySummaries: summaries)
} else {
Text("No training data available")
.font(.headline)
.foregroundColor(ColorTheme.lightGrey)
Button(action: handleLogout) {
Text("Logout")
}
}
}
.refreshable {
Expand Down Expand Up @@ -67,21 +68,12 @@ struct DashboardView: View {

private func fetchData() {
isLoadingTrainingWeek = true
let group = DispatchGroup()

group.enter()
fetchWeeklySummaries {
group.leave()
}

group.enter()
fetchTrainingWeekData {
group.leave()
isLoadingTrainingWeek = false
}

group.notify(queue: .main) {
self.isLoadingTrainingWeek = false
}
fetchWeeklySummaries {}
}

private func fetchTrainingWeekData(completion: @escaping () -> Void) {
Expand Down
61 changes: 51 additions & 10 deletions mobile/mobile/ProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ struct ProfileView: View {
@Binding var showProfile: Bool
@State private var isSaving: Bool = false
@State private var isLoading: Bool = true
@State private var lastFetchTime: Date?
private let cacheTimeout: TimeInterval = 300

var body: some View {
ZStack {
Expand Down Expand Up @@ -68,27 +70,41 @@ struct ProfileView: View {
let preferencesString = String(data: preferencesJSON, encoding: .utf8)
{
profileData?.preferences = preferencesString
ProfileCache.updatePreferences(preferencesString)
}
}
)
}

private func shouldRefetchData() -> Bool {
guard let lastFetch = lastFetchTime else {
return true
}
let timeSinceLastFetch = Date().timeIntervalSince(lastFetch)
let shouldRefetch = timeSinceLastFetch > cacheTimeout
return shouldRefetch
}

private func fetchProfileData() {
guard let token = appState.jwtToken else {
isLoading = false
return
isLoading = false
return
}

if !ProfileCache.shouldRefetch() && ProfileCache.data != nil {
self.profileData = ProfileCache.data
isLoading = false
return
}

APIManager.shared.fetchProfileData(token: token) { result in
DispatchQueue.main.async {
self.isLoading = false
switch result {
case .success(let profile):
self.profileData = profile
case .failure(let error):
print("Error fetching profile data: \(error)")
DispatchQueue.main.async {
self.isLoading = false
if case .success(let profile) = result {
ProfileCache.update(profile)
self.profileData = profile
}
}
}
}
}
}
Expand Down Expand Up @@ -197,3 +213,28 @@ struct LoadingIcon: View {
}
}
}

private enum ProfileCache {
static var lastFetchTime: Date?
static var data: ProfileData?
static let timeout: TimeInterval = 300

static func shouldRefetch() -> Bool {
guard let lastFetch = lastFetchTime else { return true }
return Date().timeIntervalSince(lastFetch) > timeout
}

static func update(_ profile: ProfileData) {
data = profile
lastFetchTime = Date()
}

static func updatePreferences(_ preferences: String) {
data?.preferences = preferences
}

static func clear() {
data = nil
lastFetchTime = nil
}
}
Loading
Loading