text:
# 聚集型態
---
Swift支援兩種*聚集型態*, 像已知的陣列型態以及字典型態,給予容器將值聚集儲存。陣列型態以序列方式儲存相同資料型態的資料。字典型態以無序列方式儲存相同資料型態的資料，它可以使用唯一的識別字從頭到尾尋找並將值取出(但必須知道它的識別字).

陣列型態以及字典型態在Swift總是清楚關於型態值和識別字怎麼儲存，意思是你無法加入錯誤的資料型態在陣列或是字典中。也意味著你可以確信型態和值可以復取在陣列或是字典中。
Swift使用顯示類型聚集確保你的程式碼清楚於型態與值可以工作以及使你早點抓取任何不匹配的型態在你開發程式時。

> **註解**
>
> Swift的`陣列` 型態配置了固定或可變得元素在不同型態下,或在通過凡是或方法的時候都會表現不同的行為。想了解更多訊息，請參考 [Mutability of Collections]() 和 [Assignment and Copy Behavior for Collection Types]().

## 陣列

*陣列*以序列的方式在同個型態中存放著多個值。同樣的值可以在不同的時間以及不同的索引下出現在陣列裡。

Swift陣列都明確的表示他們儲存值的形態。他們不同於Objective-C的`NSArray`和 `NSMutableArray`類別， which can store any kind of object and do not provide any information about the nature of the objects they return. In Swift, the type of values that a particular array can store is always made clear, either through an explicit type annotation, or through type inference, and does not have to be a class type. If you create an array of `Int` values, for example, you can’t insert any value other than `Int` values into that array. Swift arrays are type safe, and are always clear about what they may contain.

### Array Type Shorthand Syntax

The type of a Swift array is written in full as `Array<SomeType>`, where `SomeType` is the type that the array is allowed to store. You can also write the type of an array in shorthand form as `SomeType[]`. Although the two forms are functionally identical, the shorthand form is preferred, and is used throughout this guide when referring to the type of an array.

### Array Literals

You can initialize an array with an *array literal*, which is a shorthand way to write one or more values as an array collection. An array literal is written as a list of values, separated by commas, surrounded by a pair of square brackets:

```swift
[value 1, value 2, value 3]
```

The example below creates an array called `shoppingList` to store `String` values:

```swift
var shoppingList: String[] = ["Eggs", "Milk"]
// shoppingList has been initialized with two initial items
```

The `shoppingList` variable is declared as “an array of `String` values”, written as `String[]`. Because this particular array has specified a value type of `String`, it is only allowed to store `String` values. Here, the `shoppingList` array is initialized with two `String` values (`"Eggs"` and `"Milk"`), written within an array literal.

> **NOTE**
>
> The `shoppingList` array is declared as a variable (with the `var` introducer) and not a constant (with the `let` introducer) because more items are added to the shopping list in the examples below.

In this case, the array literal contains two `String` values and nothing else. This matches the type of the `shoppingList` variable’s declaration (an array that can only contain `String` values), and so the assignment of the array literal is permitted as a way to initialize `shoppingList` with two initial items.

Thanks to Swift’s type inference, you don’t have to write the type of the array if you’re initializing it with an array literal containing values of the same type. The initialization of `shoppingList` could have been written in a shorter form instead:

```swift
var shoppingList = ["Eggs", "Milk"]
```

Because all values in the array literal are of the same type, Swift can infer that `String[]` is the correct type to use for the `shoppingList` variable.

### Accessing and Modifying an Array

You access and modify an array through its methods and properties, or by using subscript syntax.

To find out the number of items in an array, check its read-only `count` property:

```swift
println("The shopping list contains \(shoppingList.count) items.")
// prints "The shopping list contains 2 items."
```

Use the Boolean `isEmpty` property as a shortcut for checking whether the `count` property is equal to `0`:

```swift
if shoppingList.isEmpty {
    println("The shopping list is empty.")
} else {
    println("The shopping list is not empty.")
}
// prints "The shopping list is not empty."
```

You can add a new item to the end of an array by calling the array’s `append` method:

```swift
shoppingList.append("Flour")
// shoppingList now contains 3 items, and someone is making pancakes
```

Alternatively, add a new item to the end of an array with the addition assignment operator (`+=`):

```swift
shoppingList += "Baking Powder"
// shoppingList now contains 4 items
```

You can also append an array of compatible items with the addition assignment operator (`+=`):

```swift
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList now contains 7 items
```

Retrieve a value from the array by using *subscript syntax*, passing the index of the value you want to retrieve within square brackets immediately after the name of the array:

```swift
var firstItem = shoppingList[0]
// firstItem is equal to "Eggs"
```

Note that the first item in the array has an index of `0`, not `1`. Arrays in Swift are always zero-indexed.

You can use subscript syntax to change an existing value at a given index:

```swift
shoppingList[0] = "Six eggs"
// the first item in the list is now equal to "Six eggs" rather than "Eggs"
```

You can also use subscript syntax to change a range of values at once, even if the replacement set of values has a different length than the range you are replacing. The following example replaces `"Chocolate Spread"`, `"Cheese"`, and `"Butter"` with `"Bananas"` and `"Apples"`:

```swift
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList now contains 6 items
```

> **NOTE**
>
> You can’t use subscript syntax to append a new item to the end of an array. If you try to use subscript syntax to retrieve or set a value for an index that is outside of an array’s existing bounds, you will trigger a runtime error. However, you can check that an index is valid before using it, by comparing it to the array’s `count` property. Except when `count` is `0` (meaning the array is empty), the largest valid index in an array will always be `count - 1`, because arrays are indexed from zero.

To insert an item into the array at a specified index, call the array’s `insert(atIndex:)` method:

```swift
shoppingList.insert("Maple Syrup", atIndex: 0)
// shoppingList now contains 7 items
// "Maple Syrup" is now the first item in the list
```

This call to the `insert` method inserts a new item with a value of `"Maple Syrup"` at the very beginning of the shopping list, indicated by an index of `0`.

Similarly, you remove an item from the array with the `removeAtIndex` method. This method removes the item at the specified index and returns the removed item (although you can ignore the returned value if you do not need it):

```swift
let mapleSyrup = shoppingList.removeAtIndex(0)
// the item that was at index 0 has just been removed
// shoppingList now contains 6 items, and no Maple Syrup
// the mapleSyrup constant is now equal to the removed "Maple Syrup" string
```

Any gaps in an array are closed when an item is removed, and so the value at index `0` is once again equal to `"Six eggs"`:

```swift
firstItem = shoppingList[0]
// firstItem is now equal to "Six eggs"
```

If you want to remove the final item from an array, use the `removeLast` method rather than the `removeAtIndex` method to avoid the need to query the array’s `count` property. Like the `removeAtIndex` method, `removeLast` returns the removed item:

```swift
let apples = shoppingList.removeLast()
// the last item in the array has just been removed
// shoppingList now contains 5 items, and no cheese
// the apples constant is now equal to the removed "Apples" string
```

### Iterating Over an Array

You can iterate over the entire set of values in an array with the `for`-`in` loop:

```swift
for item in shoppingList {
    println(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
```

If you need the integer index of each item as well as its value, use the global `enumerate` function to iterate over the array instead. The `enumerate` function returns a tuple for each item in the array composed of the index and the value for that item. You can decompose the tuple into temporary constants or variables as part of the iteration:

```swift
for (index, value) in enumerate(shoppingList) {
    println("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
```

