Files
ichmagmaus111andClaude Opus 5.5 0622057a4f Ghostgram: open-source release
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 17:57:09 +01:00

95 lines
3.0 KiB
Swift

import Foundation
import SwiftSignalKit
import XCTest
/// A fresh temporary App Group for one test; removed in `tearDown`.
final class GGTestDirectory {
let path: String
init() {
let url = FileManager.default.temporaryDirectory.appendingPathComponent("GGCoreTests-\(UUID().uuidString)", isDirectory: true)
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
self.path = url.path
}
func subpath(_ components: String...) -> String {
var result = self.path
for component in components {
result = (result as NSString).appendingPathComponent(component)
}
return result
}
func makeDirectory(_ components: String...) -> String {
var result = self.path
for component in components {
result = (result as NSString).appendingPathComponent(component)
}
try? FileManager.default.createDirectory(atPath: result, withIntermediateDirectories: true, attributes: nil)
return result
}
func remove() {
try? FileManager.default.removeItem(atPath: self.path)
}
func environment(process: GGProcessKind = .app) -> GGCoreEnvironment {
return GGCoreEnvironment(appGroupPath: self.path, process: process)
}
var globalFilePath: String {
return self.subpath("ghostgram", "settings", "global.json")
}
var sharedFilePath: String {
return self.subpath("ghostgram", "settings", "shared.json")
}
func writeFile(_ path: String, _ text: String) {
let directory = (path as NSString).deletingLastPathComponent
try? FileManager.default.createDirectory(atPath: directory, withIntermediateDirectories: true, attributes: nil)
try? Data(text.utf8).write(to: URL(fileURLWithPath: path))
}
/// The `values` object of a settings file, or nil when the file is missing or damaged.
func storedValues(_ path: String) -> [String: Any]? {
guard let data = FileManager.default.contents(atPath: path) else {
return nil
}
guard let object = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
return nil
}
return object["values"] as? [String: Any]
}
}
/// Collects the values a signal delivers.
final class GGSignalRecorder<T> {
private let lock = NSLock()
private var storage: [T] = []
private var disposable: Disposable?
init(_ signal: Signal<T, NoError>) {
self.disposable = signal.start(next: { [weak self] value in
guard let self = self else {
return
}
self.lock.lock()
self.storage.append(value)
self.lock.unlock()
})
}
var values: [T] {
self.lock.lock()
let values = self.storage
self.lock.unlock()
return values
}
func dispose() {
self.disposable?.dispose()
self.disposable = nil
}
}