Skip to content

Commit

Permalink
Add Async methods for StorageAPI
Browse files Browse the repository at this point in the history
  • Loading branch information
sp4ce-cowboy committed Apr 21, 2024
1 parent ae7c96f commit 014670f
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 116 deletions.
4 changes: 4 additions & 0 deletions TowerForge/TowerForge.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
BA436AEE2BD42F8100BE3E4F /* RemoteStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA436AED2BD42F8100BE3E4F /* RemoteStorage.swift */; };
BA436AF02BD437D900BE3E4F /* LocalStorage+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA436AEF2BD437D900BE3E4F /* LocalStorage+Metadata.swift */; };
BA436AF22BD443A500BE3E4F /* RemoteStorage+Access.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA436AF12BD443A500BE3E4F /* RemoteStorage+Access.swift */; };
BA436AF42BD4AB8400BE3E4F /* StorageHandler+Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA436AF32BD4AB8400BE3E4F /* StorageHandler+Auth.swift */; };
BA443D3D2BAD9557009F0FFB /* RemoveSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA443D3C2BAD9557009F0FFB /* RemoveSystem.swift */; };
BA443D3F2BAD9774009F0FFB /* RemoveEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA443D3E2BAD9774009F0FFB /* RemoveEvent.swift */; };
BA443D422BAD9885009F0FFB /* DamageEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA443D412BAD9885009F0FFB /* DamageEventTests.swift */; };
Expand Down Expand Up @@ -488,6 +489,7 @@
BA436AED2BD42F8100BE3E4F /* RemoteStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteStorage.swift; sourceTree = "<group>"; };
BA436AEF2BD437D900BE3E4F /* LocalStorage+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LocalStorage+Metadata.swift"; sourceTree = "<group>"; };
BA436AF12BD443A500BE3E4F /* RemoteStorage+Access.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteStorage+Access.swift"; sourceTree = "<group>"; };
BA436AF32BD4AB8400BE3E4F /* StorageHandler+Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorageHandler+Auth.swift"; sourceTree = "<group>"; };
BA443D3C2BAD9557009F0FFB /* RemoveSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveSystem.swift; sourceTree = "<group>"; };
BA443D3E2BAD9774009F0FFB /* RemoveEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveEvent.swift; sourceTree = "<group>"; };
BA443D412BAD9885009F0FFB /* DamageEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamageEventTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1055,6 +1057,7 @@
isa = PBXGroup;
children = (
BA436AE92BD42F5400BE3E4F /* StorageHandler.swift */,
BA436AF32BD4AB8400BE3E4F /* StorageHandler+Auth.swift */,
BAEC99FB2BD15AAB00E0C437 /* StorageDatabase.swift */,
BA436AEB2BD42F7800BE3E4F /* LocalStorage.swift */,
BA436AEF2BD437D900BE3E4F /* LocalStorage+Metadata.swift */,
Expand Down Expand Up @@ -1624,6 +1627,7 @@
9B0406122BB889940026E903 /* PowerUpNode.swift in Sources */,
3CCF9CAF2BAB1A96004D170E /* SceneUpdateDelegate.swift in Sources */,
BA82C7402BC8674A000515A0 /* StatisticsFactory.swift in Sources */,
BA436AF42BD4AB8400BE3E4F /* StorageHandler+Auth.swift in Sources */,
3CAC4A692BB697A400A5D22E /* SpriteRenderStage.swift in Sources */,
523C29302BBD0916004C6EAC /* GameWaitingRoomViewController.swift in Sources */,
BA2F5AC12BC80BE500CBD8E9 /* Statistic.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,16 @@ class AuthenticationProvider {
observers.forEach { $0.onLogout() }
}

func getCurrentUserId() -> String? {
var currentUserId: String?
self.authenticationManager.getUserData { authData, _ in
currentUserId = authData?.userId
func getCurrentUserId(completion: @escaping (String?, Error?) -> Void) {
self.authenticationManager.getUserData { authData, error in
if let error = error {
Logger.log("Error retrieving CurrentUserId \(error)", self)
completion(nil, error)
} else if let playerId = authData?.userId {
Logger.log("Successfully retrieved currentUserId", self)
completion(playerId, nil)
}
}
return currentUserId
}
}

Expand Down
12 changes: 12 additions & 0 deletions TowerForge/TowerForge/Metrics/Statistics/Statistic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@ extension Statistic {
/// This extension allows Statistic to be merged
extension Statistic {

func merge(with that: Self) -> Self {
let this = self

let largerPermanent = Double.maximum(this.permanentValue, that.permanentValue)
let largerCurrent = Double.maximum(this.currentValue, that.currentValue)
let largerMaxCurrent = Double.maximum(this.maximumCurrentValue, that.maximumCurrentValue)

return Self(permanentValue: largerPermanent,
currentValue: largerCurrent,
maxCurrentValue: largerMaxCurrent)
}

static func merge(this: Self, that: Self) -> Self {
let largerPermanent = Double.maximum(this.permanentValue, that.permanentValue)
let largerCurrent = Double.maximum(this.currentValue, that.currentValue)
Expand Down
95 changes: 71 additions & 24 deletions TowerForge/TowerForge/StorageAPI/RemoteStorage+Access.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,6 @@ import Foundation
/// functions, for both storage and metadata.
extension RemoteStorage {

/// Checks if a player's metadata exists without requiring a closure input
/*static func checkIfPlayerMetadataExists(for playerId: String) -> Bool {
var exists = false

Self.remoteStorageExists(for: .Metadata, player: playerId) { bool in
exists = bool
}

return exists
}*/