For more about the `for`-`in` loop, see [For Loops](205_Control_Flow.html#for-loops "For Loops").

### Creating and Initializing an Array

You can create an empty array of a certain type (without setting any initial values) using initializer syntax:

```swift
var someInts = Int[]()
println("someInts is of type Int[] with \(someInts.count) items.")
// prints "someInts is of type Int[] with 0 items."
```

Note that the type of the `someInts` variable is inferred to be `Int[]`, because it is set to the output of an `Int[]` initializer.

Alternatively, if the context already provides type information, such as a function argument or an already-typed variable or constant, you can create an empty array with an empty array literal, which is written as `[]` (an empty pair of square brackets):

```swift
someInts.append(3)
// someInts now contains 1 value of type Int
someInts = []
// someInts is now an empty array, but is still of type Int[]
```

Swift’s `Array` type also provides an initializer for creating an array of a certain size with all of its values set to a provided default value. You pass this initializer the number of items to be added to the new array (called `count`) and a default value of the appropriate type (called `repeatedValue`):

```swift
var threeDoubles = Double[](count: 3, repeatedValue: 0.0)
// threeDoubles is of type Double[], and equals [0.0, 0.0, 0.0]
```

Thanks to type inference, you don’t need to specify the type to be stored in the array when using this initializer, because it can be inferred from the default value:

```swift
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
// anotherThreeDoubles is inferred as Double[], and equals [2.5, 2.5, 2.5]
```

Finally, you can create a new array by adding together two existing arrays of compatible type with the addition operator (`+`). The new array’s type is inferred from the type of the two arrays you add together:

```swift
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as Double[], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
```

## Dictionaries

A *dictionary* is a container that stores multiple values of the same type. Each value is associated with a unique key, which acts as an identifier for that value within the dictionary. Unlike items in an array, items in a dictionary do not have a specified order. You use a dictionary when you need to look up values based on their identifier, in much the same way that a real-world dictionary is used to look up the definition for a particular word.

Swift dictionaries are specific about the types of keys and values they can store. They differ from Objective-C’s `NSDictionary` and `NSMutableDictionary` classes, which can use any kind of object as their keys and values and do not provide any information about the nature of these objects. In Swift, the type of keys and values that a particular dictionary can store is always made clear, either through an explicit type annotation or through type inference.

Swift’s dictionary type is written as `Dictionary<KeyType, ValueType>`, where `KeyType` is the type of value that can be used as a dictionary key, and `ValueType` is the type of value that the dictionary stores for those keys.

The only restriction is that `KeyType` must be hashable—that is, it must provide a way to make itself uniquely representable. All of Swift’s basic types (such as `String`, `Int`, `Double`, and `Bool`) are hashable by default, and all of these types can be used as the keys of a dictionary. Enumeration member values without associated values (as described in [Enumerations]()) are also hashable by default.

### Dictionary Literals

You can initialize a dictionary with with a *dictionary literal*, which has a similar syntax to the array literal seen earlier. A dictionary literal is a shorthand way to write one or more key-value pairs as a `Dictionary` collection.

A *key-value pair* is a combination of a key and a value. In a dictionary literal, the key and value in each key-value pair are separated by a colon. The key-value pairs are written as a list, separated by commas, surrounded by a pair of square brackets:

```swift
[key 1: value 1, key 2: value 2, key 3: value 3]`
```

The example below creates a dictionary to store the names of international airports. In this dictionary, the keys are three-letter International Air Transport Association codes, and the values are airport names:

```swift
var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]
```

The `airports` dictionary is declared as having a type of `Dictionary<String, String>`, which means “a `Dictionary` whose keys are of type `String`, and whose values are also of type `String`”.

> **NOTE**
>
> The `airports` dictionary is declared as a variable (with the `var` introducer), and not a constant (with the `let` introducer), because more airports will be added to the dictionary in the examples below.

The `airports` dictionary is initialized with a dictionary literal containing two key-value pairs. The first pair has a key of `"TYO"` and a value of `"Tokyo"`. The second pair has a key of `"DUB"` and a value of `"Dublin"`.

This dictionary literal contains two `String: String` pairs. This matches the type of the `airports` variable declaration (a dictionary with only `String` keys, and only `String` values) and so the assignment of the dictionary literal is permitted as a way to initialize the `airports` dictionary with two initial items.

As with arrays, you don’t have to write the type of the dictionary if you’re initializing it with a dictionary literal whose keys and values have consistent types. The initialization of `airports` could have been be written in a shorter form instead:

```swift
var airports = ["TYO": "Tokyo", "DUB": "Dublin"]
```

Because all keys in the literal are of the same type as each other, and likewise all values are of the same type as each other, Swift can infer that `Dictionary<String, String>` is the correct type to use for the `airports` dictionary.

### Accessing and Modifying a Dictionary

You access and modify a dictionary through its methods and properties, or by using subscript syntax. As with an array, you can find out the number of items in a `Dictionary` by checking its read-only `count` property:

```swift
println("The dictionary of airports contains \(airports.count) items.")
// prints "The dictionary of airports contains 2 items."
```

You can add a new item to a dictionary with subscript syntax. Use a new key of the appropriate type as the subscript index, and assign a new value of the appropriate type:

```swift
airports["LHR"] = "London"
// the airports dictionary now contains 3 items
```

You can also use subscript syntax to change the value associated with a particular key:

```swift
airports["LHR"] = "London Heathrow"
// the value for "LHR" has been changed to "London Heathrow"
```

As an alternative to subscripting, use a dictionary’s `updateValue(forKey:)` method to set or update the value for a particular key. Like the subscript examples above, the `updateValue(forKey:)` method sets a value for a key if none exists, or updates the value if that key already exists. Unlike a subscript, however, the `updateValue(forKey:)` method returns the *old* value after performing an update. This enables you to check whether or not an update took place.

The `updateValue(forKey:)` method returns an optional value of the dictionary’s value type. For a dictionary that stores `String` values, for example, the method returns a value of type `String?`, or “optional `String`”. This optional value contains the old value for that key if one existed before the update, or `nil` if no value existed:

```swift
if let oldValue = airports.updateValue("Dublin International", forKey: "DUB") {
    println("The old value for DUB was \(oldValue).")
}
// prints "The old value for DUB was Dublin."
```

You can also use subscript syntax to retrieve a value from the dictionary for a particular key. Because it is possible to request a key for which no value exists, a dictionary’s subscript returns an optional value of the dictionary’s value type. If the dictionary contains a value for the requested key, the subscript returns an optional value containing the existing value for that key. Otherwise, the subscript returns `nil`:

```swift
if let airportName = airports["DUB"] {
    println("The name of the airport is \(airportName).")
} else {
    println("That airport is not in the airports dictionary.")
}
// prints "The name of the airport is Dublin International."
```

You can use subscript syntax to remove a key-value pair from a dictionary by assigning a value of `nil` for that key:

```swift
airports["APL"] = "Apple International"
// "Apple International" is not the real airport for APL, so delete it
airports["APL"] = nil
// APL has now been removed from the dictionary
```

Alternatively, remove a key-value pair from a dictionary with the `removeValueForKey` method. This method removes the key-value pair if it exists and returns the removed value, or returns `nil` if no value existed:

```swift
if let removedValue = airports.removeValueForKey("DUB") {
    println("The removed airport's name is \(removedValue).")
} else {
    println("The airports dictionary does not contain a value for DUB.")
}
// prints "The removed airport's name is Dublin International."
```

### Iterating Over a Dictionary

You can iterate over the key-value pairs in a dictionary with a `for`-`in` loop. Each item in the dictionary is returned as a `(key, value)` tuple, and you can decompose the tuple’s members into temporary constants or variables as part of the iteration:

```swift
for (airportCode, airportName) in airports {
    println("\(airportCode): \(airportName)")
}
// TYO: Tokyo
// LHR: London Heathrow
```

For more about the `for`-`in` loop, see [For Loops](205_Control_Flow.html#for-loops "For Loops").

You can also retrieve an iteratable collection of a dictionary’s keys or values by accessing its `keys` and `values` properties:

```swift
for airportCode in airports.keys {
    println("Airport code: \(airportCode)")
}
// Airport code: TYO
// Airport code: LHR

for airportName in airports.values {
    println("Airport name: \(airportName)")
}
// Airport name: Tokyo
// Airport name: London Heathrow
```

If you need to use a dictionary’s keys or values with an API that takes an `Array` instance, initialize a new array with the `keys` or `values` property:

```swift
let airportCodes = Array(airports.keys)
// airportCodes is ["TYO", "LHR"]

let airportNames = Array(airports.values)
// airportNames is ["Tokyo", "London Heathrow"]
```

> **NOTE**
>
> Swift’s `Dictionary` type is an unordered collection. The order in which keys, values, and key-value pairs are retrieved when iterating over a dictionary is not specified.

### Creating an Empty Dictionary

As with arrays, you can create an empty `Dictionary` of a certain type by using initializer syntax:

```swift
var namesOfIntegers = Dictionary<Int, String>()
// namesOfIntegers is an empty Dictionary<Int, String>
```

This example creates an empty dictionary of type `Int`, `String` to store human-readable names of integer values. Its keys are of type `Int`, and its values are of type `String`.

If the context already provides type information, create an empty dictionary with an empty dictionary literal, which is written as `[:]` (a colon inside a pair of square brackets):

```swift
namesOfIntegers[16] = "sixteen"
// namesOfIntegers now contains 1 key-value pair
namesOfIntegers = [:]
// namesOfIntegers is once again an empty dictionary of type Int, String
```

> **NOTE**
>
> Behind the scenes, Swift’s array and dictionary types are implemented as *generic collections*. For more on generic types and collections, see [Generics]().

## Mutability of Collections

Arrays and dictionaries store multiple values together in a single collection. If you create an array or a dictionary and assign it to a variable, the collection that is created will be *mutable*. This means that you can change (or *mutate*) the size of the collection after it is created by adding more items to the collection, or by removing existing items from the ones it already contains. Conversely, if you assign an array or a dictionary to a constant, that array or dictionary is *immutable*, and its size cannot be changed.

For dictionaries, immutability also means that you cannot replace the value for an existing key in the dictionary. An immutable dictionary’s contents cannot be changed once they are set.

Immutability has a slightly different meaning for arrays, however. You are still not allowed to perform any action that has the potential to change the size of an immutable array, but you are allowed to set a new value for an existing index in the array. This enables Swift’s `Array` type to provide optimal performance for array operations when the size of an array is fixed.

The mutability behavior of Swift’s `Array` type also affects how array instances are assigned and modified. For more information, see [Assignment and Copy Behavior for Collection Types]().

> **NOTE**
>
> It is good practice to create immutable collections in all cases where the collection’s size does not need to change. Doing so enables the Swift compiler to optimize the performance of the collections you create.


text:
# Axchange

Android file transfer based on ADB.

## Preview

![Preview](./Resource/Preview.png)

## License

The Axchange is released under [The Unlicensed License](./LICENSE).

## Contributor

Made with love by [@Lakr233](https://twitter.com/Lakr233) for [@lz___233](https://twitter.com/lz__233) ~~咱写还不行么~~

Thanks [@unixzii](https://twitter.com/unixzii) for being with me so many times.

---

Copyright © 2022 Lakr Aream. All Rights Reserved.

repo_language: Swift

mime_type: text/plain


text:
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "AuxiliaryExecute",
    products: [
        .library(
            name: "AuxiliaryExecute",
            targets: ["AuxiliaryExecute"]
        ),
    ],
    targets: [
        .target(
            name: "AuxiliaryExecute",
            dependencies: []
        ),
        .testTarget(
            name: "AuxiliaryExecuteTests",
            dependencies: ["AuxiliaryExecute"]
        ),
    ]
)


text:
# AuxiliaryExecute

A Swift wrapper for system shell over posix_spawn with search path and env support.

## Usage

```
import AuxiliaryExecute

AuxiliaryExecute.local.bash(command: "echo nya")
```

## Customization & Defaults

The source for this package is well explained in details along with comments. Feel free looking for them.

```
// automatically search for binary within env PATH
let result = AuxiliaryExecute.local.shell(
    command: "bash",
    args: ["-c", "echo $mua"],
    environment: ["mua": "nya"],
    timeout: 0
) { stdout in
    print(stdout)
} stderrBlock: { stderr in
    print(stderr)
}

// or call with binary's full path
func spawn(
    command: String,
    args: [String] = [],
    environment: [String: String] = [:],
    timeout: Double = 0,
    stdoutBlock: ((String) -> Void)? = nil,
    stderrBlock: ((String) -> Void)? = nil
)

