mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-20 14:30:51 +02:00
Update Ghostgram features
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "SGSimpleSettings",
|
||||
module_name = "SGSimpleSettings",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//Swiftgram/SGAppGroupIdentifier:SGAppGroupIdentifier",
|
||||
"//Swiftgram/SGLogging:SGLogging",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
//// A copy of Atomic from SwiftSignalKit
|
||||
//import Foundation
|
||||
//
|
||||
//public enum AtomicWrapperLockError: Error {
|
||||
// case isLocked
|
||||
//}
|
||||
//
|
||||
//public final class AtomicWrapper<T> {
|
||||
// private var lock: pthread_mutex_t
|
||||
// private var value: T
|
||||
//
|
||||
// public init(value: T) {
|
||||
// self.lock = pthread_mutex_t()
|
||||
// self.value = value
|
||||
//
|
||||
// pthread_mutex_init(&self.lock, nil)
|
||||
// }
|
||||
//
|
||||
// deinit {
|
||||
// pthread_mutex_destroy(&self.lock)
|
||||
// }
|
||||
//
|
||||
// public func with<R>(_ f: (T) -> R) -> R {
|
||||
// pthread_mutex_lock(&self.lock)
|
||||
// let result = f(self.value)
|
||||
// pthread_mutex_unlock(&self.lock)
|
||||
//
|
||||
// return result
|
||||
// }
|
||||
//
|
||||
// public func tryWith<R>(_ f: (T) -> R) throws -> R {
|
||||
// if pthread_mutex_trylock(&self.lock) == 0 {
|
||||
// let result = f(self.value)
|
||||
// pthread_mutex_unlock(&self.lock)
|
||||
// return result
|
||||
// } else {
|
||||
// throw AtomicWrapperLockError.isLocked
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public func modify(_ f: (T) -> T) -> T {
|
||||
// pthread_mutex_lock(&self.lock)
|
||||
// let result = f(self.value)
|
||||
// self.value = result
|
||||
// pthread_mutex_unlock(&self.lock)
|
||||
//
|
||||
// return result
|
||||
// }
|
||||
//
|
||||
// public func swap(_ value: T) -> T {
|
||||
// pthread_mutex_lock(&self.lock)
|
||||
// let previous = self.value
|
||||
// self.value = value
|
||||
// pthread_mutex_unlock(&self.lock)
|
||||
//
|
||||
// return previous
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// RWLock.swift
|
||||
// SwiftConcurrentCollections
|
||||
//
|
||||
// Created by Pete Prokop on 09/02/2020.
|
||||
// Copyright © 2020 Pete Prokop. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class RWLock {
|
||||
private var lock: pthread_rwlock_t
|
||||
|
||||
// MARK: Lifecycle
|
||||
deinit {
|
||||
pthread_rwlock_destroy(&lock)
|
||||
}
|
||||
|
||||
public init() {
|
||||
lock = pthread_rwlock_t()
|
||||
pthread_rwlock_init(&lock, nil)
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
public func writeLock() {
|
||||
pthread_rwlock_wrlock(&lock)
|
||||
}
|
||||
|
||||
public func readLock() {
|
||||
pthread_rwlock_rdlock(&lock)
|
||||
}
|
||||
|
||||
public func unlock() {
|
||||
pthread_rwlock_unlock(&lock)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
import Foundation
|
||||
import SGAppGroupIdentifier
|
||||
import SGLogging
|
||||
|
||||
let APP_GROUP_IDENTIFIER = sgAppGroupIdentifier()
|
||||
|
||||
public class SGSimpleSettings {
|
||||
|
||||
public static let shared = SGSimpleSettings()
|
||||
|
||||
private init() {
|
||||
setDefaultValues()
|
||||
migrate()
|
||||
preCacheValues()
|
||||
}
|
||||
|
||||
private func setDefaultValues() {
|
||||
UserDefaults.standard.register(defaults: SGSimpleSettings.defaultValues)
|
||||
// Just in case group defaults will be nil
|
||||
UserDefaults.standard.register(defaults: SGSimpleSettings.groupDefaultValues)
|
||||
if let groupUserDefaults = UserDefaults(suiteName: APP_GROUP_IDENTIFIER) {
|
||||
groupUserDefaults.register(defaults: SGSimpleSettings.groupDefaultValues)
|
||||
}
|
||||
}
|
||||
|
||||
private func migrate() {
|
||||
let showRepostToStoryMigrationKey = "migrated_\(Keys.showRepostToStory.rawValue)"
|
||||
if let groupUserDefaults = UserDefaults(suiteName: APP_GROUP_IDENTIFIER) {
|
||||
if !groupUserDefaults.bool(forKey: showRepostToStoryMigrationKey) {
|
||||
self.showRepostToStoryV2 = self.showRepostToStory
|
||||
groupUserDefaults.set(true, forKey: showRepostToStoryMigrationKey)
|
||||
SGLogger.shared.log("SGSimpleSettings", "Migrated showRepostToStory. \(self.showRepostToStory) -> \(self.showRepostToStoryV2)")
|
||||
}
|
||||
} else {
|
||||
SGLogger.shared.log("SGSimpleSettings", "Unable to migrate showRepostToStory. Shared UserDefaults suite is not available for '\(APP_GROUP_IDENTIFIER)'.")
|
||||
}
|
||||
|
||||
let chatListLinesMigrationKey = "migrated_\(Keys.chatListLines.rawValue)"
|
||||
if !UserDefaults.standard.bool(forKey: chatListLinesMigrationKey) {
|
||||
let legacyCompactMessagePreviewKey = "compactMessagePreview"
|
||||
if UserDefaults.standard.object(forKey: legacyCompactMessagePreviewKey) != nil {
|
||||
if UserDefaults.standard.bool(forKey: legacyCompactMessagePreviewKey) {
|
||||
self.chatListLines = ChatListLines.one.rawValue
|
||||
}
|
||||
UserDefaults.standard.removeObject(forKey: legacyCompactMessagePreviewKey)
|
||||
SGLogger.shared.log("SGSimpleSettings", "Migrated compactMessagePreview -> chatListLines. \(self.chatListLines)")
|
||||
}
|
||||
UserDefaults.standard.set(true, forKey: chatListLinesMigrationKey)
|
||||
}
|
||||
}
|
||||
|
||||
private func preCacheValues() {
|
||||
// let dispatchGroup = DispatchGroup()
|
||||
|
||||
let tasks = [
|
||||
// { let _ = self.allChatsFolderPositionOverride },
|
||||
{ let _ = self.tabBarSearchEnabled },
|
||||
{ let _ = self.allChatsHidden },
|
||||
{ let _ = self.hideTabBar },
|
||||
{ let _ = self.bottomTabStyle },
|
||||
{ let _ = self.compactChatList },
|
||||
{ let _ = self.chatListLines },
|
||||
{ let _ = self.compactFolderNames },
|
||||
{ let _ = self.disableSwipeToRecordStory },
|
||||
{ let _ = self.rememberLastFolder },
|
||||
{ let _ = self.quickTranslateButton },
|
||||
{ let _ = self.stickerSize },
|
||||
{ let _ = self.stickerTimestamp },
|
||||
{ let _ = self.hideReactions },
|
||||
{ let _ = self.disableGalleryCamera },
|
||||
{ let _ = self.disableSendAsButton },
|
||||
{ let _ = self.disableSnapDeletionEffect },
|
||||
{ let _ = self.startTelescopeWithRearCam },
|
||||
{ let _ = self.hideRecordingButton },
|
||||
{ let _ = self.inputToolbar },
|
||||
{ let _ = self.dismissedSGSuggestions },
|
||||
{ let _ = self.customAppBadge }
|
||||
]
|
||||
|
||||
tasks.forEach { task in
|
||||
DispatchQueue.global(qos: .background).async(/*group: dispatchGroup*/) {
|
||||
task()
|
||||
}
|
||||
}
|
||||
|
||||
// dispatchGroup.notify(queue: DispatchQueue.main) {}
|
||||
}
|
||||
|
||||
public func synchronizeShared() {
|
||||
if let groupUserDefaults = UserDefaults(suiteName: APP_GROUP_IDENTIFIER) {
|
||||
groupUserDefaults.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
public enum Keys: String, CaseIterable {
|
||||
case hidePhoneInSettings
|
||||
case showTabNames
|
||||
case startTelescopeWithRearCam
|
||||
case accountColorsSaturation
|
||||
case uploadSpeedBoost
|
||||
case downloadSpeedBoost
|
||||
case bottomTabStyle
|
||||
case rememberLastFolder
|
||||
case lastAccountFolders
|
||||
case localDNSForProxyHost
|
||||
case sendLargePhotos
|
||||
case outgoingPhotoQuality
|
||||
case storyStealthMode
|
||||
case canUseStealthMode
|
||||
case disableSwipeToRecordStory
|
||||
case quickTranslateButton
|
||||
case outgoingLanguageTranslation
|
||||
case hideReactions
|
||||
case showRepostToStory
|
||||
case showRepostToStoryV2
|
||||
case contextShowSelectFromUser
|
||||
case contextShowSaveToCloud
|
||||
case contextShowRestrict
|
||||
// case contextShowBan
|
||||
case contextShowHideForwardName
|
||||
case contextShowReport
|
||||
case contextShowReply
|
||||
case contextShowPin
|
||||
case contextShowSaveMedia
|
||||
case contextShowMessageReplies
|
||||
case contextShowJson
|
||||
case disableScrollToNextChannel
|
||||
case disableScrollToNextTopic
|
||||
case disableChatSwipeOptions
|
||||
case disableDeleteChatSwipeOption
|
||||
case disableGalleryCamera
|
||||
case disableGalleryCameraPreview
|
||||
case disableSendAsButton
|
||||
case disableSnapDeletionEffect
|
||||
case stickerSize
|
||||
case stickerTimestamp
|
||||
case hideRecordingButton
|
||||
case hideTabBar
|
||||
case showDC
|
||||
case showCreationDate
|
||||
case showRegDate
|
||||
case regDateCache
|
||||
case compactChatList
|
||||
case chatListLines
|
||||
case compactFolderNames
|
||||
case allChatsTitleLengthOverride
|
||||
// case allChatsFolderPositionOverride
|
||||
case allChatsHidden
|
||||
case defaultEmojisFirst
|
||||
case messageDoubleTapActionOutgoing
|
||||
case wideChannelPosts
|
||||
case forceEmojiTab
|
||||
case forceBuiltInMic
|
||||
case secondsInMessages
|
||||
case hideChannelBottomButton
|
||||
case forceSystemSharing
|
||||
case confirmCalls
|
||||
case videoPIPSwipeDirection
|
||||
case legacyNotificationsFix
|
||||
case messageFilterKeywords
|
||||
case inputToolbar
|
||||
case pinnedMessageNotifications
|
||||
case mentionsAndRepliesNotifications
|
||||
case primaryUserId
|
||||
case status
|
||||
case dismissedSGSuggestions
|
||||
case duckyAppIconAvailable
|
||||
case transcriptionBackend
|
||||
case translationBackend
|
||||
case customAppBadge
|
||||
case canUseNY
|
||||
case nyStyle
|
||||
case wideTabBar
|
||||
case tabBarSearchEnabled
|
||||
case hideStories
|
||||
case warnOnStoriesOpen
|
||||
case showProfileId
|
||||
case sendWithReturnKey
|
||||
}
|
||||
|
||||
public enum DownloadSpeedBoostValues: String, CaseIterable {
|
||||
case none
|
||||
case medium
|
||||
case maximum
|
||||
}
|
||||
|
||||
public enum BottomTabStyleValues: String, CaseIterable {
|
||||
case telegram
|
||||
case ios
|
||||
}
|
||||
|
||||
public enum AllChatsTitleLengthOverride: String, CaseIterable {
|
||||
case none
|
||||
case short
|
||||
case long
|
||||
}
|
||||
|
||||
public enum AllChatsFolderPositionOverride: String, CaseIterable {
|
||||
case none
|
||||
case last
|
||||
case hidden
|
||||
}
|
||||
|
||||
public enum ChatListLines: String, CaseIterable {
|
||||
case three = "3"
|
||||
case two = "2"
|
||||
case one = "1"
|
||||
|
||||
public static let defaultValue: ChatListLines = .three
|
||||
}
|
||||
|
||||
public enum MessageDoubleTapAction: String, CaseIterable {
|
||||
case `default`
|
||||
case none
|
||||
case edit
|
||||
}
|
||||
|
||||
public enum VideoPIPSwipeDirection: String, CaseIterable {
|
||||
case up
|
||||
case down
|
||||
case none
|
||||
}
|
||||
|
||||
public enum TranscriptionBackend: String, CaseIterable {
|
||||
case `default`
|
||||
case apple
|
||||
}
|
||||
|
||||
public enum TranslationBackend: String, CaseIterable {
|
||||
case `default`
|
||||
case gtranslate
|
||||
case system
|
||||
// Make sure to update TranslationConfiguration
|
||||
}
|
||||
|
||||
public enum PinnedMessageNotificationsSettings: String, CaseIterable {
|
||||
case `default`
|
||||
case silenced
|
||||
case disabled
|
||||
}
|
||||
|
||||
public enum MentionsAndRepliesNotificationsSettings: String, CaseIterable {
|
||||
case `default`
|
||||
case silenced
|
||||
case disabled
|
||||
}
|
||||
|
||||
public enum NYStyle: String, CaseIterable {
|
||||
case `default`
|
||||
case snow
|
||||
case lightning
|
||||
}
|
||||
|
||||
public static let defaultValues: [String: Any] = [
|
||||
Keys.hidePhoneInSettings.rawValue: true,
|
||||
Keys.showTabNames.rawValue: true,
|
||||
Keys.startTelescopeWithRearCam.rawValue: false,
|
||||
Keys.accountColorsSaturation.rawValue: 100,
|
||||
Keys.uploadSpeedBoost.rawValue: false,
|
||||
Keys.downloadSpeedBoost.rawValue: DownloadSpeedBoostValues.none.rawValue,
|
||||
Keys.rememberLastFolder.rawValue: false,
|
||||
Keys.bottomTabStyle.rawValue: BottomTabStyleValues.telegram.rawValue,
|
||||
Keys.lastAccountFolders.rawValue: [:],
|
||||
Keys.localDNSForProxyHost.rawValue: false,
|
||||
Keys.sendLargePhotos.rawValue: false,
|
||||
Keys.outgoingPhotoQuality.rawValue: 70,
|
||||
Keys.storyStealthMode.rawValue: false,
|
||||
Keys.canUseStealthMode.rawValue: true,
|
||||
Keys.disableSwipeToRecordStory.rawValue: false,
|
||||
Keys.quickTranslateButton.rawValue: false,
|
||||
Keys.outgoingLanguageTranslation.rawValue: [:],
|
||||
Keys.hideReactions.rawValue: false,
|
||||
Keys.showRepostToStory.rawValue: true,
|
||||
Keys.contextShowSelectFromUser.rawValue: true,
|
||||
Keys.contextShowSaveToCloud.rawValue: true,
|
||||
Keys.contextShowRestrict.rawValue: true,
|
||||
// Keys.contextShowBan.rawValue: true,
|
||||
Keys.contextShowHideForwardName.rawValue: true,
|
||||
Keys.contextShowReport.rawValue: true,
|
||||
Keys.contextShowReply.rawValue: true,
|
||||
Keys.contextShowPin.rawValue: true,
|
||||
Keys.contextShowSaveMedia.rawValue: true,
|
||||
Keys.contextShowMessageReplies.rawValue: true,
|
||||
Keys.contextShowJson.rawValue: false,
|
||||
Keys.disableScrollToNextChannel.rawValue: false,
|
||||
Keys.disableScrollToNextTopic.rawValue: false,
|
||||
Keys.disableChatSwipeOptions.rawValue: false,
|
||||
Keys.disableDeleteChatSwipeOption.rawValue: false,
|
||||
Keys.disableGalleryCamera.rawValue: false,
|
||||
Keys.disableGalleryCameraPreview.rawValue: false,
|
||||
Keys.disableSendAsButton.rawValue: false,
|
||||
Keys.disableSnapDeletionEffect.rawValue: false,
|
||||
Keys.stickerSize.rawValue: 100,
|
||||
Keys.stickerTimestamp.rawValue: true,
|
||||
Keys.hideRecordingButton.rawValue: false,
|
||||
Keys.hideTabBar.rawValue: false,
|
||||
Keys.showDC.rawValue: false,
|
||||
Keys.showCreationDate.rawValue: true,
|
||||
Keys.showRegDate.rawValue: true,
|
||||
Keys.regDateCache.rawValue: [:],
|
||||
Keys.compactChatList.rawValue: false,
|
||||
Keys.chatListLines.rawValue: ChatListLines.defaultValue.rawValue,
|
||||
Keys.compactFolderNames.rawValue: false,
|
||||
Keys.allChatsTitleLengthOverride.rawValue: AllChatsTitleLengthOverride.none.rawValue,
|
||||
// Keys.allChatsFolderPositionOverride.rawValue: AllChatsFolderPositionOverride.none.rawValue
|
||||
Keys.allChatsHidden.rawValue: false,
|
||||
Keys.defaultEmojisFirst.rawValue: false,
|
||||
Keys.messageDoubleTapActionOutgoing.rawValue: MessageDoubleTapAction.default.rawValue,
|
||||
Keys.wideChannelPosts.rawValue: false,
|
||||
Keys.forceEmojiTab.rawValue: false,
|
||||
Keys.hideChannelBottomButton.rawValue: false,
|
||||
Keys.secondsInMessages.rawValue: false,
|
||||
Keys.forceSystemSharing.rawValue: false,
|
||||
Keys.confirmCalls.rawValue: true,
|
||||
Keys.videoPIPSwipeDirection.rawValue: VideoPIPSwipeDirection.up.rawValue,
|
||||
Keys.messageFilterKeywords.rawValue: [],
|
||||
Keys.inputToolbar.rawValue: false,
|
||||
Keys.primaryUserId.rawValue: "",
|
||||
Keys.dismissedSGSuggestions.rawValue: [],
|
||||
Keys.duckyAppIconAvailable.rawValue: true,
|
||||
Keys.transcriptionBackend.rawValue: TranscriptionBackend.default.rawValue,
|
||||
Keys.translationBackend.rawValue: TranslationBackend.default.rawValue,
|
||||
Keys.customAppBadge.rawValue: "",
|
||||
Keys.canUseNY.rawValue: false,
|
||||
Keys.nyStyle.rawValue: NYStyle.default.rawValue,
|
||||
Keys.wideTabBar.rawValue: false,
|
||||
Keys.tabBarSearchEnabled.rawValue: true,
|
||||
Keys.hideStories.rawValue: false,
|
||||
Keys.warnOnStoriesOpen.rawValue: false,
|
||||
Keys.showProfileId.rawValue: true,
|
||||
Keys.sendWithReturnKey.rawValue: false
|
||||
]
|
||||
|
||||
public static let groupDefaultValues: [String: Any] = [
|
||||
Keys.legacyNotificationsFix.rawValue: false,
|
||||
Keys.pinnedMessageNotifications.rawValue: PinnedMessageNotificationsSettings.default.rawValue,
|
||||
Keys.mentionsAndRepliesNotifications.rawValue: MentionsAndRepliesNotificationsSettings.default.rawValue,
|
||||
Keys.status.rawValue: 1,
|
||||
Keys.showRepostToStoryV2.rawValue: true,
|
||||
]
|
||||
|
||||
@UserDefault(key: Keys.hidePhoneInSettings.rawValue)
|
||||
public var hidePhoneInSettings: Bool
|
||||
|
||||
@UserDefault(key: Keys.showTabNames.rawValue)
|
||||
public var showTabNames: Bool
|
||||
|
||||
@UserDefault(key: Keys.startTelescopeWithRearCam.rawValue)
|
||||
public var startTelescopeWithRearCam: Bool
|
||||
|
||||
@UserDefault(key: Keys.accountColorsSaturation.rawValue)
|
||||
public var accountColorsSaturation: Int32
|
||||
|
||||
@UserDefault(key: Keys.uploadSpeedBoost.rawValue)
|
||||
public var uploadSpeedBoost: Bool
|
||||
|
||||
@UserDefault(key: Keys.downloadSpeedBoost.rawValue)
|
||||
public var downloadSpeedBoost: String
|
||||
|
||||
@UserDefault(key: Keys.rememberLastFolder.rawValue)
|
||||
public var rememberLastFolder: Bool
|
||||
|
||||
// Disabled while Telegram is migrating to Glass
|
||||
// @UserDefault(key: Keys.bottomTabStyle.rawValue)
|
||||
public var bottomTabStyle: String {
|
||||
set {}
|
||||
get {
|
||||
return BottomTabStyleValues.ios.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
public var lastAccountFolders = UserDefaultsBackedDictionary<String, Int32>(userDefaultsKey: Keys.lastAccountFolders.rawValue, threadSafe: false)
|
||||
|
||||
@UserDefault(key: Keys.localDNSForProxyHost.rawValue)
|
||||
public var localDNSForProxyHost: Bool
|
||||
|
||||
@UserDefault(key: Keys.sendLargePhotos.rawValue)
|
||||
public var sendLargePhotos: Bool
|
||||
|
||||
@UserDefault(key: Keys.outgoingPhotoQuality.rawValue)
|
||||
public var outgoingPhotoQuality: Int32
|
||||
|
||||
@UserDefault(key: Keys.hideStories.rawValue)
|
||||
public var hideStories: Bool
|
||||
|
||||
@UserDefault(key: Keys.warnOnStoriesOpen.rawValue)
|
||||
public var warnOnStoriesOpen: Bool
|
||||
|
||||
@UserDefault(key: Keys.storyStealthMode.rawValue)
|
||||
public var storyStealthMode: Bool
|
||||
|
||||
@UserDefault(key: Keys.canUseStealthMode.rawValue)
|
||||
public var canUseStealthMode: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableSwipeToRecordStory.rawValue)
|
||||
public var disableSwipeToRecordStory: Bool
|
||||
|
||||
@UserDefault(key: Keys.quickTranslateButton.rawValue)
|
||||
public var quickTranslateButton: Bool
|
||||
|
||||
public var outgoingLanguageTranslation = UserDefaultsBackedDictionary<String, String>(userDefaultsKey: Keys.outgoingLanguageTranslation.rawValue, threadSafe: false)
|
||||
|
||||
@UserDefault(key: Keys.hideReactions.rawValue)
|
||||
public var hideReactions: Bool
|
||||
|
||||
// @available(*, deprecated, message: "Use showRepostToStoryV2 instead")
|
||||
@UserDefault(key: Keys.showRepostToStory.rawValue)
|
||||
public var showRepostToStory: Bool
|
||||
|
||||
@UserDefault(key: Keys.showRepostToStoryV2.rawValue, userDefaults: UserDefaults(suiteName: APP_GROUP_IDENTIFIER) ?? .standard)
|
||||
public var showRepostToStoryV2: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowRestrict.rawValue)
|
||||
public var contextShowRestrict: Bool
|
||||
|
||||
/*@UserDefault(key: Keys.contextShowBan.rawValue)
|
||||
public var contextShowBan: Bool*/
|
||||
|
||||
@UserDefault(key: Keys.contextShowSelectFromUser.rawValue)
|
||||
public var contextShowSelectFromUser: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowSaveToCloud.rawValue)
|
||||
public var contextShowSaveToCloud: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowHideForwardName.rawValue)
|
||||
public var contextShowHideForwardName: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowReport.rawValue)
|
||||
public var contextShowReport: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowReply.rawValue)
|
||||
public var contextShowReply: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowPin.rawValue)
|
||||
public var contextShowPin: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowSaveMedia.rawValue)
|
||||
public var contextShowSaveMedia: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowMessageReplies.rawValue)
|
||||
public var contextShowMessageReplies: Bool
|
||||
|
||||
@UserDefault(key: Keys.contextShowJson.rawValue)
|
||||
public var contextShowJson: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableScrollToNextChannel.rawValue)
|
||||
public var disableScrollToNextChannel: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableScrollToNextTopic.rawValue)
|
||||
public var disableScrollToNextTopic: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableChatSwipeOptions.rawValue)
|
||||
public var disableChatSwipeOptions: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableDeleteChatSwipeOption.rawValue)
|
||||
public var disableDeleteChatSwipeOption: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableGalleryCamera.rawValue)
|
||||
public var disableGalleryCamera: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableGalleryCameraPreview.rawValue)
|
||||
public var disableGalleryCameraPreview: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableSendAsButton.rawValue)
|
||||
public var disableSendAsButton: Bool
|
||||
|
||||
@UserDefault(key: Keys.disableSnapDeletionEffect.rawValue)
|
||||
public var disableSnapDeletionEffect: Bool
|
||||
|
||||
@UserDefault(key: Keys.stickerSize.rawValue)
|
||||
public var stickerSize: Int32
|
||||
|
||||
@UserDefault(key: Keys.stickerTimestamp.rawValue)
|
||||
public var stickerTimestamp: Bool
|
||||
|
||||
@UserDefault(key: Keys.hideRecordingButton.rawValue)
|
||||
public var hideRecordingButton: Bool
|
||||
|
||||
@UserDefault(key: Keys.hideTabBar.rawValue)
|
||||
public var hideTabBar: Bool
|
||||
|
||||
@UserDefault(key: Keys.showProfileId.rawValue)
|
||||
public var showProfileId: Bool
|
||||
|
||||
@UserDefault(key: Keys.showDC.rawValue)
|
||||
public var showDC: Bool
|
||||
|
||||
@UserDefault(key: Keys.showCreationDate.rawValue)
|
||||
public var showCreationDate: Bool
|
||||
|
||||
@UserDefault(key: Keys.showRegDate.rawValue)
|
||||
public var showRegDate: Bool
|
||||
|
||||
public var regDateCache = UserDefaultsBackedDictionary<String, Data>(userDefaultsKey: Keys.regDateCache.rawValue, threadSafe: false)
|
||||
|
||||
@UserDefault(key: Keys.compactChatList.rawValue)
|
||||
public var compactChatList: Bool
|
||||
|
||||
@UserDefault(key: Keys.chatListLines.rawValue)
|
||||
public var chatListLines: String
|
||||
|
||||
@UserDefault(key: Keys.compactFolderNames.rawValue)
|
||||
public var compactFolderNames: Bool
|
||||
|
||||
@UserDefault(key: Keys.allChatsTitleLengthOverride.rawValue)
|
||||
public var allChatsTitleLengthOverride: String
|
||||
//
|
||||
// @UserDefault(key: Keys.allChatsFolderPositionOverride.rawValue)
|
||||
// public var allChatsFolderPositionOverride: String
|
||||
@UserDefault(key: Keys.allChatsHidden.rawValue)
|
||||
public var allChatsHidden: Bool
|
||||
|
||||
@UserDefault(key: Keys.defaultEmojisFirst.rawValue)
|
||||
public var defaultEmojisFirst: Bool
|
||||
|
||||
@UserDefault(key: Keys.messageDoubleTapActionOutgoing.rawValue)
|
||||
public var messageDoubleTapActionOutgoing: String
|
||||
|
||||
@UserDefault(key: Keys.wideChannelPosts.rawValue)
|
||||
public var wideChannelPosts: Bool
|
||||
|
||||
@UserDefault(key: Keys.forceEmojiTab.rawValue)
|
||||
public var forceEmojiTab: Bool
|
||||
|
||||
@UserDefault(key: Keys.forceBuiltInMic.rawValue)
|
||||
public var forceBuiltInMic: Bool
|
||||
|
||||
@UserDefault(key: Keys.secondsInMessages.rawValue)
|
||||
public var secondsInMessages: Bool
|
||||
|
||||
@UserDefault(key: Keys.hideChannelBottomButton.rawValue)
|
||||
public var hideChannelBottomButton: Bool
|
||||
|
||||
@UserDefault(key: Keys.forceSystemSharing.rawValue)
|
||||
public var forceSystemSharing: Bool
|
||||
|
||||
@UserDefault(key: Keys.confirmCalls.rawValue)
|
||||
public var confirmCalls: Bool
|
||||
|
||||
@UserDefault(key: Keys.videoPIPSwipeDirection.rawValue)
|
||||
public var videoPIPSwipeDirection: String
|
||||
|
||||
@UserDefault(key: Keys.legacyNotificationsFix.rawValue, userDefaults: UserDefaults(suiteName: APP_GROUP_IDENTIFIER) ?? .standard)
|
||||
public var legacyNotificationsFix: Bool
|
||||
|
||||
@UserDefault(key: Keys.status.rawValue, userDefaults: UserDefaults(suiteName: APP_GROUP_IDENTIFIER) ?? .standard)
|
||||
public var status: Int64
|
||||
|
||||
public var ephemeralStatus: Int64 = 1
|
||||
|
||||
@UserDefault(key: Keys.messageFilterKeywords.rawValue)
|
||||
public var messageFilterKeywords: [String]
|
||||
|
||||
@UserDefault(key: Keys.inputToolbar.rawValue)
|
||||
public var inputToolbar: Bool
|
||||
|
||||
@UserDefault(key: Keys.sendWithReturnKey.rawValue)
|
||||
public var sendWithReturnKey: Bool
|
||||
|
||||
@UserDefault(key: Keys.pinnedMessageNotifications.rawValue, userDefaults: UserDefaults(suiteName: APP_GROUP_IDENTIFIER) ?? .standard)
|
||||
public var pinnedMessageNotifications: String
|
||||
|
||||
@UserDefault(key: Keys.mentionsAndRepliesNotifications.rawValue, userDefaults: UserDefaults(suiteName: APP_GROUP_IDENTIFIER) ?? .standard)
|
||||
public var mentionsAndRepliesNotifications: String
|
||||
|
||||
@UserDefault(key: Keys.primaryUserId.rawValue)
|
||||
public var primaryUserId: String
|
||||
|
||||
@UserDefault(key: Keys.dismissedSGSuggestions.rawValue)
|
||||
public var dismissedSGSuggestions: [String]
|
||||
|
||||
@UserDefault(key: Keys.duckyAppIconAvailable.rawValue)
|
||||
public var duckyAppIconAvailable: Bool
|
||||
|
||||
@UserDefault(key: Keys.transcriptionBackend.rawValue)
|
||||
public var transcriptionBackend: String
|
||||
|
||||
@UserDefault(key: Keys.translationBackend.rawValue)
|
||||
public var translationBackend: String
|
||||
|
||||
@UserDefault(key: Keys.customAppBadge.rawValue)
|
||||
public var customAppBadge: String
|
||||
|
||||
@UserDefault(key: Keys.canUseNY.rawValue)
|
||||
public var canUseNY: Bool
|
||||
|
||||
@UserDefault(key: Keys.nyStyle.rawValue)
|
||||
public var nyStyle: String
|
||||
|
||||
@UserDefault(key: Keys.wideTabBar.rawValue)
|
||||
public var wideTabBar: Bool
|
||||
|
||||
@UserDefault(key: Keys.tabBarSearchEnabled.rawValue)
|
||||
public var tabBarSearchEnabled: Bool
|
||||
}
|
||||
|
||||
extension SGSimpleSettings {
|
||||
public var isStealthModeEnabled: Bool {
|
||||
return storyStealthMode && canUseStealthMode
|
||||
}
|
||||
|
||||
public static func makeOutgoingLanguageTranslationKey(accountId: Int64, peerId: Int64) -> String {
|
||||
return "\(accountId):\(peerId)"
|
||||
}
|
||||
}
|
||||
|
||||
extension SGSimpleSettings {
|
||||
public var translationBackendEnum: SGSimpleSettings.TranslationBackend {
|
||||
return TranslationBackend(rawValue: translationBackend) ?? .default
|
||||
}
|
||||
|
||||
public var transcriptionBackendEnum: SGSimpleSettings.TranscriptionBackend {
|
||||
return TranscriptionBackend(rawValue: transcriptionBackend) ?? .default
|
||||
}
|
||||
}
|
||||
|
||||
extension SGSimpleSettings {
|
||||
public var isNYEnabled: Bool {
|
||||
return canUseNY && NYStyle(rawValue: nyStyle) != .default
|
||||
}
|
||||
}
|
||||
|
||||
public func getSGDownloadPartSize(_ default: Int64, fileSize: Int64?) -> Int64 {
|
||||
let currentDownloadSetting = SGSimpleSettings.shared.downloadSpeedBoost
|
||||
// Increasing chunk size for small files make it worse in terms of overall download performance
|
||||
let smallFileSizeThreshold = 1 * 1024 * 1024 // 1 MB
|
||||
switch (currentDownloadSetting) {
|
||||
case SGSimpleSettings.DownloadSpeedBoostValues.medium.rawValue:
|
||||
if let fileSize, fileSize <= smallFileSizeThreshold {
|
||||
return `default`
|
||||
}
|
||||
return 512 * 1024
|
||||
case SGSimpleSettings.DownloadSpeedBoostValues.maximum.rawValue:
|
||||
if let fileSize, fileSize <= smallFileSizeThreshold {
|
||||
return `default`
|
||||
}
|
||||
return 1024 * 1024
|
||||
default:
|
||||
return `default`
|
||||
}
|
||||
}
|
||||
|
||||
public func getSGMaxPendingParts(_ default: Int) -> Int {
|
||||
let currentDownloadSetting = SGSimpleSettings.shared.downloadSpeedBoost
|
||||
switch (currentDownloadSetting) {
|
||||
case SGSimpleSettings.DownloadSpeedBoostValues.medium.rawValue:
|
||||
return 8
|
||||
case SGSimpleSettings.DownloadSpeedBoostValues.maximum.rawValue:
|
||||
return 12
|
||||
default:
|
||||
return `default`
|
||||
}
|
||||
}
|
||||
|
||||
public func sgUseShortAllChatsTitle(_ default: Bool) -> Bool {
|
||||
let currentOverride = SGSimpleSettings.shared.allChatsTitleLengthOverride
|
||||
switch (currentOverride) {
|
||||
case SGSimpleSettings.AllChatsTitleLengthOverride.short.rawValue:
|
||||
return true
|
||||
case SGSimpleSettings.AllChatsTitleLengthOverride.long.rawValue:
|
||||
return false
|
||||
default:
|
||||
return `default`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
import Foundation
|
||||
|
||||
public protocol AllowedUserDefaultTypes {}
|
||||
|
||||
/* // This one is more painful than helpful
|
||||
extension Bool: AllowedUserDefaultTypes {}
|
||||
extension String: AllowedUserDefaultTypes {}
|
||||
extension Int: AllowedUserDefaultTypes {}
|
||||
extension Int32: AllowedUserDefaultTypes {}
|
||||
extension Double: AllowedUserDefaultTypes {}
|
||||
extension Float: AllowedUserDefaultTypes {}
|
||||
extension Data: AllowedUserDefaultTypes {}
|
||||
extension URL: AllowedUserDefaultTypes {}
|
||||
//extension Dictionary<String, Any>: AllowedUserDefaultTypes {}
|
||||
extension Array: AllowedUserDefaultTypes where Element: AllowedUserDefaultTypes {}
|
||||
*/
|
||||
|
||||
// Does not support Optional types due to caching
|
||||
@propertyWrapper
|
||||
public class UserDefault<T> /*where T: AllowedUserDefaultTypes*/ {
|
||||
public let key: String
|
||||
public let userDefaults: UserDefaults
|
||||
private var cachedValue: T?
|
||||
|
||||
public init(key: String, userDefaults: UserDefaults = .standard) {
|
||||
self.key = key
|
||||
self.userDefaults = userDefaults
|
||||
}
|
||||
|
||||
public var wrappedValue: T {
|
||||
get {
|
||||
#if DEBUG && false
|
||||
SGtrace("UD.\(key)", what: "GET")
|
||||
#endif
|
||||
|
||||
if let strongCachedValue = cachedValue {
|
||||
#if DEBUG && false
|
||||
SGtrace("UD", what: "CACHED \(key) \(strongCachedValue)")
|
||||
#endif
|
||||
return strongCachedValue
|
||||
}
|
||||
|
||||
cachedValue = readFromUserDefaults()
|
||||
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(key)", what: "EXTRACTED: \(cachedValue!)")
|
||||
#endif
|
||||
return cachedValue!
|
||||
}
|
||||
set {
|
||||
cachedValue = newValue
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(key)", what: "CACHE UPDATED \(cachedValue!)")
|
||||
#endif
|
||||
userDefaults.set(newValue, forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func readFromUserDefaults() -> T {
|
||||
switch T.self {
|
||||
case is Bool.Type:
|
||||
return (userDefaults.bool(forKey: key) as! T)
|
||||
case is String.Type:
|
||||
return (userDefaults.string(forKey: key) as! T)
|
||||
case is Int64.Type:
|
||||
return (Int64(exactly: userDefaults.integer(forKey: key)) as! T)
|
||||
case is Int32.Type:
|
||||
return (Int32(exactly: userDefaults.integer(forKey: key)) as! T)
|
||||
case is Int.Type:
|
||||
return (userDefaults.integer(forKey: key) as! T)
|
||||
case is Double.Type:
|
||||
return (userDefaults.double(forKey: key) as! T)
|
||||
case is Float.Type:
|
||||
return (userDefaults.float(forKey: key) as! T)
|
||||
case is Data.Type:
|
||||
return (userDefaults.data(forKey: key) as! T)
|
||||
case is URL.Type:
|
||||
return (userDefaults.url(forKey: key) as! T)
|
||||
case is Array<String>.Type:
|
||||
return (userDefaults.stringArray(forKey: key) as! T)
|
||||
case is Array<Any>.Type:
|
||||
return (userDefaults.array(forKey: key) as! T)
|
||||
default:
|
||||
fatalError("Unsupported UserDefault type \(T.self)")
|
||||
// cachedValue = (userDefaults.object(forKey: key) as! T)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//public class AtomicUserDefault<T>: UserDefault<T> {
|
||||
// private let atomicCachedValue: AtomicWrapper<T?> = AtomicWrapper(value: nil)
|
||||
//
|
||||
// public override var wrappedValue: T {
|
||||
// get {
|
||||
// return atomicCachedValue.modify({ value in
|
||||
// if let strongValue = value {
|
||||
// return strongValue
|
||||
// }
|
||||
// return self.readFromUserDefaults()
|
||||
// })!
|
||||
// }
|
||||
// set {
|
||||
// let _ = atomicCachedValue.modify({ _ in
|
||||
// userDefaults.set(newValue, forKey: key)
|
||||
// return newValue
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// Based on ConcurrentDictionary.swift from https://github.com/peterprokop/SwiftConcurrentCollections
|
||||
|
||||
/// Thread-safe UserDefaults dictionary wrapper
|
||||
/// - Important: Note that this is a `class`, i.e. reference (not value) type
|
||||
/// - Important: Key can only be String type
|
||||
public class UserDefaultsBackedDictionary<Key: Hashable, Value> {
|
||||
public let userDefaultsKey: String
|
||||
public let userDefaults: UserDefaults
|
||||
|
||||
private var container: [Key: Value]? = nil
|
||||
private let rwlock = RWLock()
|
||||
private let threadSafe: Bool
|
||||
|
||||
public var keys: [Key] {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "KEYS")
|
||||
#endif
|
||||
let result: [Key]
|
||||
if threadSafe {
|
||||
rwlock.readLock()
|
||||
}
|
||||
if container == nil {
|
||||
container = userDefaultsContainer
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "EXTRACTED: \(container!)")
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "FROM CACHE: \(container!)")
|
||||
#endif
|
||||
}
|
||||
result = Array(container!.keys)
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public var values: [Value] {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "VALUES")
|
||||
#endif
|
||||
let result: [Value]
|
||||
if threadSafe {
|
||||
rwlock.readLock()
|
||||
}
|
||||
if container == nil {
|
||||
container = userDefaultsContainer
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "EXTRACTED: \(container!)")
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "FROM CACHE: \(container!)")
|
||||
#endif
|
||||
}
|
||||
result = Array(container!.values)
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public init(userDefaultsKey: String, userDefaults: UserDefaults = .standard, threadSafe: Bool) {
|
||||
self.userDefaultsKey = userDefaultsKey
|
||||
self.userDefaults = userDefaults
|
||||
self.threadSafe = threadSafe
|
||||
}
|
||||
|
||||
/// Sets the value for key
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: The value to set for key
|
||||
/// - key: The key to set value for
|
||||
public func set(value: Value, forKey key: Key) {
|
||||
if threadSafe {
|
||||
rwlock.writeLock()
|
||||
}
|
||||
_set(value: value, forKey: key)
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func remove(_ key: Key) -> Value? {
|
||||
let result: Value?
|
||||
if threadSafe {
|
||||
rwlock.writeLock()
|
||||
}
|
||||
result = _remove(key)
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func removeValue(forKey: Key) -> Value? {
|
||||
return self.remove(forKey)
|
||||
}
|
||||
|
||||
public func contains(_ key: Key) -> Bool {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "CONTAINS")
|
||||
#endif
|
||||
let result: Bool
|
||||
if threadSafe {
|
||||
rwlock.readLock()
|
||||
}
|
||||
if container == nil {
|
||||
container = userDefaultsContainer
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "EXTRACTED: \(container!)")
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "FROM CACHE: \(container!)")
|
||||
#endif
|
||||
}
|
||||
result = container!.index(forKey: key) != nil
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public func value(forKey key: Key) -> Value? {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "VALUE")
|
||||
#endif
|
||||
let result: Value?
|
||||
if threadSafe {
|
||||
rwlock.readLock()
|
||||
}
|
||||
if container == nil {
|
||||
container = userDefaultsContainer
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "EXTRACTED: \(container!)")
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "FROM CACHE: \(container!)")
|
||||
#endif
|
||||
}
|
||||
result = container![key]
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public func mutateValue(forKey key: Key, mutation: (Value) -> Value) {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "MUTATE")
|
||||
#endif
|
||||
if threadSafe {
|
||||
rwlock.writeLock()
|
||||
}
|
||||
if container == nil {
|
||||
container = userDefaultsContainer
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "EXTRACTED: \(container!)")
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "FROM CACHE: \(container!)")
|
||||
#endif
|
||||
}
|
||||
if let value = container![key] {
|
||||
container![key] = mutation(value)
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "UPDATING CACHE \(key): \(value), \(container!)")
|
||||
#endif
|
||||
userDefaultsContainer = container!
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "CACHE UPDATED \(key): \(value), \(container!)")
|
||||
#endif
|
||||
}
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
public var isEmpty: Bool {
|
||||
return self.keys.isEmpty
|
||||
}
|
||||
|
||||
// MARK: Subscript
|
||||
public subscript(key: Key) -> Value? {
|
||||
get {
|
||||
return value(forKey: key)
|
||||
}
|
||||
set {
|
||||
if threadSafe {
|
||||
rwlock.writeLock()
|
||||
}
|
||||
defer {
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
}
|
||||
guard let newValue = newValue else {
|
||||
_remove(key)
|
||||
return
|
||||
}
|
||||
_set(value: newValue, forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
@inline(__always)
|
||||
private func _set(value: Value, forKey key: Key) {
|
||||
if container == nil {
|
||||
container = userDefaultsContainer
|
||||
}
|
||||
self.container![key] = value
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "UPDATING CACHE \(key): \(value), \(container!)")
|
||||
#endif
|
||||
userDefaultsContainer = container!
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "CACHE UPDATED \(key): \(value), \(container!)")
|
||||
#endif
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
@discardableResult
|
||||
private func _remove(_ key: Key) -> Value? {
|
||||
if container == nil {
|
||||
container = userDefaultsContainer
|
||||
}
|
||||
guard let index = container!.index(forKey: key) else { return nil }
|
||||
|
||||
let tuple = container!.remove(at: index)
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "UPDATING CACHE REMOVE \(key) \(container!)")
|
||||
#endif
|
||||
userDefaultsContainer = container!
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "CACHE UPDATED REMOVED \(key) \(container!)")
|
||||
#endif
|
||||
return tuple.value
|
||||
}
|
||||
|
||||
private var userDefaultsContainer: [Key: Value] {
|
||||
get {
|
||||
return userDefaults.dictionary(forKey: userDefaultsKey) as! [Key: Value]
|
||||
}
|
||||
set {
|
||||
userDefaults.set(newValue, forKey: userDefaultsKey)
|
||||
}
|
||||
}
|
||||
|
||||
public func drop() {
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "DROPPING")
|
||||
#endif
|
||||
if threadSafe {
|
||||
rwlock.writeLock()
|
||||
}
|
||||
userDefaults.removeObject(forKey: userDefaultsKey)
|
||||
container = userDefaultsContainer
|
||||
#if DEBUG
|
||||
SGtrace("UD.\(userDefaultsKey)\(threadSafe ? "-ts" : "")", what: "DROPPED: \(container!)")
|
||||
#endif
|
||||
if threadSafe {
|
||||
rwlock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
private let queue = DispatchQueue(label: "app.swiftgram.ios.trace", qos: .utility)
|
||||
|
||||
public func SGtrace(_ domain: String, what: @autoclosure() -> String) {
|
||||
let string = what()
|
||||
var rawTime = time_t()
|
||||
time(&rawTime)
|
||||
var timeinfo = tm()
|
||||
localtime_r(&rawTime, &timeinfo)
|
||||
|
||||
var curTime = timeval()
|
||||
gettimeofday(&curTime, nil)
|
||||
let seconds = Int(curTime.tv_sec % 60) // Extracting the current second
|
||||
let microseconds = curTime.tv_usec // Full microsecond precision
|
||||
|
||||
queue.async {
|
||||
let result = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%06d %@", arguments: [domain, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), seconds, microseconds, string])
|
||||
|
||||
print(result)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user