static func checkIfPlayerMetadataExistsAsync(for playerId: String) async -> Bool {
await withCheckedContinuation { continuation in
Self.remoteStorageExists(for: .Metadata, player: playerId) { exists in
Expand All @@ -32,23 +21,48 @@ extension RemoteStorage {
}
}

/// Checks if a player's statistics exists without requiring a closure input
static func checkIfPlayerStorageExists(for playerId: String) -> Bool {
var exists = false

Self.remoteStorageExists(for: .Statistics, player: playerId) { bool in
exists = bool
static func checkIfRemotePlayerDataExists(playerId: String, completion: @escaping (Bool) -> Void) {
// Check for player storage existence
RemoteStorage.checkIfPlayerDatabaseExists(for: playerId) { storageExists in
// Check for player metadata existence
RemoteStorage.checkIfPlayerMetadataExists(for: playerId) { metadataExists in
// Check for any inconsistencies
if (storageExists && !metadataExists) || (!storageExists && metadataExists) {
Logger.log("Inconsistency error: Storage Exists: \(storageExists), Metadata Exists: \(metadataExists)")
}

// Return the combined result
completion(storageExists && metadataExists)
}
}
}

/// Checks if a player's metadata exists
static func checkIfPlayerMetadataExists(for playerId: String,
completion: @escaping (Bool) -> Void) {

return exists
Self.remoteStorageExists(for: .Metadata, player: playerId, completion: completion)
}

static func saveMetadataToFirebase(player: String, with inputData: Metadata) {
/// Checks if a player's statistics exists
static func checkIfPlayerDatabaseExists(for playerId: String,
completion: @escaping (Bool) -> Void) {

Self.remoteStorageExists(for: .Statistics, player: playerId, completion: completion)
}

/// Checks if a player's statistics exists without requiring a closure input

static func saveMetadataToFirebase(player: String,
with inputData: Metadata,
completion: @escaping (Bool) -> Void) {
let metadataCompletion: (Error?) -> Void = { error in
if let error = error {
Logger.log("Saving metadata to firebase error: \(error)", self)
completion(false)
} else {
Logger.log("Saving metadata to firebase success", self)
completion(true)
}
}

Expand All @@ -58,12 +72,16 @@ extension RemoteStorage {
completion: metadataCompletion)
}

static func saveStorageToFirebase(player: String, with inputData: StatisticsDatabase) {
static func saveDatabaseToFirebase(player: String,
with inputData: StatisticsDatabase,
completion: @escaping (Bool) -> Void) {
let storageCompletion: (Error?) -> Void = { error in
if let error = error {
Logger.log("Saving storage to firebase error: \(error)", self)
completion(false)
} else {
Logger.log("Saving storage to firebase success", self)
completion(true)
}
}

Expand All @@ -79,15 +97,15 @@ extension RemoteStorage {
if let error = error {
Logger.log("Deleting metadata from firebase error: \(error)", self)
} else {
Logger.log("Saving Metadata from firebase success", self)
Logger.log("Deleting Metadata from firebase success", self)
}
}

Self.deleteDataFromFirebase(for: .Metadata, player: player, completion: completion)
}

/// Deletes storage for the specific player from Firebase
static func deleteStorageFromFirebase(player: String) {
static func deleteDatabaseFromFirebase(player: String) {
let completion: (Error?) -> Void = { error in
if let error = error {
Logger.log("Deleting storage from firebase error: \(error)", self)
Expand All @@ -99,7 +117,36 @@ extension RemoteStorage {
Self.deleteDataFromFirebase(for: .Statistics, player: player, completion: completion)
}

static func loadStorageFromFirebase(player: String) -> StatisticsDatabase? {
static func loadMetadataFromFirebase(player: String,
completion: @escaping (Metadata?, Error?) -> Void) {
Self.checkIfPlayerDatabaseExists(for: player) { exists in
guard exists else {
completion(nil, nil) // Consider custom error
return
}

Self.loadDataFromFirebase(for: .Metadata, player: player) { (metadata: Metadata?, error: Error?) in
completion(metadata, error)
}
}
}

static func loadDatabaseFromFirebase(player: String,
completion: @escaping (StatisticsDatabase?, Error?) -> Void) {
Self.checkIfPlayerDatabaseExists(for: player) { exists in
guard exists else {
completion(nil, nil) // Consider custom error
return
}

Self.loadDataFromFirebase(for: .Statistics,
player: player) { (statistics: StatisticsDatabase?, error: Error?) in
completion(statistics, error)
}
}
}

/*static func loadStorageFromFirebase(player: String) -> StatisticsDatabase? {
guard Self.checkIfPlayerStorageExists(for: player) else {
return nil
}
Expand Down Expand Up @@ -141,5 +188,5 @@ extension RemoteStorage {
}

return metadata
}
}*/
}
169 changes: 169 additions & 0 deletions TowerForge/TowerForge/StorageAPI/StorageHandler+Auth.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//
// StorageHandler+Auth.swift
// TowerForge
//
// Created by Rubesh on 21/4/24.
//

import Foundation

/// This extension adds authentication methods to StorageHandler
extension StorageHandler {
/*func onLogin() {
Task {
onAsyncLogin()
}
}*/

func onLogin() {
Logger.log("LOGIN: IS CALLED FROM STORAGE HANDLER", Self.self)

authenticationProvider.getCurrentUserId { [weak self] userId, error in
guard let self = self else {
return
}

if let error = error {
Logger.log("IMPT: onLogin failed due to error: \(error.localizedDescription) from STORAGE_HANDLER", self)
return
}

guard let userId = userId else {
Logger.log("IMPT: onLogin failed due to userId nil from STORAGE_HANDLER", self)
return
}

// Update the playerId and metadata locally
self.localUpdatePlayerIdAndMetadata(with: userId)

// Asynchronously check if remote data exists for currentPlayerId
self.checkIfRemotePlayerDataExists { exists in
if exists {
self.onReLogin { reloginSuccess in
if !reloginSuccess {
Logger.log("RE-LOGIN FAILED: Remote player exists but Re-login failure", self)
}
}
} else {
self.onFirstLogin()
}
}
}
}

/// Helper function to asynchronously check if both Metadata and Storage exist
func checkIfRemotePlayerDataExists(completion: @escaping (Bool) -> Void) {
RemoteStorage.checkIfRemotePlayerDataExists(playerId: Self.currentPlayerId, completion: completion)
}

func onFirstLogin() {
self.remoteSave()
}

/// Returns true if re-login success, false otherwise
func onReLogin(completion: @escaping (Bool) -> Void) {
RemoteStorage.loadMetadataFromFirebase(player: Self.currentPlayerId) { remoteMetadata, _ in
guard let remoteMetadata = remoteMetadata else {
Logger.log("RELOGIN ERROR: REMOTE METADATA NOT FOUND")
completion(false)
return
}

RemoteStorage.loadDatabaseFromFirebase(player: Self.currentPlayerId) { remoteStorage, _ in
guard let remoteStorage = remoteStorage else {
Logger.log("RELOGIN ERROR: REMOTE STORAGE NOT FOUND")
completion(false)
return
}

guard let finalStorage = StatisticsDatabase.merge(this: remoteStorage, that: self.statisticsDatabase) else {
Logger.log("RELOGIN ERROR: REMOTE STORAGE NOT FOUND")
completion(false)
return
}

LocalStorage.saveDatabaseToLocalStorage(finalStorage)
RemoteStorage.saveDatabaseToFirebase(player: Self.currentPlayerId,
with: finalStorage) { saveStorageSuccess in
if !saveStorageSuccess {
completion(false)
return
}

self.metadata = remoteMetadata
RemoteStorage.saveMetadataToFirebase(player: Self.currentPlayerId, with: self.metadata) {
saveMetadataSuccess in

completion(saveMetadataSuccess)
}
}
}
}
}

func localUpdatePlayerIdAndMetadata(with userId: String) {
Constants.CURRENT_PLAYER_ID = userId
metadata.updateIdentifierToCurrentID()
self.localSave()
}

func onLogout() {
Logger.log("LOGOUT: CALLED FROM STORAGE_HANDLER", Self.self)
self.save() // Save any potential unsaved changes
metadata.resetIdentifier() // Reset metadata to original value
Logger.log("LOGOUT: metadata reset to \(metadata.uniqueIdentifier)", Self.self)
self.save() // Save updated metadata
}

/// Returns true if re-login success, false otherwise
/*func onReLogin() -> Bool {
// Fetch metadata
guard let remoteMetadata = RemoteStorage.loadMetadataFromFirebase(player: Self.currentPlayerId) else {
Logger.log("RELOGIN ERROR: REMOTE METADATA NOT FOUND")
return false
}

// Fetch storage
guard let remoteStorage = RemoteStorage.loadDatabaseFromFirebase(player: Self.currentPlayerId) else {
Logger.log("RELOGIN ERROR: REMOTE STORAGE NOT FOUND")
return false
}

var finalStorage = StatisticsDatabase.merge(this: remoteStorage, that: statisticsDatabase)

// Merge storage - MERGE
// Merge metadata - KEEP LATEST
// Set storage to merged storage
// Save new storage to file
// Universal save will automatically update metadata
// Universal save to remote

return true

}*/

/*func onReLoginAsync() async -> Bool {
do {
// Fetch metadata and storage asynchronously
let remoteMetadata = try await RemoteStorage.loadMetadataFromFirebase(player: Self.currentPlayerId)
let remoteStorage = try await RemoteStorage.loadDatabaseFromFirebase(player: Self.currentPlayerId)

// Merge storage - assume a static merge function in StatisticsDatabase
let finalStorage = StatisticsDatabase.merge(this: remoteStorage, that: statisticsDatabase)

// Save merged storage locally and remotely
LocalStorage.saveDatabaseToLocal(finalStorage) // Assuming there's a method to save locally
try await RemoteStorage.saveDatabaseToFirebase(player: Self.currentPlayerId, with: finalStorage)

// Update metadata locally and remotely
// Assuming newer metadata is better or merge strategy is implemented
metadata = remoteMetadata
try await RemoteStorage.saveMetadataToFirebase(player: Self.currentPlayerId, with: metadata)

return true
} catch {
Logger.log("RELOGIN ERROR: \(error)")
return false
}
}*/
}
Loading

0 comments on commit 014670f

Please sign in to comment.