// for customize option for shell
func appendSearchPath(with value: String)
func updateExtraSearchPath(with block: (inout [String]) -> Void)
func updateOverwriteTable(with block: (inout [String: String?]) -> Void)
```

## License

AuxiliaryExecute is licensed under [MIT](./LICENSE).

---

Copyright © 2021 Lakr Aream. All Rights Reserved.


text:
@testable import AuxiliaryExecute
import XCTest

final class AuxiliaryExecuteTests: XCTestCase {
    func testMain() throws {
        XCTAssertNotNil(Int(exactly: AuxiliaryExecute.maxTimeoutValue))
        XCTAssertNotNil(Int32(exactly: AuxiliaryExecute.maxTimeoutValue))
        XCTAssertNotNil(Double(exactly: AuxiliaryExecute.maxTimeoutValue))
        XCTAssertNotNil(TimeInterval(exactly: AuxiliaryExecute.maxTimeoutValue))

        do {
            let result = AuxiliaryExecute.local.bash(command: "printf \"\nnya\n\"")
            print(result)
            XCTAssert(result.exitCode == 0)
            XCTAssertNil(result.error)
            XCTAssert(result.stdout == "\nnya\n")
            XCTAssert(result.stderr == "")
        }

        do {
            let result = AuxiliaryExecute.local.shell(
                command: "bash",
                args: ["-c", "echo $mua"],
                environment: ["mua": "nya"],
                timeout: 0
            ) { stdout in
                print(stdout)
            } stderrBlock: { stderr in
                print(stderr)
            }

            XCTAssert(result.exitCode == 0)
            XCTAssertNil(result.error)
            XCTAssert(result.stdout.trimmingCharacters(in: .whitespacesAndNewlines) == "nya")
            XCTAssert(result.stderr == "")
        }

        do {
            let result = AuxiliaryExecute.local.shell(
                command: "bash",
                args: ["-c", "echo $mua"],
                environment: ["mua": "nya=nya="],
                timeout: 0
            ) { stdout in
                print(stdout)
            } stderrBlock: { stderr in
                print(stderr)
            }

            XCTAssert(result.exitCode == 0)
            XCTAssertNil(result.error)
            XCTAssert(result.stdout.trimmingCharacters(in: .whitespacesAndNewlines) == "nya=nya=")
            XCTAssert(result.stderr == "")
        }

        do {
            let result = AuxiliaryExecute.local.shell(
                command: "tail",
                args: ["-f", "/dev/null"],
                timeout: 1
            ) { stdout in
                print(stdout)
            } stderrBlock: { stderr in
                print(stderr)
            }

            XCTAssert(result.exitCode == SIGKILL)
            XCTAssert(result.error == .timeout)
        }
    }

    @available(macOS 12.0.0, *)
    func testAsync() async throws {
        do {
            let result = await AuxiliaryExecute.spawnAsync(
                command: "/usr/bin/uname",
                args: ["-a"],
                timeout: 1
            ) { stdout in
                print(stdout)
            } stderrBlock: { stderr in
                print(stderr)
            }

            XCTAssertEqual(result.exitCode, 0)
            XCTAssert(result.stdout.contains("Darwin Kernel"))
        }

        do {
            let result = await AuxiliaryExecute.spawnAsync(
                command: "/usr/bin/tail",
                args: ["-f", "/dev/null"],
                timeout: 1
            ) { stdout in
                print(stdout)
            } stderrBlock: { stderr in
                print(stderr)
            }

            XCTAssertEqual(result.exitCode, Int(SIGKILL))
            XCTAssertEqual(result.error, .timeout)
        }
    }
}


text:
//
//  AuxiliaryExecute.swift
//  MyYearWithGit
//
//  Created by Lakr Aream on 2021/11/27.
//

import Foundation

/// Execute command or shell with posix, shared with AuxiliaryExecute.local
public class AuxiliaryExecute {
    /// we do not recommend you to subclass this singleton
    public static let local = AuxiliaryExecute()

    // if binary not found when you call the shell api
    // we will take some time to rebuild the bianry table each time
    // -->>> this is a time-heavy-task
    // so use binaryLocationFor(command:) to cache it if needed

    // system path
    internal var currentPath: [String] = []
    // system binary table
    internal var binaryTable: [String: String] = [:]

    // for you to put your own search path
    internal var extraSearchPath: [String] = []
    // for you to set your own binary table and will be used firstly
    // if you set nil here
    // -> we will return nil even the binary found in system path
    internal var overwriteTable: [String: String?] = [:]

    // this value is used when providing 0 or negative timeout paramete
    internal static let maxTimeoutValue: Double = 2_147_483_647

    /// when reading from file pipe, must called from async queue
    internal static let pipeControlQueue = DispatchQueue(
        label: "wiki.qaq.AuxiliaryExecute.pipeRead",
        attributes: .concurrent
    )

    /// when killing process or monitoring events from process, must called from async queue
    /// we are making this queue serial queue so won't called at the same time when timeout
    internal static let processControlQueue = DispatchQueue(
        label: "wiki.qaq.AuxiliaryExecute.processControl",
        attributes: []
    )

    /// used for setting binary table, avoid crash
    internal let lock = NSLock()

    /// nope!
    private init() {
        // no need to setup binary table
        // we will make call to it when you call the shell api
        // if you only use the spawn api
        // we don't need to setup the hole table cause it‘s time-heavy-task
    }

    /// Execution Error, do the localization your self
    public enum ExecuteError: Error, LocalizedError, Codable {
        // not found in path
        case commandNotFound
        // invalid, may be missing, wrong permission or any other reason
        case commandInvalid
        // fcntl failed
        case openFilePipeFailed
        // posix failed
        case posixSpawnFailed
        // waitpid failed
        case waitPidFailed
        // timeout when execute
        case timeout
    }

    /// Execution Recipe
    public struct ExecuteRecipe: Codable {
        // exit code, usually 0 - 255 by system
        // -1 means something bad happened, set by us for convince
        public let exitCode: Int
        // process pid that was when it is alive
        // -1 means spawn failed in some situation
        public let pid: Int
        // wait result for final waitpid inside block at
        // processSource - eventMask.exit, usually is pid
        // -1 for other cases
        public let wait: Int
        // any error from us, not the command it self
        // DOES NOT MEAN THAT THE COMMAND DONE WELL
        public let error: ExecuteError?
        // stdout
        public let stdout: String
        // stderr
        public let stderr: String

        /// General initialization of recipe object
        /// - Parameters:
        ///   - exitCode: code when process exit
        ///   - pid: pid when process alive
        ///   - wait: wait result on waitpid
        ///   - error: error if any
        ///   - stdout: stdout
        ///   - stderr: stderr
        internal init(
            exitCode: Int,
            pid: Int,
            wait: Int,
            error: AuxiliaryExecute.ExecuteError?,
            stdout: String,
            stderr: String
        ) {
            self.exitCode = exitCode
            self.pid = pid
            self.wait = wait
            self.error = error
            self.stdout = stdout
            self.stderr = stderr
        }

        /// Template for making failure recipe
        /// - Parameters:
        ///   - exitCode: default -1
        ///   - pid: default -1
        ///   - wait: default -1
        ///   - error: error
        ///   - stdout: default empty
        ///   - stderr: default empty
        internal static func failure(
            exitCode: Int = -1,
            pid: Int = -1,
            wait: Int = -1,
            error: AuxiliaryExecute.ExecuteError?,
            stdout: String = "",
            stderr: String = ""
        ) -> ExecuteRecipe {
            .init(
                exitCode: exitCode,
                pid: pid,
                wait: wait,
                error: error,
                stdout: stdout,
                stderr: stderr
            )
        }
    }
}


text:
//
//  AuxiliaryExecute+Spawn.swift
//  AuxiliaryExecute
//
//  Created by Lakr Aream on 2021/12/6.
//

import Foundation

public extension AuxiliaryExecute {
    /// call posix spawn to begin execute
    /// - Parameters:
    ///   - command: full path of the binary file. eg: "/bin/cat"
    ///   - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
    ///   - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
    ///   - timeout: any wall timeout if lager than 0, in seconds. eg: 6
    ///   - output: a block call from pipeControlQueue in background when buffer from stdout or stderr available for read
    /// - Returns: execution recipe, see it's definition for details
    @discardableResult
    static func spawn(
        command: String,
        args: [String] = [],
        environment: [String: String] = [:],
        timeout: Double = 0,
        setPid: ((pid_t) -> Void)? = nil,
        output: ((String) -> Void)? = nil
    )
        -> ExecuteRecipe
    {
        let outputLock = NSLock()
        let result = spawn(
            command: command,
            args: args,
            environment: environment,
            timeout: timeout,
            setPid: setPid
        ) { str in
            outputLock.lock()
            output?(str)
            outputLock.unlock()
        } stderrBlock: { str in
            outputLock.lock()
            output?(str)
            outputLock.unlock()
        }
        return result
    }

    /// call posix spawn to begin execute and block until the process exits
    /// - Parameters:
    ///   - command: full path of the binary file. eg: "/bin/cat"
    ///   - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
    ///   - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
    ///   - timeout: any wall timeout if lager than 0, in seconds. eg: 6
    ///   - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read
    ///   - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read
    /// - Returns: execution recipe, see it's definition for details
    static func spawn(
        command: String,
        args: [String] = [],
        environment: [String: String] = [:],
        timeout: Double = 0,
        setPid: ((pid_t) -> Void)? = nil,
        stdoutBlock: ((String) -> Void)? = nil,
        stderrBlock: ((String) -> Void)? = nil
    ) -> ExecuteRecipe {
        let sema = DispatchSemaphore(value: 0)
        var recipe: ExecuteRecipe!
        spawn(
            command: command,
            args: args,
            environment: environment,
            timeout: timeout,
            setPid: setPid,
            stdoutBlock: stdoutBlock,
            stderrBlock: stderrBlock
        ) {
            recipe = $0
            sema.signal()
        }
        sema.wait()
        return recipe
    }

    /// call posix spawn to begin execute
    /// - Parameters:
    ///   - command: full path of the binary file. eg: "/bin/cat"
    ///   - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
    ///   - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
    ///   - timeout: any wall timeout if lager than 0, in seconds. eg: 6
    ///   - setPid: called sync when pid available
    ///   - stdoutBlock: a block call from pipeControlQueue in background when buffer from stdout available for read
    ///   - stderrBlock: a block call from pipeControlQueue in background when buffer from stderr available for read
    ///   - completionBlock: a block called from processControlQueue or current queue when the process is finished or an error occurred
    static func spawn(
        command: String,
        args: [String] = [],
        environment: [String: String] = [:],
        timeout: Double = 0,
        setPid: ((pid_t) -> Void)? = nil,
        stdoutBlock: ((String) -> Void)? = nil,
        stderrBlock: ((String) -> Void)? = nil,
        completionBlock: ((ExecuteRecipe) -> Void)? = nil
    ) {
        // MARK: PREPARE FILE PIPE -

        var pipestdout: [Int32] = [0, 0]
        var pipestderr: [Int32] = [0, 0]

        let bufsiz = Int(exactly: BUFSIZ) ?? 65535

        pipe(&pipestdout)
        pipe(&pipestderr)

        guard fcntl(pipestdout[0], F_SETFL, O_NONBLOCK) != -1 else {
            let recipe = ExecuteRecipe.failure(error: .openFilePipeFailed)
            completionBlock?(recipe)
            return
        }
        guard fcntl(pipestderr[0], F_SETFL, O_NONBLOCK) != -1 else {
            let recipe = ExecuteRecipe.failure(error: .openFilePipeFailed)
            completionBlock?(recipe)
            return
        }

        // MARK: PREPARE FILE ACTION -

        var fileActions: posix_spawn_file_actions_t?
        posix_spawn_file_actions_init(&fileActions)
        posix_spawn_file_actions_addclose(&fileActions, pipestdout[0])
        posix_spawn_file_actions_addclose(&fileActions, pipestderr[0])
        posix_spawn_file_actions_adddup2(&fileActions, pipestdout[1], STDOUT_FILENO)
        posix_spawn_file_actions_adddup2(&fileActions, pipestderr[1], STDERR_FILENO)
        posix_spawn_file_actions_addclose(&fileActions, pipestdout[1])
        posix_spawn_file_actions_addclose(&fileActions, pipestderr[1])

        defer {
            posix_spawn_file_actions_destroy(&fileActions)
        }

        // MARK: PREPARE ENV -

        var realEnvironmentBuilder: [String] = []
        // before building the environment, we need to read from the existing environment
        do {
            var envBuilder = [String: String]()
            var currentEnv = environ
            while let rawStr = currentEnv.pointee {
                defer { currentEnv += 1 }
                // get the env
                let str = String(cString: rawStr)
                guard let key = str.components(separatedBy: "=").first else {
                    continue
                }
                if !(str.count >= "\(key)=".count) {
                    continue
                }
                // this is to aviod any problem with mua=nya=nya= that ending with =
                let value = String(str.dropFirst("\(key)=".count))
                envBuilder[key] = value
            }
            // now, let's overwrite the environment specified in parameters
            for (key, value) in environment {
                envBuilder[key] = value
            }
            // now, package those items
            for (key, value) in envBuilder {
                realEnvironmentBuilder.append("\(key)=\(value)")
            }
        }
        // making it a c shit
        let realEnv: [UnsafeMutablePointer<CChar>?] = realEnvironmentBuilder.map { $0.withCString(strdup) }
        defer { for case let env? in realEnv { free(env) } }

        // MARK: PREPARE ARGS -

        let args = [command] + args
        let argv: [UnsafeMutablePointer<CChar>?] = args.map { $0.withCString(strdup) }
        defer { for case let arg? in argv { free(arg) } }

        // MARK: NOW POSIX_SPAWN -

        var pid: pid_t = 0
        let spawnStatus = posix_spawn(&pid, command, &fileActions, nil, argv + [nil], realEnv + [nil])
        if spawnStatus != 0 {
            let recipe = ExecuteRecipe.failure(error: .posixSpawnFailed)
            completionBlock?(recipe)
            return
        }

        setPid?(pid)

        close(pipestdout[1])
        close(pipestderr[1])

        var stdoutStr = ""
        var stderrStr = ""

        // MARK: OUTPUT BRIDGE -

        let stdoutSource = DispatchSource.makeReadSource(fileDescriptor: pipestdout[0], queue: pipeControlQueue)
        let stderrSource = DispatchSource.makeReadSource(fileDescriptor: pipestderr[0], queue: pipeControlQueue)

        let stdoutSem = DispatchSemaphore(value: 0)
        let stderrSem = DispatchSemaphore(value: 0)

        stdoutSource.setCancelHandler {
            close(pipestdout[0])
            stdoutSem.signal()
        }
        stderrSource.setCancelHandler {
            close(pipestderr[0])
            stderrSem.signal()
        }

        stdoutSource.setEventHandler {
            let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufsiz)
            defer { buffer.deallocate() }
            let bytesRead = read(pipestdout[0], buffer, bufsiz)
            guard bytesRead > 0 else {
                if bytesRead == -1, errno == EAGAIN {
                    return
                }
                stdoutSource.cancel()
                return
            }

            let array = Array(UnsafeBufferPointer(start: buffer, count: bytesRead)) + [UInt8(0)]
            array.withUnsafeBufferPointer { ptr in
                let str = String(cString: unsafeBitCast(ptr.baseAddress, to: UnsafePointer<CChar>.self))
                stdoutStr += str
                stdoutBlock?(str)
            }
        }
        stderrSource.setEventHandler {
            let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufsiz)
            defer { buffer.deallocate() }

            let bytesRead = read(pipestderr[0], buffer, bufsiz)
            guard bytesRead > 0 else {
                if bytesRead == -1, errno == EAGAIN {
                    return
                }
                stderrSource.cancel()
                return
            }

            let array = Array(UnsafeBufferPointer(start: buffer, count: bytesRead)) + [UInt8(0)]
            array.withUnsafeBufferPointer { ptr in
                let str = String(cString: unsafeBitCast(ptr.baseAddress, to: UnsafePointer<CChar>.self))
                stderrStr += str
                stderrBlock?(str)
            }
        }

        stdoutSource.resume()
        stderrSource.resume()

        // MARK: WAIT + TIMEOUT CONTROL -

        let realTimeout = timeout > 0 ? timeout : maxTimeoutValue
        let wallTimeout = DispatchTime.now() + (
            TimeInterval(exactly: realTimeout) ?? maxTimeoutValue
        )
        var status: Int32 = 0
        var wait: pid_t = 0
        var isTimeout = false

        let timerSource = DispatchSource.makeTimerSource(flags: [], queue: processControlQueue)
        timerSource.setEventHandler {
            isTimeout = true
            kill(pid, SIGKILL)
        }

        let processSource = DispatchSource.makeProcessSource(identifier: pid, eventMask: .exit, queue: processControlQueue)
        processSource.setEventHandler {
            wait = waitpid(pid, &status, 0)

            processSource.cancel()
            timerSource.cancel()

            stdoutSem.wait()
            stderrSem.wait()

            // by using exactly method, we won't crash it!
            let recipe = ExecuteRecipe(
                exitCode: Int(exactly: status) ?? -1,
                pid: Int(exactly: pid) ?? -1,
                wait: Int(exactly: wait) ?? -1,
                error: isTimeout ? .timeout : nil,
                stdout: stdoutStr,
                stderr: stderrStr
            )
            completionBlock?(recipe)
        }
        processSource.resume()

        // timeout control
        timerSource.schedule(deadline: wallTimeout)
        timerSource.resume()
    }
}


text:
//
//  AuxiliaryExecute+Spawn.swift
//  AuxiliaryExecute
//
//  Created by Cyandev on 2022/1/10.
//

#if swift(>=5.5)

    import Foundation

    @available(iOS 15.0, macOS 12.0.0, *)
    public extension AuxiliaryExecute {
        /// async/await function for spawn using withCheckedContinuation
        /// - Parameters:
        ///   - command: full path of the binary file. eg: "/bin/cat"
        ///   - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
        ///   - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
        ///   - timeout: any wall timeout if lager than 0, in seconds. eg: 6
        ///   - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read
        ///   - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read
        /// - Returns: execution recipe, see it's definition for details
        @discardableResult
        static func spawnAsync(
            command: String,
            args: [String] = [],
            environment: [String: String] = [:],
            timeout: Double = 0,
            stdoutBlock: ((String) -> Void)? = nil,
            stderrBlock: ((String) -> Void)? = nil
        ) async -> ExecuteRecipe {
            await withCheckedContinuation { cont in
                self.spawn(
                    command: command,
                    args: args,
                    environment: environment,
                    timeout: timeout,
                    stdoutBlock: stdoutBlock,
                    stderrBlock: stderrBlock
                ) { recipe in
                    cont.resume(returning: recipe)
                }
            }
        }

        /// async/await function for spawn using withCheckedContinuation
        /// - Parameters:
        ///   - command: full path of the binary file. eg: "/bin/cat"
        ///   - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
        ///   - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
        ///   - timeout: any wall timeout if lager than 0, in seconds. eg: 6
        ///   - output: a block call from pipeControlQueue in background when buffer from stdout or stderr available for read
        /// - Returns: execution recipe, see it's definition for details
        @discardableResult
        static func spawnAsync(
            command: String,
            args: [String] = [],
            environment: [String: String] = [:],
            timeout: Double = 0,
            output: ((String) -> Void)? = nil
        ) async -> ExecuteRecipe {
            let lock = NSLock()
            return await spawnAsync(
                command: command,
                args: args,
                environment: environment,
                timeout: timeout,
                stdoutBlock: { str in
                    lock.lock()
                    output?(str)
                    lock.unlock()
                }, stderrBlock: { str in
                    lock.lock()
                    output?(str)
                    lock.unlock()
                }
            )
        }
    }

#endif


text:
//
//  AuxiliaryExecute+Shell.swift
//  AuxiliaryExecute
//
//  Created by Lakr Aream on 2021/12/6.
//

import Foundation

public extension AuxiliaryExecute {
    /// Setup binary table, require lock
    internal func setupBinaryTable() {
        lock.lock()

        let environmentPath = ProcessInfo
            .processInfo
            .environment["PATH"]?
            .components(separatedBy: ":")
            .compactMap { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            ?? []

        currentPath = environmentPath
        binaryTable.removeAll()

        // now, let's search inside your path and system path
        for eachPath in environmentPath + extraSearchPath {
            if let dirElements = try? FileManager
                .default
                .contentsOfDirectory(atPath: eachPath)
            {
                for node in dirElements {
                    let itemLocation = URL(fileURLWithPath: eachPath)
                        .appendingPathComponent(node)
                    if !isBinaryValid(at: itemLocation) {
                        continue
                    }
                    binaryTable[node] = itemLocation.path
                }
            }
        }

        lock.unlock()
    }

    /// append the search path, thread safe
    /// - Parameter value: the path
    func appendSearchPath(with value: String) {
        lock.lock()
        extraSearchPath.append(value)
        lock.unlock()
    }

    /// update the customized search path, thread safe
    /// - Parameter block: update inside this block
    func updateExtraSearchPath(with block: (inout [String]) -> Void) {
        lock.lock()
        block(&extraSearchPath)
        lock.unlock()
    }

    /// update the customized binary table, thread safe
    /// - Parameter block: update inside this block
    func updateOverwriteTable(with block: (inout [String: String?]) -> Void) {
        lock.lock()
        block(&overwriteTable)
        lock.unlock()
    }

    /// return whether you telling us to not to find anything with this command in shell
    /// - Parameter command: command name
    /// - Returns: should skip search
    internal func commandShouldNotExists(command: String) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        if overwriteTable.keys.contains(command) {
            if let value = overwriteTable[command] {
                // it is a String?? :)
                return value == nil
            } else {
                // there must be something wrong
                assertionFailure("broken memory found with overwriteTable")
                return false
            }
        }
        return false
    }

    /// safely grab the full path for shell command
    /// - Parameter command: command name
    /// - Returns: full path if exists, otherwise nil
    func binaryLocationFor(command: String) -> String? {
        lock.lock()
        defer { lock.unlock() }
        // check if you overwritten the command first
        if overwriteTable.keys.contains(command) {
            if let value = overwriteTable[command] {
                // it is a String?? :)
                // you telling us to not find anything with this command
                return value
            } else {
                // there must be something wrong
                assertionFailure("broken memory found with overwriteTable")
                return nil
            }
        } else {
            return binaryTable[command]
        }
    }

    /// Indicate if a file has permission to execute, check's with permission
    /// - Parameter at: file url
    /// - Returns: able to execute or not
    private func isBinaryValid(at: URL) -> Bool {
        FileManager
            .default
            .isExecutableFile(atPath: at.path)
    }

    /// call a binary to execute
    /// - Parameters:
    ///   - command: the command's name, not full path. eg: bash
    ///   - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
    ///   - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
    ///   - timeout: any wall timeout if lager than 0, in seconds. eg: 6
    ///   - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read
    ///   - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read
    /// - Returns: execution recipe, see it's definition for details
    @discardableResult
    func shell(
        command: String,
        args: [String] = [],
        environment: [String: String] = [:],
        timeout: Double = 0,
        stdoutBlock: ((String) -> Void)? = nil,
        stderrBlock: ((String) -> Void)? = nil
    ) -> ExecuteRecipe {
        // the command with full file system path
        var commandLocation: String?
        if let location = binaryLocationFor(command: command) {
            commandLocation = location
        } else if !commandShouldNotExists(command: command) {
            // before calling to setup, check if you telling us so
            // if not so, search for the binary, for two reason:
            // - table is not yet built
            // - it may be added to the system after last setup
            setupBinaryTable()
        }
        // if nil, look for the command once again, after another setup
        if commandLocation == nil {
            commandLocation = binaryLocationFor(command: command)
        }
        // make sure we find the command
        guard let commandLocation = commandLocation else {
            return ExecuteRecipe.failure(error: .commandNotFound)
        }
        // now, let's validate the command
        guard isBinaryValid(at: URL(fileURLWithPath: commandLocation)) else {
            return ExecuteRecipe.failure(error: .commandInvalid)
        }
        // finally let‘s call the spawn
        let recipe = AuxiliaryExecute.spawn(
            command: commandLocation,
            args: args,
            environment: environment,
            timeout: timeout,
            stdoutBlock: stdoutBlock,
            stderrBlock: stderrBlock
        )
        return recipe
    }

    /// run script with bash, if bash available
    /// - Parameters:
    ///   - command: script to be passed to bash. eg: "echo nya"
    ///   - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
    ///   - timeout: any wall timeout if lager than 0, in seconds. eg: 6
    ///   - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read
    ///   - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read
    /// - Returns: execution recipe, see it's definition for details
    @discardableResult
    func bash(
        command: String,
        environment: [String: String] = [:],
        timeout: Double = 0,
        stdoutBlock: ((String) -> Void)? = nil,
        stderrBlock: ((String) -> Void)? = nil
    ) -> ExecuteRecipe {
        let result = shell(
            command: "bash",
            args: ["-c", command],
            environment: environment,
            timeout: timeout,
            stdoutBlock: stdoutBlock,
            stderrBlock: stderrBlock
        )
        return result
    }
}


text:
// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "Colorful",
    platforms: [
        .iOS(.v13),
        .tvOS(.v13),
        .macOS(.v10_15),
        .watchOS(.v6)
    ],
    products: [
        .library(name: "Colorful", targets: ["Colorful"]),
    ],
    targets: [
        .target(name: "Colorful", dependencies: []),
    ]
)


text:
# Colorful

A SwiftUI implementation of AppleCard's animated colorful blur background.

## Preview

![Preview](./Preview.png)

## Usage

```
import Colorful

