<p align="left">
	<a href="http://tesseract.one/">
		<img alt="Tesseract" src ="./.github/logo-horiz.svg" height=128/>
	</a>
</p>

# Tesseract on iOS for Wallet developers

Integration of Tesseract into your wallet allows any dApp to request your wallet to sign a transaction. Currently Tesseract enables native dApps to integrate with wallets through IPC, which from the user perspective is just a modal screen from the wallet.

## Getting started

Getting **Tesseract** to work in iOS wallet is different from everywhere else only by the transports set it supports.

Currently we provide IPC transport, which allows the wallets to present their screens on top of iOS applications on request and sign the transactions.

## Installation

### Add Extension target

Add new Action Extension target to the your Wallet project. This will be your Wallet interface for the dApps.

### Add Tesseract.swift dependency

1. Add [Tesseract.swift](https://github.com/tesseract-one/Tesseract.swift) repository dependency through `File` -> `Add Package` menu in Xcode.
2. Add `TesseractService` framework dependency to your Extension target.

### Add supported network protocols to your targets

* Edit Extension target attributes in its `Info.plist` and add supported ptorocols (in this example `test` and `substrate-v1` protocol.)
```xml
<?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>NSExtension</key>
	<dict>
		<key>NSExtensionAttributes</key>
		<dict>
			<key>NSExtensionServiceRoleType</key>
			<string>NSExtensionServiceRoleTypeEditor</string>
			<key>NSExtensionActivationRule</key>
			<string>
				SUBQUERY(
					extensionItems, 
					$item,
					SUBQUERY(
						$item.attachments,
						$att,
						ANY $att.registeredTypeIdentifiers UTI-CONFORMS-TO "one.tesseract.test"
						OR ANY $att.registeredTypeIdentifiers UTI-CONFORMS-TO "one.tesseract.substrate-v1"
					).@count == $item.attachments.@count
				).@count == 1
			</string>
			<key>NSExtensionServiceAllowsFinderPreviewItem</key>
			<true/>
			<key>NSExtensionServiceAllowsTouchBarItem</key>
			<true/>
			<key>NSExtensionServiceFinderPreviewIconName</key>
			<string>NSActionTemplate</string>
			<key>NSExtensionServiceTouchBarBezelColorName</key>
			<string>TouchBarBezel</string>
			<key>NSExtensionServiceTouchBarIconName</key>
			<string>NSActionTemplate</string>
		</dict>
		<key>NSExtensionMainStoryboard</key>
		<string>MainInterface</string>
		<key>NSExtensionPointIdentifier</key>
		<string>com.apple.ui-services</string>
	</dict>
</dict>
</plist>
```
* Add URL schemes to your `Info.plist` of the Wallet target
```xml
<?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>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLName</key>
			<string>one.tesseract</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>tesseract+test</string>
				<string>tesseract+substrate-v1</string>
			</array>
		</dict>
	</array>
</dict>
</plist>
```

## Services

Through Tesseract, wallets serve the dApps by providing services accessible from the outside. A service implementation is responsible for understanding requests, providing the user with confirmation UI and replying back.

To make Tesseract work in your wallet, you need to describe, how exactly the wallet wants to react when a dApp needs something.

In Tesseract this is done via services. One service per blockchain protocol. The way the wallet signs transactions i.e. for Subsrate and for Ethereum is very different, thus every service has its own API to implement.

Let's take a look at the `TestService` implementation example from [Examples](./Examples/Swift/Extension/TestSigningService.swift).:

```swift
import TesseractService

protocol TestSigningServiceDelegate: AnyObject {
    func acceptTx(tx: String) async throws -> Bool
}

class TestSigningService: TestService {
    var signature: String
    weak var delegate: TestSigningServiceDelegate?
    
    init(delegate: TestSigningServiceDelegate, signature: String) {
        self.delegate = delegate
        self.signature = signature
    }
    
    func signTransation(req: String) async throws -> String {
        guard let delegate = self.delegate else {
            throw TesseractError.null(TestSigningServiceDelegate.self)
        }
        guard try await delegate.acceptTx(tx: req) else {
            throw TesseractError.cancelled
        }
        return req + signature
    }
}
```

The wallet is responsible to present the user with the relevant UI and reply with a response in case the user agrees to proced. Otherwise just throw `TesseractError.cancelled`. Or any other error if, in example, the request data is malformed.

## Transport

For now Tesseract supports only iOS IPC transport which called `IPCTransportIOS`.

Transport should be created and provided to the `Tesseract` instance. See example bellow.

Full documentation on Transports development can be found here: [Transports How To](./TRANSPORTS.MD)

## Initialization

Here is a typical Tesseract initialization snippet (the example is taken from [Example](./Examples/Swift/Extension/ActionViewController.swift)):

`ActionViewController` is a root ViewController provided in Extension `Info.plist` file.

```swift
import TesseractService

class ActionViewController: UIViewController, TestSigningServiceDelegate {
    var tesseract: Tesseract!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let data = WalletData()
        let service = TestSigningService(delegate: self, signature: data.signature)
        
        self.tesseract = try! Tesseract()
            .transport(IPCTransportIOS(self))
            .service(service)
            // add all supported services through .service() calls
    }

    @MainActor
    func acceptTx(tx: String) async throws -> Bool {
        // Show UI
    }
}
```

## Conclusion

We tried our best to present an API as easy for the wallet developer as we could and handled all the edge cases we know of inside the library. At least we improved it to the point that it satisfied us while building the [dev-wallet.swift](https://github.com/tesseract-one/dev-wallet.swift).

If you have any suggestions, please, create an issue or submit a PR.

Thanks!