-
Notifications
You must be signed in to change notification settings - Fork 35
feat: Apple SDK update for version 13.4.0 #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 |
|---|---|---|
|
|
@@ -31,7 +31,7 @@ Add the package to your `Package.swift` dependencies: | |
|
|
||
| ```swift | ||
| dependencies: [ | ||
| .package(url: "[email protected]:appwrite/sdk-for-apple.git", from: "13.3.1"), | ||
| .package(url: "[email protected]:appwrite/sdk-for-apple.git", from: "13.4.0"), | ||
| ], | ||
| ``` | ||
|
|
||
|
|
||
This file contains hidden or 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 hidden or 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,305 @@ | ||
| import Foundation | ||
|
|
||
| public enum Condition: String, Codable { | ||
| case equal = "equal" | ||
| case notEqual = "notEqual" | ||
| case greaterThan = "greaterThan" | ||
| case greaterThanEqual = "greaterThanEqual" | ||
| case lessThan = "lessThan" | ||
| case lessThanEqual = "lessThanEqual" | ||
| case contains = "contains" | ||
| case isNull = "isNull" | ||
| case isNotNull = "isNotNull" | ||
| } | ||
|
|
||
| enum OperatorValue: Codable { | ||
| case string(String) | ||
| case int(Int) | ||
| case double(Double) | ||
| case bool(Bool) | ||
| case array([OperatorValue]) | ||
| case null | ||
|
|
||
| init(from decoder: Decoder) throws { | ||
| let container = try decoder.singleValueContainer() | ||
|
|
||
| if container.decodeNil() { | ||
| self = .null | ||
| } else if let stringValue = try? container.decode(String.self) { | ||
| self = .string(stringValue) | ||
| } else if let intValue = try? container.decode(Int.self) { | ||
| self = .int(intValue) | ||
| } else if let doubleValue = try? container.decode(Double.self) { | ||
| self = .double(doubleValue) | ||
| } else if let boolValue = try? container.decode(Bool.self) { | ||
| self = .bool(boolValue) | ||
| } else if let arrayValue = try? container.decode([OperatorValue].self) { | ||
| self = .array(arrayValue) | ||
| } else { | ||
| throw DecodingError.dataCorruptedError( | ||
| in: container, | ||
| debugDescription: "OperatorValue cannot be decoded" | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| func encode(to encoder: Encoder) throws { | ||
| var container = encoder.singleValueContainer() | ||
| switch self { | ||
| case .string(let value): | ||
| try container.encode(value) | ||
| case .int(let value): | ||
| try container.encode(value) | ||
| case .double(let value): | ||
| try container.encode(value) | ||
| case .bool(let value): | ||
| try container.encode(value) | ||
| case .array(let value): | ||
| try container.encode(value) | ||
| case .null: | ||
| try container.encodeNil() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public struct Operator : Codable, CustomStringConvertible { | ||
| var method: String | ||
| var values: [OperatorValue]? | ||
|
|
||
| init(method: String, values: Any? = nil) { | ||
| self.method = method | ||
| self.values = Operator.convertToOperatorValueArray(values) | ||
| } | ||
|
|
||
| public init(from decoder: Decoder) throws { | ||
| let container = try decoder.container(keyedBy: CodingKeys.self) | ||
|
|
||
| self.method = try container.decode(String.self, forKey: .method) | ||
| self.values = try container.decodeIfPresent([OperatorValue].self, forKey: .values) | ||
| } | ||
|
|
||
| private static func convertToOperatorValueArray(_ values: Any?) -> [OperatorValue]? { | ||
| // Handle nil | ||
| if values == nil { | ||
| return nil | ||
| } | ||
|
|
||
| // Handle NSNull as [.null] | ||
| if values is NSNull { | ||
| return [.null] | ||
| } | ||
|
|
||
| switch values { | ||
| case let valueArray as [OperatorValue]: | ||
| return valueArray | ||
| case let stringArray as [String]: | ||
| return stringArray.map { .string($0) } | ||
| case let intArray as [Int]: | ||
| return intArray.map { .int($0) } | ||
| case let doubleArray as [Double]: | ||
| return doubleArray.map { .double($0) } | ||
| case let boolArray as [Bool]: | ||
| return boolArray.map { .bool($0) } | ||
| case let stringValue as String: | ||
| return [.string(stringValue)] | ||
| case let intValue as Int: | ||
| return [.int(intValue)] | ||
| case let doubleValue as Double: | ||
| return [.double(doubleValue)] | ||
| case let boolValue as Bool: | ||
| return [.bool(boolValue)] | ||
| case let anyArray as [Any]: | ||
| // Preserve empty arrays as empty OperatorValue arrays | ||
| if anyArray.isEmpty { | ||
| return [] | ||
| } | ||
|
|
||
| // Map all items, converting nil/unknown to .null | ||
| let nestedValues = anyArray.map { item -> OperatorValue in | ||
| if item is NSNull { | ||
| return .null | ||
| } else if let stringValue = item as? String { | ||
| return .string(stringValue) | ||
| } else if let intValue = item as? Int { | ||
| return .int(intValue) | ||
| } else if let doubleValue = item as? Double { | ||
| return .double(doubleValue) | ||
| } else if let boolValue = item as? Bool { | ||
| return .bool(boolValue) | ||
| } else if let nestedArray = item as? [Any] { | ||
| let converted = convertToOperatorValueArray(nestedArray) ?? [] | ||
| return .array(converted) | ||
| } else { | ||
| // Unknown/unsupported types become .null | ||
| return .null | ||
| } | ||
| } | ||
| return nestedValues | ||
| default: | ||
| // Unknown types become [.null] | ||
| return [.null] | ||
| } | ||
| } | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case method | ||
| case values | ||
| } | ||
|
|
||
| public func encode(to encoder: Encoder) throws { | ||
| var container = encoder.container(keyedBy: CodingKeys.self) | ||
| try container.encode(method, forKey: .method) | ||
|
|
||
| if (values != nil) { | ||
| try container.encode(values, forKey: .values) | ||
| } | ||
| } | ||
|
|
||
| public var description: String { | ||
| guard let data = try? JSONEncoder().encode(self) else { | ||
| return "" | ||
| } | ||
|
|
||
| return String(data: data, encoding: .utf8) ?? "" | ||
| } | ||
|
|
||
| public static func increment(_ value: Double = 1, max: Double? = nil) -> String { | ||
| if value.isNaN || value.isInfinite { | ||
| fatalError("Value cannot be NaN or Infinity") | ||
| } | ||
| if let max = max, max.isNaN || max.isInfinite { | ||
| fatalError("Max cannot be NaN or Infinity") | ||
| } | ||
| var values: [Any] = [value] | ||
| if let max = max { | ||
| values.append(max) | ||
| } | ||
| return Operator(method: "increment", values: values).description | ||
| } | ||
|
|
||
| public static func decrement(_ value: Double = 1, min: Double? = nil) -> String { | ||
| if value.isNaN || value.isInfinite { | ||
| fatalError("Value cannot be NaN or Infinity") | ||
| } | ||
| if let min = min, min.isNaN || min.isInfinite { | ||
| fatalError("Min cannot be NaN or Infinity") | ||
| } | ||
| var values: [Any] = [value] | ||
| if let min = min { | ||
| values.append(min) | ||
| } | ||
| return Operator(method: "decrement", values: values).description | ||
| } | ||
|
|
||
| public static func multiply(_ factor: Double, max: Double? = nil) -> String { | ||
| if factor.isNaN || factor.isInfinite { | ||
| fatalError("Factor cannot be NaN or Infinity") | ||
| } | ||
| if let max = max, max.isNaN || max.isInfinite { | ||
| fatalError("Max cannot be NaN or Infinity") | ||
| } | ||
| var values: [Any] = [factor] | ||
| if let max = max { | ||
| values.append(max) | ||
| } | ||
| return Operator(method: "multiply", values: values).description | ||
| } | ||
|
|
||
| public static func divide(_ divisor: Double, min: Double? = nil) -> String { | ||
| if divisor.isNaN || divisor.isInfinite { | ||
| fatalError("Divisor cannot be NaN or Infinity") | ||
| } | ||
| if let min = min, min.isNaN || min.isInfinite { | ||
| fatalError("Min cannot be NaN or Infinity") | ||
| } | ||
| if divisor == 0 { | ||
| fatalError("Divisor cannot be zero") | ||
| } | ||
| var values: [Any] = [divisor] | ||
| if let min = min { | ||
| values.append(min) | ||
| } | ||
| return Operator(method: "divide", values: values).description | ||
| } | ||
|
|
||
| public static func modulo(_ divisor: Double) -> String { | ||
| if divisor.isNaN || divisor.isInfinite { | ||
| fatalError("Divisor cannot be NaN or Infinity") | ||
| } | ||
| if divisor == 0 { | ||
| fatalError("Divisor cannot be zero") | ||
| } | ||
| return Operator(method: "modulo", values: [divisor]).description | ||
| } | ||
|
|
||
| public static func power(_ exponent: Double, max: Double? = nil) -> String { | ||
| if exponent.isNaN || exponent.isInfinite { | ||
| fatalError("Exponent cannot be NaN or Infinity") | ||
| } | ||
| if let max = max, max.isNaN || max.isInfinite { | ||
| fatalError("Max cannot be NaN or Infinity") | ||
| } | ||
| var values: [Any] = [exponent] | ||
| if let max = max { | ||
| values.append(max) | ||
| } | ||
| return Operator(method: "power", values: values).description | ||
| } | ||
|
|
||
| public static func arrayAppend(_ values: [Any]) -> String { | ||
| return Operator(method: "arrayAppend", values: values).description | ||
| } | ||
|
|
||
| public static func arrayPrepend(_ values: [Any]) -> String { | ||
| return Operator(method: "arrayPrepend", values: values).description | ||
| } | ||
|
|
||
| public static func arrayInsert(_ index: Int, value: Any) -> String { | ||
| return Operator(method: "arrayInsert", values: [index, value]).description | ||
| } | ||
|
|
||
| public static func arrayRemove(_ value: Any) -> String { | ||
| return Operator(method: "arrayRemove", values: [value]).description | ||
| } | ||
|
|
||
| public static func arrayUnique() -> String { | ||
| return Operator(method: "arrayUnique", values: []).description | ||
| } | ||
|
|
||
| public static func arrayIntersect(_ values: [Any]) -> String { | ||
| return Operator(method: "arrayIntersect", values: values).description | ||
| } | ||
|
|
||
| public static func arrayDiff(_ values: [Any]) -> String { | ||
| return Operator(method: "arrayDiff", values: values).description | ||
| } | ||
|
|
||
| public static func arrayFilter(_ condition: Condition, value: Any? = nil) -> String { | ||
| let values: [Any] = [condition.rawValue, value ?? NSNull()] | ||
| return Operator(method: "arrayFilter", values: values).description | ||
| } | ||
|
|
||
| public static func stringConcat(_ value: Any) -> String { | ||
| return Operator(method: "stringConcat", values: [value]).description | ||
| } | ||
|
|
||
| public static func stringReplace(_ search: String, _ replace: String) -> String { | ||
| return Operator(method: "stringReplace", values: [search, replace]).description | ||
| } | ||
|
|
||
| public static func toggle() -> String { | ||
| return Operator(method: "toggle", values: []).description | ||
| } | ||
|
|
||
| public static func dateAddDays(_ days: Int) -> String { | ||
| return Operator(method: "dateAddDays", values: [days]).description | ||
| } | ||
|
|
||
| public static func dateSubDays(_ days: Int) -> String { | ||
| return Operator(method: "dateSubDays", values: [days]).description | ||
| } | ||
|
|
||
| public static func dateSetNow() -> String { | ||
| return Operator(method: "dateSetNow", values: []).description | ||
| } | ||
| } | ||
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't crash consumer apps on invalid operator inputs
These helpers currently trap with
fatalErrorwhenever callers pass data likeDouble.nan,Double.infinity, or zero divisors. Because these APIs are public SDK surface, that means a runtime crash in any production app that forwards unvalidated user input—exactly what we’re trying to protect developers from. Apple’s own guidance and community best practices call outfatalErroras a tool for unrecoverable programming mistakes, not regular input validation; in SDK code it becomes a foot‑gun for your customers.(compilenrun.com)Instead of terminating the process, bubble up a normal error so callers can decide how to handle it (show a validation message, log and skip, etc.). One concrete way to achieve that without changing the JSON payload logic is to add a lightweight error enum and make these builders
throws, for example:Please apply the same pattern to
decrement,multiply,divide,modulo, andpower, so none of them can take the entire host app down.(compilenrun.com)