var body: some View {
    ColorfulView()
}
```

## Customization & Defaults

```
init(
    animated: Bool = defaultAnimated,
    animation: Animation = defaultAnimation,
    blurRadius: CGFloat = defaultBlurRadius,
    colors: [Color] = defaultColorList,
    colorCount: Int = defaultColorCount
)
```

## License

Colorful is licensed under [MIT](./LICENSE).

---

Copyright © 2021 Lakr Aream. All Rights Reserved.


text:
//
//  Colorful.swift
//
//
//  Created by Lakr Aream on 2021/9/19.
//

import SwiftUI

private let kDefaultSourceColorList = [#colorLiteral(red: 0.9586862922, green: 0.660125792, blue: 0.8447988033, alpha: 1), #colorLiteral(red: 0.8714533448, green: 0.723166883, blue: 0.9342088699, alpha: 1), #colorLiteral(red: 0.7458761334, green: 0.7851135731, blue: 0.9899476171, alpha: 1), #colorLiteral(red: 0.4398113191, green: 0.8953480721, blue: 0.9796616435, alpha: 1), #colorLiteral(red: 0.3484552801, green: 0.933657825, blue: 0.9058339596, alpha: 1), #colorLiteral(red: 0.5567936897, green: 0.9780793786, blue: 0.6893508434, alpha: 1)]

public extension ColorfulView {
    static let defaultAnimated: Bool = true
    static let defaultBlurRadius: CGFloat = 64
    static let defaultColorCount: Int = 32

    static let defaultAnimation: Animation = Animation
        .interpolatingSpring(stiffness: 50, damping: 1)
        .speed(0.05)

    static let defaultColorList: [Color] = kDefaultSourceColorList
        .map { Color($0) }

    #if canImport(AppKit) && !targetEnvironment(macCatalyst)
        static let defaultColorListNSColor: [NSColor] = kDefaultSourceColorList
    #endif

    #if canImport(UIKit)
        static let defaultColorListUIColor: [UIColor] = kDefaultSourceColorList
    #endif
}


text:
//
//  ColorfulView.swift
//  Colorful
//
//  Created by Lakr Aream on 2021/9/19.
//

import SwiftUI

public struct ColorfulView: View {
    // MARK: - PROPERTY

    @State var randomization: [PointRandomization]
    @State var size: CGSize = .init()

    private let colorElements: [Color]
    private let animated: Bool
    private let animation: Animation
    private let blurRadius: CGFloat

    private let timer = Timer
        .publish(every: 5, on: .main, in: .common)
        .autoconnect()

    // MARK: - INIT

    public init(
        animated: Bool = defaultAnimated,
        animation: Animation = defaultAnimation,
        blurRadius: CGFloat = defaultBlurRadius,
        colors: [Color] = defaultColorList,
        colorCount: Int = defaultColorCount
    ) {
        assert(colors.count > 0)
        assert(colorCount > 0)
        assert(blurRadius > 0)

        self.animated = animated
        self.animation = animation
        self.blurRadius = blurRadius

        var colorCompiler = [Color]()
        while colorCompiler.count < colorCount {
            colorCompiler.append(contentsOf: colors.shuffled())
        }
        if colorCompiler.count > colorCount {
            colorCompiler.removeLast(colorCompiler.count - colorCount)
        }
        assert(colorCompiler.count == colorCount)
        colorElements = colorCompiler

        var builder = [PointRandomization]()
        for _ in 0 ..< colorCount {
            builder.append(.init())
        }
        _randomization = State(initialValue: builder)
    }

    #if canImport(AppKit) && !targetEnvironment(macCatalyst)
        public init(
            animated: Bool = defaultAnimated,
            animation: Animation = defaultAnimation,
            blurRadius: CGFloat = defaultBlurRadius,
            nsColors: [NSColor],
            colorCount: Int = defaultColorCount
        ) {
            self.init(
                animated: animated,
                animation: animation,
                blurRadius: blurRadius,
                colors: nsColors.map { Color($0) },
                colorCount: colorCount
            )
        }
    #endif

    #if canImport(UIKit)
        public init(
            animated: Bool = defaultAnimated,
            animation: Animation = defaultAnimation,
            blurRadius: CGFloat = defaultBlurRadius,
            uiColors: [UIColor],
            colorCount: Int = defaultColorCount
        ) {
            self.init(
                animated: animated,
                animation: animation,
                blurRadius: blurRadius,
                colors: uiColors.map { Color($0) },
                colorCount: colorCount
            )
        }
    #endif

    // MARK: - VIEW

    public var body: some View {
        GeometryReader { reader in
            ZStack {
                ForEach(obtainRangeAndUpdate(size: reader.size)) { configure in
                    Circle()
                        .foregroundColor(configure.color)
                        .opacity(0.5)
                        .frame(
                            width: configure.diameter,
                            height: configure.diameter
                        )
                        .offset(
                            x: configure.offsetX,
                            y: configure.offsetY
                        )
                }
            }
            .frame(width: reader.size.width,
                   height: reader.size.height)
        }
        .clipped()
        .blur(radius: blurRadius)
        .onReceive(timer) { _ in
            dispatchUpdate()
        }
        .onAppear {
            randomizationStart()
        }
    }

    // MARK: - FUNCTION

    private func dispatchUpdate() {
        if !animated { return }
        withAnimation(animation) {
            randomizationStart()
        }
    }

    private func randomizationStart() {
        var randomizationBuilder = [PointRandomization]()
        for i in 0 ..< randomization.count {
            let randomizationElement: PointRandomization = {
                var builder = PointRandomization()
                builder.randomizeIn(size: size)
                builder.id = randomization[i].id
                builder.color = colorElements[i]
                return builder
            }()
            randomizationBuilder.append(randomizationElement)
        }
        randomization = randomizationBuilder
    }

    private func obtainRangeAndUpdate(size: CGSize) -> [PointRandomization] {
        issueSizeUpdate(withValue: size)
        return randomization
    }

    private func issueSizeUpdate(withValue size: CGSize) {
        if self.size == size { return }
        DispatchQueue.main.async {
            self.size = size
            self.dispatchUpdate()
        }
    }
}


text:
//
//  PointRandomization.swift
//  Colorful
//
//  Created by Lakr Aream on 2021/9/19.
//

import SwiftUI

extension ColorfulView {
    struct PointRandomization: Equatable, Hashable, Identifiable {
        var id = UUID()

        var diameter: CGFloat = 0
        var offsetX: CGFloat = 0
        var offsetY: CGFloat = 0

        var color: Color = .white.opacity(0)

        mutating func randomizeIn(size: CGSize) {
            let decision = (size.width + size.height) / 4
            diameter = CGFloat.random(in: (decision * 0.25) ... (decision * 0.75))
            offsetX = CGFloat.random(in: -(size.width / 2) ... +(size.width / 2))
            offsetY = CGFloat.random(in: -(size.height / 2) ... +(size.height / 2))
        }

        func hash(into hasher: inout Hasher) {
            hasher.combine(diameter)
            hasher.combine(offsetX)
            hasher.combine(offsetY)
        }

        static func == (lhs: PointRandomization, rhs: PointRandomization) -> Bool {
            lhs.diameter == rhs.diameter &&
                lhs.offsetX == rhs.offsetX &&
                lhs.offsetY == rhs.offsetY
        }
    }
}


text:
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:App/Axchange/Axchange.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:External/AuxiliaryExecute">
   </FileRef>
   <FileRef
      location = "group:External/Colorful">
   </FileRef>
</Workspace>


text:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


text:
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 55;
	objects = {

/* Begin PBXBuildFile section */
		50059A692891BFE2003D0CD4 /* DeviceFileView+Exec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50059A682891BFE2003D0CD4 /* DeviceFileView+Exec.swift */; };
		50059A6C2891C010003D0CD4 /* DeviceFileView+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50059A6B2891C010003D0CD4 /* DeviceFileView+Helper.swift */; };
		504711DF289023AE005BD174 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504711DE289023AE005BD174 /* App.swift */; };
		504711E1289023AE005BD174 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504711E0289023AE005BD174 /* MainView.swift */; };
		504711E3289023AE005BD174 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504711E2289023AE005BD174 /* Assets.xcassets */; };
		504711EE289023C9005BD174 /* Auxiliary in Resources */ = {isa = PBXBuildFile; fileRef = 504711ED289023C9005BD174 /* Auxiliary */; };
		504711F32890242E005BD174 /* AuxiliaryExecute in Frameworks */ = {isa = PBXBuildFile; productRef = 504711F22890242E005BD174 /* AuxiliaryExecute */; };
		504711F52890242E005BD174 /* Colorful in Frameworks */ = {isa = PBXBuildFile; productRef = 504711F42890242E005BD174 /* Colorful */; };
		504711F828902603005BD174 /* App+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504711F728902603005BD174 /* App+Setup.swift */; };
		504711FB28902680005BD174 /* Executor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504711FA28902680005BD174 /* Executor.swift */; };
		504711FE2890287F005BD174 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504711FD2890287F005BD174 /* SidebarView.swift */; };
		5047120028902886005BD174 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504711FF28902886005BD174 /* WelcomeView.swift */; };
		50471203289028C2005BD174 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50471202289028C2005BD174 /* View.swift */; };
		5047120628902B1E005BD174 /* AppStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5047120528902B1E005BD174 /* AppStatus.swift */; };
		50471208289030A3005BD174 /* Executor+ADB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50471207289030A3005BD174 /* Executor+ADB.swift */; };
		5047120A28903110005BD174 /* DispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5047120928903110005BD174 /* DispatchQueue.swift */; };
		5047120F2890322A005BD174 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5047120E2890322A005BD174 /* Device.swift */; };
		50B8FD7C28927A0800B3BC33 /* DeviceFileView+LogElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8FD7B28927A0800B3BC33 /* DeviceFileView+LogElement.swift */; };
		50EB3BA42890FCF100CB1BDA /* Device+Exec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB3BA32890FCF100CB1BDA /* Device+Exec.swift */; };
		50EB3BA62890FEDF00CB1BDA /* Device+Prop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB3BA52890FEDF00CB1BDA /* Device+Prop.swift */; };
		50EB3BA828911AC900CB1BDA /* DeviceFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB3BA728911AC900CB1BDA /* DeviceFileView.swift */; };
		50EB3BAA2891218D00CB1BDA /* UnauthorizedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB3BA92891218D00CB1BDA /* UnauthorizedView.swift */; };
		50EB3BAC28912EC400CB1BDA /* Device+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB3BAB28912EC400CB1BDA /* Device+File.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
		50059A682891BFE2003D0CD4 /* DeviceFileView+Exec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeviceFileView+Exec.swift"; sourceTree = "<group>"; };
		50059A6B2891C010003D0CD4 /* DeviceFileView+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeviceFileView+Helper.swift"; sourceTree = "<group>"; };
		504711DB289023AE005BD174 /* Axchange.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Axchange.app; sourceTree = BUILT_PRODUCTS_DIR; };
		504711DE289023AE005BD174 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
		504711E0289023AE005BD174 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
		504711E2289023AE005BD174 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		504711E7289023AE005BD174 /* Axchange.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Axchange.entitlements; sourceTree = "<group>"; };
		504711ED289023C9005BD174 /* Auxiliary */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Auxiliary; sourceTree = "<group>"; };
		504711F728902603005BD174 /* App+Setup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+Setup.swift"; sourceTree = "<group>"; };
		504711FA28902680005BD174 /* Executor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Executor.swift; sourceTree = "<group>"; };
		504711FD2890287F005BD174 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
		504711FF28902886005BD174 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
		50471202289028C2005BD174 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
		5047120528902B1E005BD174 /* AppStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStatus.swift; sourceTree = "<group>"; };
		50471207289030A3005BD174 /* Executor+ADB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Executor+ADB.swift"; sourceTree = "<group>"; };
		5047120928903110005BD174 /* DispatchQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DispatchQueue.swift; sourceTree = "<group>"; };
		5047120E2890322A005BD174 /* Device.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = "<group>"; };
		50B8FD7B28927A0800B3BC33 /* DeviceFileView+LogElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeviceFileView+LogElement.swift"; sourceTree = "<group>"; };
		50EB3BA32890FCF100CB1BDA /* Device+Exec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Device+Exec.swift"; sourceTree = "<group>"; };
		50EB3BA52890FEDF00CB1BDA /* Device+Prop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Device+Prop.swift"; sourceTree = "<group>"; };
		50EB3BA728911AC900CB1BDA /* DeviceFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceFileView.swift; sourceTree = "<group>"; };
		50EB3BA92891218D00CB1BDA /* UnauthorizedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnauthorizedView.swift; sourceTree = "<group>"; };
		50EB3BAB28912EC400CB1BDA /* Device+File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Device+File.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		504711D8289023AE005BD174 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				504711F52890242E005BD174 /* Colorful in Frameworks */,
				504711F32890242E005BD174 /* AuxiliaryExecute in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		50059A6A2891BFF8003D0CD4 /* DeviceFileView */ = {
			isa = PBXGroup;
			children = (
				50EB3BA728911AC900CB1BDA /* DeviceFileView.swift */,
				50059A682891BFE2003D0CD4 /* DeviceFileView+Exec.swift */,
				50059A6B2891C010003D0CD4 /* DeviceFileView+Helper.swift */,
				50B8FD7B28927A0800B3BC33 /* DeviceFileView+LogElement.swift */,
			);
			path = DeviceFileView;
			sourceTree = "<group>";
		};
		504711D2289023AE005BD174 = {
			isa = PBXGroup;
			children = (
				504711DD289023AE005BD174 /* Axchange */,
				504711DC289023AE005BD174 /* Products */,
				504711F12890242E005BD174 /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		504711DC289023AE005BD174 /* Products */ = {
			isa = PBXGroup;
			children = (
				504711DB289023AE005BD174 /* Axchange.app */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		504711DD289023AE005BD174 /* Axchange */ = {
			isa = PBXGroup;
			children = (
				504711EF289023DB005BD174 /* App */,
				504711ED289023C9005BD174 /* Auxiliary */,
				504711F928902674005BD174 /* Backend */,
				504711F0289023E6005BD174 /* Interface */,
				50471201289028B5005BD174 /* Extension */,
			);
			path = Axchange;
			sourceTree = "<group>";
		};
		504711EF289023DB005BD174 /* App */ = {
			isa = PBXGroup;
			children = (
				504711DE289023AE005BD174 /* App.swift */,
				504711F728902603005BD174 /* App+Setup.swift */,
				504711E2289023AE005BD174 /* Assets.xcassets */,
				504711E7289023AE005BD174 /* Axchange.entitlements */,
			);
			path = App;
			sourceTree = "<group>";
		};
		504711F0289023E6005BD174 /* Interface */ = {
			isa = PBXGroup;
			children = (
				504711E0289023AE005BD174 /* MainView.swift */,
				504711FD2890287F005BD174 /* SidebarView.swift */,
				504711FF28902886005BD174 /* WelcomeView.swift */,
				50EB3BA92891218D00CB1BDA /* UnauthorizedView.swift */,
				50059A6A2891BFF8003D0CD4 /* DeviceFileView */,
			);
			path = Interface;
			sourceTree = "<group>";
		};
		504711F12890242E005BD174 /* Frameworks */ = {
			isa = PBXGroup;
			children = (
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
		504711F928902674005BD174 /* Backend */ = {
			isa = PBXGroup;
			children = (
				5047120428902B18005BD174 /* AppStatus */,
				5047120D2890321D005BD174 /* Device */,
				504711FC28902683005BD174 /* Executor */,
			);
			path = Backend;
			sourceTree = "<group>";
		};
		504711FC28902683005BD174 /* Executor */ = {
			isa = PBXGroup;
			children = (
				504711FA28902680005BD174 /* Executor.swift */,
				50471207289030A3005BD174 /* Executor+ADB.swift */,
			);
			path = Executor;
			sourceTree = "<group>";
		};
		50471201289028B5005BD174 /* Extension */ = {
			isa = PBXGroup;
			children = (
				50471202289028C2005BD174 /* View.swift */,
				5047120928903110005BD174 /* DispatchQueue.swift */,
			);
			path = Extension;
			sourceTree = "<group>";
		};
		5047120428902B18005BD174 /* AppStatus */ = {
			isa = PBXGroup;
			children = (
				5047120528902B1E005BD174 /* AppStatus.swift */,
			);
			path = AppStatus;
			sourceTree = "<group>";
		};
		5047120D2890321D005BD174 /* Device */ = {
			isa = PBXGroup;
			children = (
				5047120E2890322A005BD174 /* Device.swift */,
				50EB3BA32890FCF100CB1BDA /* Device+Exec.swift */,
				50EB3BAB28912EC400CB1BDA /* Device+File.swift */,
				50EB3BA52890FEDF00CB1BDA /* Device+Prop.swift */,
			);
			path = Device;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		504711DA289023AE005BD174 /* Axchange */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 504711EA289023AE005BD174 /* Build configuration list for PBXNativeTarget "Axchange" */;
			buildPhases = (
				504711F62890259E005BD174 /* Swift Format */,
				504711D7289023AE005BD174 /* Sources */,
				504711D8289023AE005BD174 /* Frameworks */,
				504711D9289023AE005BD174 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = Axchange;
			packageProductDependencies = (
				504711F22890242E005BD174 /* AuxiliaryExecute */,
				504711F42890242E005BD174 /* Colorful */,
			);
			productName = Axchange;
			productReference = 504711DB289023AE005BD174 /* Axchange.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		504711D3289023AE005BD174 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				BuildIndependentTargetsInParallel = 1;
				LastSwiftUpdateCheck = 1340;
				LastUpgradeCheck = 1340;
				TargetAttributes = {
					504711DA289023AE005BD174 = {
						CreatedOnToolsVersion = 13.4.1;
					};
				};
			};
			buildConfigurationList = 504711D6289023AE005BD174 /* Build configuration list for PBXProject "Axchange" */;
			compatibilityVersion = "Xcode 13.0";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 504711D2289023AE005BD174;
			productRefGroup = 504711DC289023AE005BD174 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				504711DA289023AE005BD174 /* Axchange */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		504711D9289023AE005BD174 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				504711E3289023AE005BD174 /* Assets.xcassets in Resources */,
				504711EE289023C9005BD174 /* Auxiliary in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		504711F62890259E005BD174 /* Swift Format */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
			);
			name = "Swift Format";
			outputFileListPaths = (
			);
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "cd $SOURCE_ROOT\npwd\n\nif ! command -v \"swiftformat\" &> /dev/null\nthen\n    echo \"swiftformat could not be found, skipping\"\n    exit 0\nfi\n\nswiftformat . --swiftversion 5.6\n";
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		504711D7289023AE005BD174 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				50EB3BA42890FCF100CB1BDA /* Device+Exec.swift in Sources */,
				504711FB28902680005BD174 /* Executor.swift in Sources */,
				5047120028902886005BD174 /* WelcomeView.swift in Sources */,
				504711F828902603005BD174 /* App+Setup.swift in Sources */,
				50059A6C2891C010003D0CD4 /* DeviceFileView+Helper.swift in Sources */,
				5047120F2890322A005BD174 /* Device.swift in Sources */,
				5047120A28903110005BD174 /* DispatchQueue.swift in Sources */,
				504711FE2890287F005BD174 /* SidebarView.swift in Sources */,
				50B8FD7C28927A0800B3BC33 /* DeviceFileView+LogElement.swift in Sources */,
				50EB3BAC28912EC400CB1BDA /* Device+File.swift in Sources */,
				50059A692891BFE2003D0CD4 /* DeviceFileView+Exec.swift in Sources */,
				504711E1289023AE005BD174 /* MainView.swift in Sources */,
				50EB3BA62890FEDF00CB1BDA /* Device+Prop.swift in Sources */,
				50471203289028C2005BD174 /* View.swift in Sources */,
				50EB3BA828911AC900CB1BDA /* DeviceFileView.swift in Sources */,
				50471208289030A3005BD174 /* Executor+ADB.swift in Sources */,
				504711DF289023AE005BD174 /* App.swift in Sources */,
				50EB3BAA2891218D00CB1BDA /* UnauthorizedView.swift in Sources */,
				5047120628902B1E005BD174 /* AppStatus.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
		504711E8289023AE005BD174 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MACOSX_DEPLOYMENT_TARGET = 12.3;
				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
				MTL_FAST_MATH = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = macosx;
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
			};
			name = Debug;
		};
		504711E9289023AE005BD174 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MACOSX_DEPLOYMENT_TARGET = 12.3;
				MTL_ENABLE_DEBUG_INFO = NO;
				MTL_FAST_MATH = YES;
				SDKROOT = macosx;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
			};
			name = Release;
		};
		504711EB289023AE005BD174 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
				CODE_SIGN_ENTITLEMENTS = Axchange/Axchange.entitlements;
				CODE_SIGN_STYLE = Automatic;
				COMBINE_HIDPI_IMAGES = YES;
				CURRENT_PROJECT_VERSION = 5;
				DEVELOPMENT_ASSET_PATHS = "";
				ENABLE_HARDENED_RUNTIME = YES;
				ENABLE_PREVIEWS = YES;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
				INFOPLIST_KEY_NSHumanReadableCopyright = "";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 12.0;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = wiki.qaq.Axchange;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_EMIT_LOC_STRINGS = YES;
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		504711EC289023AE005BD174 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
				CODE_SIGN_ENTITLEMENTS = Axchange/Axchange.entitlements;
				CODE_SIGN_STYLE = Automatic;
				COMBINE_HIDPI_IMAGES = YES;
				CURRENT_PROJECT_VERSION = 5;
				DEVELOPMENT_ASSET_PATHS = "";
				ENABLE_HARDENED_RUNTIME = YES;
				ENABLE_PREVIEWS = YES;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
				INFOPLIST_KEY_NSHumanReadableCopyright = "";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 12.0;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = wiki.qaq.Axchange;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_EMIT_LOC_STRINGS = YES;
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		504711D6289023AE005BD174 /* Build configuration list for PBXProject "Axchange" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				504711E8289023AE005BD174 /* Debug */,
				504711E9289023AE005BD174 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		504711EA289023AE005BD174 /* Build configuration list for PBXNativeTarget "Axchange" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				504711EB289023AE005BD174 /* Debug */,
				504711EC289023AE005BD174 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */

/* Begin XCSwiftPackageProductDependency section */
		504711F22890242E005BD174 /* AuxiliaryExecute */ = {
			isa = XCSwiftPackageProductDependency;
			productName = AuxiliaryExecute;
		};
		504711F42890242E005BD174 /* Colorful */ = {
			isa = XCSwiftPackageProductDependency;
			productName = Colorful;
		};
/* End XCSwiftPackageProductDependency section */
	};
	rootObject = 504711D3289023AE005BD174 /* Project object */;
}


text:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>


text:
//
//  SidebarView.swift
//  Axchange
//
//  Created by Lakr Aream on 2022/7/26.
//

import SwiftUI

#if DEBUG
    private let stubNavigationTarget: some View = Text("Hello World")
        .usePreferredContentSize()
#endif

struct SidebarView: View {
    @ObservedObject var appStatus = AppStatus.shared

    var progressIndicator: some View {
        HStack(spacing: 4) {
            Rectangle()
                .foregroundColor(.clear)
                .frame(width: 18, height: 18)
                .overlay(
                    ProgressView()
                        .progressViewStyle(.circular)
                        .scaleEffect(0.4)
                )
                .frame(width: 18, height: 18)
            Text("Scanning...")
                .font(.system(.headline, design: .rounded))
                .multilineTextAlignment(.leading)
            Spacer()
        }
    }

    var body: some View {
        List {
            NavigationLink {
                WelcomeView()
            } label: {
                Label("Welcome", systemImage: "sun.min.fill")
            }

            Section("Devices") {
                if appStatus.devices.isEmpty {
                    Button {} label: {
                        HStack {
                            Label("No Device", systemImage: "app.dashed")
                            Spacer()
                        }
                        .background(Color.accentColor.opacity(0.0001))
                    }
                    .buttonStyle(PlainButtonStyle())
                } else {
                    ForEach(appStatus.devices) { device in
                        NavigationLink {
                            DeviceFileView(device: device)
                        } label: {
                            Label(device.interfaceName, systemImage: "flipphone")
                        }
                    }
                }
            }
            Section("Misc") {
                Button {
                    Executor.shared.scanForDevices()
                } label: {
                    HStack {
                        Label("Scan", systemImage: "cable.connector.horizontal")
                        Spacer()
                    }
                    .background(Color.accentColor.opacity(0.0001))
                }
                .buttonStyle(PlainButtonStyle())
                Button {
                    Executor.resetADB()
                    AppStatus.shared.devices = []
                } label: {
                    HStack {
                        Label("Reset", systemImage: "drop.triangle")
                        Spacer()
                    }
                    .background(Color.accentColor.opacity(0.0001))
                }
                .buttonStyle(PlainButtonStyle())
            }
            .disabled(appStatus.isScanningDevices)
        }
        .listStyle(SidebarListStyle())
        .overlay(
            progressIndicator
                .frame(
                    maxWidth: .infinity,
                    maxHeight: .infinity,
                    alignment: .bottomLeading
                )
                .padding()
                .opacity(appStatus.isScanningDevices ? 1 : 0)
                .animation(.interactiveSpring(), value: appStatus.isScanningDevices)
        )
    }
}


text:
//
//  UnauthorizedView.swift
//  Axchange
//
//  Created by Lakr Aream on 2022/7/27.
//

import Colorful
import SwiftUI

struct UnauthorizedView: View {
    let device: Device

    var errorIcon: some View {
        ZStack {
            Image(systemName: "person.crop.circle.badge.xmark")
                .font(.system(size: 28, weight: .semibold, design: .rounded))
                .foregroundColor(.pink)
        }
        .frame(width: 60, height: 60, alignment: .center)
    }

    var body: some View {
        ZStack {
            ColorfulView(colors: [.red], colorCount: 4)
                .ignoresSafeArea()
            VStack {
                errorIcon
                Text("Device \(device.adbIdentifier) is unauthorized")
                    .font(.system(.headline, design: .rounded))
            }
            VStack {
                Spacer()
                Text("Open your phone to authorize connect request.")
                    .font(.system(.footnote, design: .rounded))
                    .opacity(0.5)
            }
            .padding()
        }
        .navigationTitle("Permission Denied")
    }
}


text:
//
//  ContentView.swift
//  Axchange
//
//  Created by Lakr Aream on 2022/7/26.
//

import SwiftUI

struct MainView: View {
    var body: some View {
        NavigationView {
            SidebarView()
            WelcomeView()
        }
        .navigationTitle("Axchange")
        .toolbar {
            ToolbarItem(placement: .navigation) {
                Button {
                    NSApp.keyWindow?.firstResponder?.tryToPerform(
                        #selector(NSSplitViewController.toggleSidebar(_:)),
                        with: nil
                    )
                } label: {
                    Label("Toggle Sidebar", systemImage: "sidebar.leading")
                }
            }
        }
    }
}


text:
//
//  WelcomeView.swift
//  Axchange
//
//  Created by Lakr Aream on 2022/7/26.
//

import Colorful
import SwiftUI

struct WelcomeView: View {
    var version: String {
        var ret = "Version: " +
            (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown")
            + " Build: " +
            (Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown")
            + " "
            + Executor.version
        #if DEBUG
            ret = "👾 \(ret) 👾"
        #endif
        return ret
    }

    var body: some View {
        ZStack {
            ColorfulView(colors: [.accentColor], colorCount: 4)
                .ignoresSafeArea()
            VStack(spacing: 4) {
                Image("Avatar")
                    .antialiased(true)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 128, height: 128)

                Spacer().frame(height: 16)

                Text("Welcome to Axchange")
                    .font(.system(.headline, design: .rounded))

                Spacer().frame(height: 24)
            }

            VStack {
                Spacer()
                Text(version)
                    .font(.system(size: 10, weight: .semibold, design: .rounded))
                    .opacity(0.5)
            }
        }
        .padding()
        .navigationTitle("Axchange")
        .usePreferredContentSize()
    }
}


text:
//
//  DeviceFileView+Exec.swift
//  Axchange
//
//  Created by Lakr Aream on 2022/7/28.
//

import AppKit
import SwiftUI

extension DeviceFileView {
    func updateDataSource() {
        let path = sourcePath
        UserDefaults.standard.set(path.path, forKey: device.adbIdentifier)
        updateQueue.async {
            DispatchQueue.withMainAndWait {
                self.isLoading = true
                self.dataSource = []
            }
            defer {
                DispatchQueue.withMainAndWait {
                    self.isLoading = false
                }
            }
            device.requestUpdateDeviceAuthorizeStatus()
            let list = device.listDir(withUrl: path)
            DispatchQueue.withMainAndWait {
                dataSource = list
            }
        }
    }

    func renameItem(withName: String, newName: String) {
        let origPath = sourcePath.appendingPathComponent(withName)
        let newPath = sourcePath.appendingPathComponent(newName)
        device.moveFile(atPath: origPath, toPath: newPath)
        updateDataSource()
    }

    func createFolder() {
        let msg = NSAlert()
        msg.addButton(withTitle: "Confirm")
        msg.addButton(withTitle: "Cancel")
        msg.messageText = "Create New Foler"

        let txt = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
        txt.stringValue = "New Foler"
        msg.window.initialFirstResponder = txt
        msg.accessoryView = txt

        guard let window = NSApp.keyWindow else {
            return
        }
        msg.beginSheetModal(for: window) { resp in
            let text = txt.stringValue
            guard resp == .alertFirstButtonReturn,
                  !text.isEmpty,
                  !text.contains("\"")
            else {
                return
            }
            device.createFolder(atPath: sourcePath.appendingPathComponent(text))
            updateDataSource()
        }
    }

    func deleteFiles(names: [String]) {
        device.deleteFiles(atPaths: names.map { sourcePath.appendingPathComponent($0) })
        updateDataSource()
    }

    func uploadFiles(atUrl: [URL]) {
        DispatchQueue.global().async {
            DispatchQueue.withMainAndWait {
                operationProgress = Progress()
                operationProgressHint = "Preparing Upload"
            }
            device.pushFiles(atPaths: atUrl, toDir: sourcePath, setPid: { pid in
                DispatchQueue.withMainAndWait {
                    self.operationProcessPid = pid
                }
            }) { url, progress in
                DispatchQueue.main.async {
                    operationProgress = progress
                    operationProgressHint = "Sending \(url.path)"
                }
            }
            DispatchQueue.withMainAndWait {
                operationProgress = nil
                operationProgressHint = nil
                operationProcessPid = nil
                updateDataSource()
            }
        }
    }

    func downloadSelection(toUrl: URL) {
        DispatchQueue.global().async {
            let paths = selection
                .map(\.name)
                .map { sourcePath.appendingPathComponent($0) }
            DispatchQueue.withMainAndWait {
                operationProgress = Progress()
                operationProgressHint = "Preparing Download"
            }
            device.downloadFiles(atPaths: paths, toDest: toUrl, setPid: { pid in
                DispatchQueue.withMainAndWait {
                    self.operationProcessPid = pid
                }
            }) { url, progress in
                DispatchQueue.withMainAndWait {
                    operationProgress = progress
                    operationProgressHint = "Downloading \(url.path)"
                }
            }
            DispatchQueue.withMainAndWait {
                operationProgress = nil
                operationProgressHint = nil
                operationProcessPid = nil
            }
        }
    }
}


text:
//
//  DeviceFileView+LogElement.swift
//  Axchange
//
//  Created by Lakr Aream on 2022/7/28.
//

import SwiftUI

extension DeviceFileView {
    struct LogElement: View {
        let record: Device.DeviceExecLog

        var body: some View {
            VStack(alignment: .leading, spacing: 0) {
                ScrollView(.horizontal, showsIndicators: false) {
                    Button {
                        NSPasteboard.general.prepareForNewContents()
                        NSPasteboard.general.setString(record.command, forType: .string)
                    } label: {
                        Text(record.command)
                            .foregroundColor(.accentColor)
                    }
                    .buttonStyle(.plain)
                }
                if !record.recipt.stdout.isEmpty {
                    Button {
                        NSPasteboard.general.prepareForNewContents()
                        NSPasteboard.general.setString(record.recipt.stdout, forType: .string)
                    } label: {
                        Text(record.recipt.stdout.trimmingCharacters(in: .whitespacesAndNewlines))
                            .lineLimit(5)
                            .frame(maxWidth: .infinity, alignment: .leading)
                    }
                    .buttonStyle(.plain)
                }
                if !record.recipt.stderr.isEmpty {
                    Button {
                        NSPasteboard.general.prepareForNewContents()
                        NSPasteboard.general.setString(record.recipt.stderr, forType: .string)
                    } label: {
                        Text(record.recipt.stderr.trimmingCharacters(in: .whitespacesAndNewlines))
                            .lineLimit(5)
                            .foregroundColor(.red)
                            .frame(maxWidth: .infinity, alignment: .leading)
                    }
                    .buttonStyle(.plain)
                }
            }
            .multilineTextAlignment(.leading)
            .frame(maxWidth: .infinity, alignment: .leading)
            .font(.system(.footnote, design: .monospaced))
        }
    }
}