mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-10 02:08:38 +09:00
Dactyloidae iOS initial commit
This commit is contained in:
parent
daa6179d22
commit
7154a0497e
2123 changed files with 197052 additions and 0 deletions
253
mobile/ios/Shared/Functions.swift
Normal file
253
mobile/ios/Shared/Functions.swift
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import SwiftyJSON
|
||||
|
||||
// Pipelining.
|
||||
precedencegroup PipelinePrecedence {
|
||||
associativity: left
|
||||
}
|
||||
infix operator |> : PipelinePrecedence
|
||||
|
||||
public func |> <T, U>(x: T, f: (T) -> U) -> U {
|
||||
return f(x)
|
||||
}
|
||||
|
||||
// Basic currying.
|
||||
public func curry<A, B>(_ f: @escaping (A) -> B) -> (A) -> B {
|
||||
return { a in
|
||||
return f(a)
|
||||
}
|
||||
}
|
||||
|
||||
public func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
|
||||
return { a in
|
||||
return { b in
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func curry<A, B, C, D>(_ f: @escaping (A, B, C) -> D) -> (A) -> (B) -> (C) -> D {
|
||||
return { a in
|
||||
return { b in
|
||||
return { c in
|
||||
return f(a, b, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func curry<A, B, C, D, E>(_ f: @escaping (A, B, C, D) -> E) -> (A, B, C) -> (D) -> E {
|
||||
return { (a, b, c) in
|
||||
return { d in
|
||||
return f(a, b, c, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function composition.
|
||||
infix operator •
|
||||
|
||||
public func •<T, U, V>(f: @escaping (T) -> U, g: @escaping (U) -> V) -> (T) -> V {
|
||||
return { t in
|
||||
return g(f(t))
|
||||
}
|
||||
}
|
||||
public func •<T, V>(f: @escaping (T) -> Void, g: @escaping () -> V) -> (T) -> V {
|
||||
return { t in
|
||||
f(t)
|
||||
return g()
|
||||
}
|
||||
}
|
||||
public func •<V>(f: @escaping () -> Void, g: @escaping () -> V) -> () -> V {
|
||||
return {
|
||||
f()
|
||||
return g()
|
||||
}
|
||||
}
|
||||
|
||||
// Why not simply provide an override for ==? Well, that's scary, and can accidentally recurse.
|
||||
// This is enough to catch arrays, which Swift will delegate to element-==.
|
||||
public func optArrayEqual<T: Equatable>(_ lhs: [T]?, rhs: [T]?) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case (.none, _):
|
||||
return false
|
||||
case (_, .none):
|
||||
return false
|
||||
default:
|
||||
// This delegates to Swift's own array '==', which calls T's == on each element.
|
||||
return lhs! == rhs!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array, return an array of slices of size `by` (possibly excepting the last slice).
|
||||
*
|
||||
* If `by` is longer than the input, returns a single chunk.
|
||||
* If `by` is less than 1, acts as if `by` is 1.
|
||||
* If the length of the array isn't a multiple of `by`, the final slice will
|
||||
* be smaller than `by`, but never empty.
|
||||
*
|
||||
* If the input array is empty, returns an empty array.
|
||||
*/
|
||||
|
||||
public func chunk<T>(_ arr: [T], by: Int) -> [ArraySlice<T>] {
|
||||
var result = [ArraySlice<T>]()
|
||||
var chunk = -1
|
||||
let size = max(1, by)
|
||||
for (index, elem) in arr.enumerated() {
|
||||
if index % size == 0 {
|
||||
result.append(ArraySlice<T>())
|
||||
chunk += 1
|
||||
}
|
||||
result[chunk].append(elem)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public func chunkCollection<E, X, T: Collection>(_ items: T, by: Int, f: ([E]) -> [X]) -> [X] where T.Iterator.Element == E {
|
||||
assert(by >= 0)
|
||||
let max = by > 0 ? by : 1
|
||||
var i = 0
|
||||
var acc: [E] = []
|
||||
var results: [X] = []
|
||||
var iter = items.makeIterator()
|
||||
|
||||
while let item = iter.next() {
|
||||
if i >= max {
|
||||
results.append(contentsOf: f(acc))
|
||||
acc = []
|
||||
i = 0
|
||||
}
|
||||
acc.append(item)
|
||||
i += 1
|
||||
}
|
||||
|
||||
if !acc.isEmpty {
|
||||
results.append(contentsOf: f(acc))
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
public extension Sequence {
|
||||
// [T] -> (T -> K) -> [K: [T]]
|
||||
// As opposed to `groupWith` (to follow Haskell's naming), which would be
|
||||
// [T] -> (T -> K) -> [[T]]
|
||||
func groupBy<Key, Value>(_ selector: (Self.Iterator.Element) -> Key, transformer: (Self.Iterator.Element) -> Value) -> [Key: [Value]] {
|
||||
var acc: [Key: [Value]] = [:]
|
||||
for x in self {
|
||||
let k = selector(x)
|
||||
var a = acc[k] ?? []
|
||||
a.append(transformer(x))
|
||||
acc[k] = a
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func zip<S: Sequence>(_ elems: S) -> [(Self.Iterator.Element, S.Iterator.Element)] {
|
||||
var rights = elems.makeIterator()
|
||||
return self.flatMap { lhs in
|
||||
guard let rhs = rights.next() else {
|
||||
return nil
|
||||
}
|
||||
return (lhs, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func optDictionaryEqual<K, V: Equatable>(_ lhs: [K: V]?, rhs: [K: V]?) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case (.none, _):
|
||||
return false
|
||||
case (_, .none):
|
||||
return false
|
||||
default:
|
||||
return lhs! == rhs!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return members of `a` that aren't nil, changing the type of the sequence accordingly.
|
||||
*/
|
||||
public func optFilter<T>(_ a: [T?]) -> [T] {
|
||||
return a.flatMap { $0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new map with only key-value pairs that have a non-nil value.
|
||||
*/
|
||||
public func optFilter<K, V>(_ source: [K: V?]) -> [K: V] {
|
||||
var m = [K: V]()
|
||||
for (k, v) in source {
|
||||
if let v = v {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a function over the values of a map.
|
||||
*/
|
||||
public func mapValues<K, T, U>(_ source: [K: T], f: ((T) -> U)) -> [K: U] {
|
||||
var m = [K: U]()
|
||||
for (k, v) in source {
|
||||
m[k] = f(v)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
public func findOneValue<K, V>(_ map: [K: V], f: (V) -> Bool) -> V? {
|
||||
for v in map.values {
|
||||
if f(v) {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a JSON array, returning the String elements as an array.
|
||||
* It's usually convenient for this to accept an optional.
|
||||
*/
|
||||
public func jsonsToStrings(_ arr: [JSON]?) -> [String]? {
|
||||
return arr?.flatMap { $0.stringValue }
|
||||
}
|
||||
|
||||
// Encapsulate a callback in a way that we can use it with NSTimer.
|
||||
private class Callback {
|
||||
private let handler:() -> Void
|
||||
|
||||
init(handler:@escaping () -> Void) {
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
@objc
|
||||
func go() {
|
||||
handler()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from http://stackoverflow.com/questions/27116684/how-can-i-debounce-a-method-call
|
||||
* Allows creating a block that will fire after a delay. Resets the timer if called again before the delay expires.
|
||||
**/
|
||||
public func debounce(_ delay: TimeInterval, action:@escaping () -> Void) -> () -> Void {
|
||||
let callback = Callback(handler: action)
|
||||
var timer: Timer?
|
||||
|
||||
return {
|
||||
// If calling again, invalidate the last timer.
|
||||
if let timer = timer {
|
||||
timer.invalidate()
|
||||
}
|
||||
timer = Timer(timeInterval: delay, target: callback, selector: #selector(Callback.go), userInfo: nil, repeats: false)
|
||||
RunLoop.current.add(timer!, forMode: RunLoopMode.defaultRunLoopMode)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue