-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor settings, profile picture and more (#232)
- Loading branch information
1 parent
185c05b
commit 92306e0
Showing
47 changed files
with
973 additions
and
681 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
public enum FeatureFlagBool: String, CaseIterable, FeatureFlag { | ||
public typealias Data = Bool | ||
|
||
case adsEnabled = "ads_enabled" | ||
case newResultsBannerEnabled = "new_results_banner_enabled" | ||
case ignoreManuallyEnteredHealthKitData = "ignore_manually_entered_health_kit_data" | ||
|
||
public var defaultValue: Data { false } | ||
} |
10 changes: 10 additions & 0 deletions
10
FCKit/Sources/FCKit/FeatureFlag/Flags/FeatureFlagDouble.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
public enum FeatureFlagDouble: String, FeatureFlag { | ||
public typealias Data = Double | ||
|
||
case databaseCacheTtl = "database_cache_ttl" | ||
case healthKitBackgroundDeliveryTimeoutMS = "health_kit_background_delivery_timeout_ms" | ||
case widgetUpdateIntervalS = "widget_update_interval_s" | ||
case dataUploadGracePeriodHours = "data_upload_grace_period_hours" | ||
|
||
public var defaultValue: Data { 0 } | ||
} |
17 changes: 17 additions & 0 deletions
17
FCKit/Sources/FCKit/FeatureFlag/Flags/FeatureFlagString.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import Foundation | ||
|
||
public enum FeatureFlagString: String, FeatureFlag { | ||
public typealias Data = String | ||
|
||
case googleAdsHomeScreenAdUnit = "google_ads_home_screen_ad_unit" | ||
case googleAdsExploreScreenAdUnit = "google_ads_explore_screen_ad_unit" | ||
case minimumAppVersion = "ios_minimum_app_version" | ||
|
||
public var defaultValue: Data { | ||
switch self { | ||
case .googleAdsHomeScreenAdUnit: return "" | ||
case .googleAdsExploreScreenAdUnit: return "" | ||
case .minimumAppVersion: return Bundle.main.version | ||
} | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
FriendlyCompetitions/AppServices/Storage/StorageAppService.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import ECKit | ||
import Factory | ||
|
||
final class StorageAppService: AppService { | ||
|
||
// MARK: - AppService | ||
|
||
func didFinishLaunching() { | ||
Task { [storageManager] in | ||
storageManager.clear(ttl: 60.days) | ||
} | ||
} | ||
|
||
// MARK: - Private | ||
|
||
@LazyInjected(\.storageManager) private var storageManager: StorageManaging | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 109 additions & 13 deletions
122
FriendlyCompetitions/Managers/Storage/StorageManager.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,130 @@ | ||
import Combine | ||
import ECKit | ||
import Factory | ||
import Files | ||
import FirebaseStorage | ||
import FirebaseStorageCombineSwift | ||
import Foundation | ||
|
||
// sourcery: AutoMockable | ||
protocol StorageManaging { | ||
func data(for storagePath: String) -> AnyPublisher<Data, Error> | ||
func get(_ path: String) -> AnyPublisher<Data, Error> | ||
func set(_ path: String, data: Data?) -> AnyPublisher<Void, Error> | ||
func clear(ttl: TimeInterval) | ||
} | ||
|
||
extension StorageManaging { | ||
func clear(ttl: TimeInterval = 0) { | ||
clear(ttl: ttl) | ||
} | ||
} | ||
|
||
final class StorageManager: StorageManaging { | ||
|
||
enum StorageManagerError: Error { | ||
case unknown | ||
case fileNotFound | ||
} | ||
|
||
private struct FileMetadata: Codable { | ||
let accessed: Date | ||
let updated: Date | ||
} | ||
|
||
// MARK: - Private Properties | ||
|
||
@Injected(\.storage) private var storage | ||
private let fileManager = FileManager.default | ||
private let storage = Storage.storage() | ||
|
||
@UserDefault("file-metadata", defaultValue: [:]) private var fileMetadata: [String: FileMetadata] | ||
|
||
init() { | ||
let environment = Container.shared.environmentManager.resolve().environment | ||
switch environment { | ||
case .prod: | ||
break | ||
case .debugLocal: | ||
storage.useEmulator(withHost: "localhost", port: 9199) | ||
case .debugRemote(let destination): | ||
storage.useEmulator(withHost: destination, port: 9199) | ||
} | ||
} | ||
|
||
// MARK: - Public Methods | ||
|
||
func data(for storagePath: String) -> AnyPublisher<Data, Error> { | ||
guard let documents = Folder.documents?.url else { | ||
return storage.data(path: storagePath) | ||
func get(_ path: String) -> AnyPublisher<Data, any Error> { | ||
let url = URL.cachesDirectory.appending(path: path) | ||
let cachedData = fileManager.contents(atPath: url.path(percentEncoded: false)) | ||
|
||
return .fromAsync { [weak self, fileManager, storage] in | ||
guard let self else { throw StorageManagerError.unknown } | ||
|
||
let reference = storage.reference(withPath: path) | ||
let serverMetadata = try await reference.getMetadata() | ||
let serverMetadataDate = serverMetadata.updated ?? serverMetadata.timeCreated ?? .now | ||
defer { fileMetadata[path] = FileMetadata(accessed: .now, updated: serverMetadataDate) } | ||
let url = URL.cachesDirectory.appending(path: path) | ||
|
||
if let fileMetadata = fileMetadata[path], | ||
serverMetadataDate <= fileMetadata.updated, | ||
fileManager.fileExists(atPath: url.path(percentEncoded: false)), | ||
let contents = fileManager.contents(atPath: url.path(percentEncoded: false)) { | ||
return contents | ||
} else { | ||
let url = try await reference.writeAsync(toFile: url) | ||
guard let contents = fileManager.contents(atPath: url.path(percentEncoded: false)) else { | ||
throw StorageManagerError.fileNotFound | ||
} | ||
return contents | ||
} | ||
} | ||
.prepend([cachedData].compacted()) | ||
.eraseToAnyPublisher() | ||
} | ||
|
||
let localPath = documents.appendingPathComponent(storagePath) | ||
let localData = try? Data(contentsOf: localPath) | ||
if let localData, !localData.isEmpty { | ||
return .just(localData) | ||
func set(_ path: String, data: Data?) -> AnyPublisher<Void, any Error> { | ||
Future { [fileManager, storage] promise in | ||
let reference = storage.reference(withPath: path) | ||
if let data { | ||
reference.putData(data, metadata: nil) { result in | ||
switch result { | ||
case .success(let success): | ||
let url = URL.cachesDirectory.appending(path: path) | ||
fileManager.createFile(atPath: url.path, contents: data) | ||
promise(.success(())) | ||
case .failure(let error): | ||
promise(.failure(error)) | ||
} | ||
} | ||
} else { | ||
reference.delete { error in | ||
if let error { | ||
promise(.failure(error)) | ||
} else { | ||
let url = URL.cachesDirectory.appending(path: path) | ||
try? fileManager.removeItem(at: url) | ||
promise(.success(())) | ||
} | ||
} | ||
} | ||
} | ||
.eraseToAnyPublisher() | ||
} | ||
|
||
func clear(ttl: TimeInterval) { | ||
let paths = fileMetadata | ||
.filter { _, metadata in metadata.accessed.addingTimeInterval(ttl) < .now } | ||
.keys | ||
|
||
return storage.data(path: storagePath) | ||
.handleEvents(receiveOutput: { _ = try? Folder.documents?.createFileIfNeeded(at: storagePath, contents: $0) }) | ||
.eraseToAnyPublisher() | ||
paths.forEach { path in | ||
fileMetadata.removeValue(forKey: path) | ||
let url = URL.cachesDirectory.appending(path: path) | ||
try? fileManager.removeItem(at: url) | ||
} | ||
} | ||
} | ||
|
||
private extension Int64 { | ||
var bytes: Int64 { self } | ||
var kb: Int64 { bytes * 1024 } | ||
var mb: Int64 { kb * 1024 } | ||
} |
Oops, something went wrong.