Files
Leeksov 4647310322 GLEGram 12.5 — Initial public release
Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
2026-04-06 09:48:12 +03:00

568 lines
22 KiB
Swift

/**
* Wrap - the easy to use Swift JSON encoder
*
* For usage, see documentation of the classes/symbols listed in this file, as well
* as the guide available at: github.com/johnsundell/wrap
*
* Copyright (c) 2015 - 2017 John Sundell. Licensed under the MIT license, as follows:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import Foundation
/// Type alias defining what type of Dictionary that Wrap produces
public typealias WrappedDictionary = [String : Any]
/**
* Wrap any object or value, encoding it into a JSON compatible Dictionary
*
* - Parameter object: The object to encode
* - Parameter context: An optional contextual object that will be available throughout
* the wrapping process. Can be used to inject extra information or objects needed to
* perform the wrapping.
* - Parameter dateFormatter: Optionally pass in a date formatter to use to encode any
* `NSDate` values found while encoding the object. If this is `nil`, any found date
* values will be encoded using the "yyyy-MM-dd HH:mm:ss" format.
*
* All the type's stored properties (both public & private) will be recursively
* encoded with their property names as the key. For example, given the following
* Struct as input:
*
* ```
* struct User {
* let name = "John"
* let age = 28
* }
* ```
*
* This function will produce the following output:
*
* ```
* [
* "name" : "John",
* "age" : 28
* ]
* ```
*
* The object passed to this function must be an instance of a Class, or a value
* based on a Struct. Standard library values, such as Ints, Strings, etc are not
* valid input.
*
* Throws a WrapError if the operation could not be completed.
*
* For more customization options, make your type conform to `WrapCustomizable`,
* that lets you override encoding keys and/or the whole wrapping process.
*
* See also `WrappableKey` (for dictionary keys) and `WrappableEnum` for Enum values.
*/
public func wrap<T>(_ object: T, context: Any? = nil, dateFormatter: DateFormatter? = nil) throws -> WrappedDictionary {
return try Wrapper(context: context, dateFormatter: dateFormatter).wrap(object: object, enableCustomizedWrapping: true)
}
/**
* Alternative `wrap()` overload that returns JSON-based `Data`
*
* See the documentation for the dictionary-based `wrap()` function for more information
*/
public func wrap<T>(_ object: T, writingOptions: JSONSerialization.WritingOptions? = nil, context: Any? = nil, dateFormatter: DateFormatter? = nil) throws -> Data {
return try Wrapper(context: context, dateFormatter: dateFormatter).wrap(object: object, writingOptions: writingOptions ?? [])
}
/**
* Alternative `wrap()` overload that encodes an array of objects into an array of dictionaries
*
* See the documentation for the dictionary-based `wrap()` function for more information
*/
public func wrap<T>(_ objects: [T], context: Any? = nil, dateFormatter: DateFormatter? = nil) throws -> [WrappedDictionary] {
return try objects.map { try wrap($0, context: context, dateFormatter: dateFormatter) }
}
/**
* Alternative `wrap()` overload that encodes an array of objects into JSON-based `Data`
*
* See the documentation for the dictionary-based `wrap()` function for more information
*/
public func wrap<T>(_ objects: [T], writingOptions: JSONSerialization.WritingOptions? = nil, context: Any? = nil, dateFormatter: DateFormatter? = nil) throws -> Data {
let dictionaries: [WrappedDictionary] = try wrap(objects, context: context, dateFormatter: dateFormatter)
return try JSONSerialization.data(withJSONObject: dictionaries, options: writingOptions ?? [])
}
// Enum describing various styles of keys in a wrapped dictionary
public enum WrapKeyStyle {
/// The keys in a dictionary produced by Wrap should match their property name (default)
case matchPropertyName
/// The keys in a dictionary produced by Wrap should be converted to snake_case.
/// For example, "myProperty" will be converted to "my_property". All keys will be lowercased.
case convertToSnakeCase
}
/**
* Protocol providing the main customization point for Wrap
*
* It's optional to implement all of the methods in this protocol, as Wrap
* supplies default implementations of them.
*/
public protocol WrapCustomizable {
/**
* The style that wrap should apply to the keys of a wrapped dictionary
*
* The value of this property is ignored if a type provides a custom
* implementation of the `keyForWrapping(propertyNamed:)` method.
*/
var wrapKeyStyle: WrapKeyStyle { get }
/**
* Override the wrapping process for this type
*
* All top-level types should return a `WrappedDictionary` from this method.
*
* You may use the default wrapping implementation by using a `Wrapper`, but
* never call `wrap()` from an implementation of this method, since that might
* cause an infinite recursion.
*
* The context & dateFormatter passed to this method is any formatter that you
* supplied when initiating the wrapping process by calling `wrap()`.
*
* Returning nil from this method will be treated as an error, and cause
* a `WrapError.wrappingFailedForObject()` error to be thrown.
*/
func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any?
/**
* Override the key that will be used when encoding a certain property
*
* Returning nil from this method will cause Wrap to skip the property
*/
func keyForWrapping(propertyNamed propertyName: String) -> String?
/**
* Override the wrapping of any property of this type
*
* The original value passed to this method will be the original value that the
* type is currently storing for the property. You can choose to either use this,
* or just access the property in question directly.
*
* The dateFormatter passed to this method is any formatter that you supplied
* when initiating the wrapping process by calling `wrap()`.
*
* Returning nil from this method will cause Wrap to use the default
* wrapping mechanism for the property, so you can choose which properties
* you want to customize the wrapping for.
*
* If you encounter an error while attempting to wrap the property in question,
* you can choose to throw. This will cause a WrapError.WrappingFailedForObject
* to be thrown from the main `wrap()` call that started the process.
*/
func wrap(propertyNamed propertyName: String, originalValue: Any, context: Any?, dateFormatter: DateFormatter?) throws -> Any?
}
/// Protocol implemented by types that may be used as keys in a wrapped Dictionary
public protocol WrappableKey {
/// Convert this type into a key that can be used in a wrapped Dictionary
func toWrappedKey() -> String
}
/**
* Protocol implemented by Enums to enable them to be directly wrapped
*
* If an Enum implementing this protocol conforms to `RawRepresentable` (it's based
* on a raw type), no further implementation is required. If you wish to customize
* how the Enum is wrapped, you can use the APIs in `WrapCustomizable`.
*/
public protocol WrappableEnum: WrapCustomizable {}
/// Protocol implemented by Date types to enable them to be wrapped
public protocol WrappableDate {
/// Wrap the date using a date formatter, generating a string representation
func wrap(dateFormatter: DateFormatter) -> String
}
/**
* Class used to wrap an object or value. Use this in any custom `wrap()` implementations
* in case you only want to add on top of the default implementation.
*
* You normally don't have to interact with this API. Use the `wrap()` function instead
* to wrap an object from top-level code.
*/
public class Wrapper {
fileprivate let context: Any?
fileprivate var dateFormatter: DateFormatter?
/**
* Initialize an instance of this class
*
* - Parameter context: An optional contextual object that will be available throughout the
* wrapping process. Can be used to inject extra information or objects needed to perform
* the wrapping.
* - Parameter dateFormatter: Any specific date formatter to use to encode any found `NSDate`
* values. If this is `nil`, any found date values will be encoded using the "yyyy-MM-dd
* HH:mm:ss" format.
*/
public init(context: Any? = nil, dateFormatter: DateFormatter? = nil) {
self.context = context
self.dateFormatter = dateFormatter
}
/// Perform automatic wrapping of an object or value. For more information, see `Wrap()`.
public func wrap(object: Any) throws -> WrappedDictionary {
return try self.wrap(object: object, enableCustomizedWrapping: false)
}
}
/// Error type used by Wrap
public enum WrapError: Error {
/// Thrown when an invalid top level object (such as a String or Int) was passed to `Wrap()`
case invalidTopLevelObject(Any)
/// Thrown when an object couldn't be wrapped. This is a last resort error.
case wrappingFailedForObject(Any)
}
// MARK: - Default protocol implementations
/// Extension containing default implementations of `WrapCustomizable`. Override as you see fit.
public extension WrapCustomizable {
var wrapKeyStyle: WrapKeyStyle {
return .matchPropertyName
}
func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return try? Wrapper(context: context, dateFormatter: dateFormatter).wrap(object: self)
}
func keyForWrapping(propertyNamed propertyName: String) -> String? {
switch self.wrapKeyStyle {
case .matchPropertyName:
return propertyName
case .convertToSnakeCase:
return self.convertPropertyNameToSnakeCase(propertyName: propertyName)
}
}
func wrap(propertyNamed propertyName: String, originalValue: Any, context: Any?, dateFormatter: DateFormatter?) throws -> Any? {
return try Wrapper(context: context, dateFormatter: dateFormatter).wrap(value: originalValue, propertyName: propertyName)
}
}
/// Extension adding convenience APIs to `WrapCustomizable` types
public extension WrapCustomizable {
/// Convert a given property name (assumed to be camelCased) to snake_case
func convertPropertyNameToSnakeCase(propertyName: String) -> String {
let regex = try! NSRegularExpression(pattern: "(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])", options: [])
let range = NSRange(location: 0, length: propertyName.count)
let camelCasePropertyName = regex.stringByReplacingMatches(in: propertyName, options: [], range: range, withTemplate: "_$1$2")
return camelCasePropertyName.lowercased()
}
}
/// Extension providing a default wrapping implementation for `RawRepresentable` Enums
public extension WrappableEnum where Self: RawRepresentable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return self.rawValue
}
}
/// Extension customizing how Arrays are wrapped
extension Array: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return try? Wrapper(context: context, dateFormatter: dateFormatter).wrap(collection: self)
}
}
/// Extension customizing how Dictionaries are wrapped
extension Dictionary: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return try? Wrapper(context: context, dateFormatter: dateFormatter).wrap(dictionary: self)
}
}
/// Extension customizing how Sets are wrapped
extension Set: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return try? Wrapper(context: context, dateFormatter: dateFormatter).wrap(collection: self)
}
}
/// Extension customizing how Int64s are wrapped, ensuring compatbility with 32 bit systems
extension Int64: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return NSNumber(value: self)
}
}
/// Extension customizing how UInt64s are wrapped, ensuring compatbility with 32 bit systems
extension UInt64: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return NSNumber(value: self)
}
}
/// Extension customizing how NSStrings are wrapped
extension NSString: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return self
}
}
/// Extension customizing how NSURLs are wrapped
extension NSURL: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return self.absoluteString
}
}
/// Extension customizing how URLs are wrapped
extension URL: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return self.absoluteString
}
}
/// Extension customizing how NSArrays are wrapped
extension NSArray: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return try? Wrapper(context: context, dateFormatter: dateFormatter).wrap(collection: Array(self))
}
}
#if !os(Linux)
/// Extension customizing how NSDictionaries are wrapped
extension NSDictionary: WrapCustomizable {
public func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return try? Wrapper(context: context, dateFormatter: dateFormatter).wrap(dictionary: self as [NSObject : AnyObject])
}
}
#endif
/// Extension making Int a WrappableKey
extension Int: WrappableKey {
public func toWrappedKey() -> String {
return String(self)
}
}
/// Extension making Date a WrappableDate
extension Date: WrappableDate {
public func wrap(dateFormatter: DateFormatter) -> String {
return dateFormatter.string(from: self)
}
}
#if !os(Linux)
/// Extension making NSdate a WrappableDate
extension NSDate: WrappableDate {
public func wrap(dateFormatter: DateFormatter) -> String {
return dateFormatter.string(from: self as Date)
}
}
#endif
// MARK: - Private
private extension Wrapper {
func wrap<T>(object: T, enableCustomizedWrapping: Bool) throws -> WrappedDictionary {
if enableCustomizedWrapping {
if let customizable = object as? WrapCustomizable {
let wrapped = try self.performCustomWrapping(object: customizable)
guard let wrappedDictionary = wrapped as? WrappedDictionary else {
throw WrapError.invalidTopLevelObject(object)
}
return wrappedDictionary
}
}
var mirrors = [Mirror]()
var currentMirror: Mirror? = Mirror(reflecting: object)
while let mirror = currentMirror {
mirrors.append(mirror)
currentMirror = mirror.superclassMirror
}
return try self.performWrapping(object: object, mirrors: mirrors.reversed())
}
func wrap<T>(object: T, writingOptions: JSONSerialization.WritingOptions) throws -> Data {
let dictionary = try self.wrap(object: object, enableCustomizedWrapping: true)
return try JSONSerialization.data(withJSONObject: dictionary, options: writingOptions)
}
func wrap<T>(value: T, propertyName: String? = nil) throws -> Any? {
if let customizable = value as? WrapCustomizable {
return try self.performCustomWrapping(object: customizable)
}
if let date = value as? WrappableDate {
return self.wrap(date: date)
}
let mirror = Mirror(reflecting: value)
if mirror.children.isEmpty {
if let displayStyle = mirror.displayStyle {
switch displayStyle {
case .enum:
if let wrappableEnum = value as? WrappableEnum {
if let wrapped = wrappableEnum.wrap(context: self.context, dateFormatter: self.dateFormatter) {
return wrapped
}
throw WrapError.wrappingFailedForObject(value)
}
return "\(value)"
case .struct:
return [:]
default:
return value
}
}
if !(value is CustomStringConvertible) {
if String(describing: value) == "(Function)" {
return nil
}
}
return value
} else if value is ExpressibleByNilLiteral && mirror.children.count == 1 {
if let firstMirrorChild = mirror.children.first {
return try self.wrap(value: firstMirrorChild.value, propertyName: propertyName)
}
}
return try self.wrap(object: value, enableCustomizedWrapping: false)
}
func wrap<T: Collection>(collection: T) throws -> [Any] {
var wrappedArray = [Any]()
let wrapper = Wrapper(context: self.context, dateFormatter: self.dateFormatter)
for element in collection {
if let wrapped = try wrapper.wrap(value: element) {
wrappedArray.append(wrapped)
}
}
return wrappedArray
}
func wrap<K, V>(dictionary: [K : V]) throws -> WrappedDictionary {
var wrappedDictionary = WrappedDictionary()
let wrapper = Wrapper(context: self.context, dateFormatter: self.dateFormatter)
for (key, value) in dictionary {
let wrappedKey: String?
if let stringKey = key as? String {
wrappedKey = stringKey
} else if let wrappableKey = key as? WrappableKey {
wrappedKey = wrappableKey.toWrappedKey()
} else if let stringConvertible = key as? CustomStringConvertible {
wrappedKey = stringConvertible.description
} else {
wrappedKey = nil
}
if let wrappedKey = wrappedKey {
wrappedDictionary[wrappedKey] = try wrapper.wrap(value: value, propertyName: wrappedKey)
}
}
return wrappedDictionary
}
func wrap(date: WrappableDate) -> String {
let dateFormatter: DateFormatter
if let existingFormatter = self.dateFormatter {
dateFormatter = existingFormatter
} else {
dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
self.dateFormatter = dateFormatter
}
return date.wrap(dateFormatter: dateFormatter)
}
func performWrapping<T>(object: T, mirrors: [Mirror]) throws -> WrappedDictionary {
let customizable = object as? WrapCustomizable
var wrappedDictionary = WrappedDictionary()
for mirror in mirrors {
for property in mirror.children {
if (property.value as? WrapOptional)?.isNil == true {
continue
}
guard let propertyName = property.label else {
continue
}
let wrappingKey: String?
if let customizable = customizable {
wrappingKey = customizable.keyForWrapping(propertyNamed: propertyName)
} else {
wrappingKey = propertyName
}
if let wrappingKey = wrappingKey {
if let wrappedProperty = try customizable?.wrap(propertyNamed: propertyName, originalValue: property.value, context: self.context, dateFormatter: self.dateFormatter) {
wrappedDictionary[wrappingKey] = wrappedProperty
} else {
wrappedDictionary[wrappingKey] = try self.wrap(value: property.value, propertyName: propertyName)
}
}
}
}
return wrappedDictionary
}
func performCustomWrapping(object: WrapCustomizable) throws -> Any {
guard let wrapped = object.wrap(context: self.context, dateFormatter: self.dateFormatter) else {
throw WrapError.wrappingFailedForObject(object)
}
return wrapped
}
}
// MARK: - Nil Handling
private protocol WrapOptional {
var isNil: Bool { get }
}
extension Optional : WrapOptional {
var isNil: Bool {
switch self {
case .none:
return true
case .some(let wrapped):
if let nillable = wrapped as? WrapOptional {
return nillable.isNil
}
return false
}
}
}