Showing preview only (291K chars total). Download the full file or copy to clipboard to get everything.
Repository: calleluks/Tofu
Branch: main
Commit: b993d4674955
Files: 163
Total size: 247.3 KB
Directory structure:
gitextract_iqplvj7o/
├── .github/
│ └── ISSUE_TEMPLATE/
│ └── issuer-icon-request.md
├── .gitignore
├── AppIcon.sketch
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── GenerateIssuerIconAssets.sh
├── LICENSE
├── README.md
├── Tofu/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ ├── 17thShard.imageset/
│ │ │ └── Contents.json
│ │ ├── AWS.imageset/
│ │ │ └── Contents.json
│ │ ├── Adobe.imageset/
│ │ │ └── Contents.json
│ │ ├── Allegro.imageset/
│ │ │ └── Contents.json
│ │ ├── Amazon.imageset/
│ │ │ └── Contents.json
│ │ ├── AnonAddy.imageset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Atlassian.imageset/
│ │ │ └── Contents.json
│ │ ├── Backblaze.imageset/
│ │ │ └── Contents.json
│ │ ├── Basecamp.imageset/
│ │ │ └── Contents.json
│ │ ├── Binance.imageset/
│ │ │ └── Contents.json
│ │ ├── BitBay.imageset/
│ │ │ └── Contents.json
│ │ ├── Bitbucket.imageset/
│ │ │ └── Contents.json
│ │ ├── Bitstamp.imageset/
│ │ │ └── Contents.json
│ │ ├── Bittrex.imageset/
│ │ │ └── Contents.json
│ │ ├── Bitwarden.imageset/
│ │ │ └── Contents.json
│ │ ├── CircularProgressViewBorderThick.imageset/
│ │ │ └── Contents.json
│ │ ├── CircularProgressViewBorderThin.imageset/
│ │ │ └── Contents.json
│ │ ├── Cloudflare.imageset/
│ │ │ └── Contents.json
│ │ ├── Coinbase.imageset/
│ │ │ └── Contents.json
│ │ ├── Contentful.imageset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── CorporateTrust.imageset/
│ │ │ └── Contents.json
│ │ ├── CyDIS.imageset/
│ │ │ └── Contents.json
│ │ ├── DNSimple.imageset/
│ │ │ └── Contents.json
│ │ ├── DigitalOcean.imageset/
│ │ │ └── Contents.json
│ │ ├── Discord.imageset/
│ │ │ └── Contents.json
│ │ ├── Docker.imageset/
│ │ │ └── Contents.json
│ │ ├── Dropbox.imageset/
│ │ │ └── Contents.json
│ │ ├── ElectronicArts.imageset/
│ │ │ └── Contents.json
│ │ ├── EpicGames.imageset/
│ │ │ └── Contents.json
│ │ ├── Evernote.imageset/
│ │ │ └── Contents.json
│ │ ├── Facebook.imageset/
│ │ │ └── Contents.json
│ │ ├── FastMail.imageset/
│ │ │ └── Contents.json
│ │ ├── Fidelity.imageset/
│ │ │ └── Contents.json
│ │ ├── Figma.imageset/
│ │ │ └── Contents.json
│ │ ├── Firefox.imageset/
│ │ │ └── Contents.json
│ │ ├── Gandi.imageset/
│ │ │ └── Contents.json
│ │ ├── GitHub.imageset/
│ │ │ └── Contents.json
│ │ ├── GitLab.imageset/
│ │ │ └── Contents.json
│ │ ├── Gitea.imageset/
│ │ │ └── Contents.json
│ │ ├── GoDaddy.imageset/
│ │ │ └── Contents.json
│ │ ├── Google.imageset/
│ │ │ └── Contents.json
│ │ ├── GreenAddress.imageset/
│ │ │ └── Contents.json
│ │ ├── HEY.imageset/
│ │ │ └── Contents.json
│ │ ├── HackTheBox.imageset/
│ │ │ └── Contents.json
│ │ ├── Heroku.imageset/
│ │ │ └── Contents.json
│ │ ├── Hetzner.imageset/
│ │ │ └── Contents.json
│ │ ├── HomeAssistant.imageset/
│ │ │ └── Contents.json
│ │ ├── Honeybadger.imageset/
│ │ │ └── Contents.json
│ │ ├── Hostek.imageset/
│ │ │ └── Contents.json
│ │ ├── Hover.imageset/
│ │ │ └── Contents.json
│ │ ├── HumbleBundle.imageset/
│ │ │ └── Contents.json
│ │ ├── IDme.imageset/
│ │ │ └── Contents.json
│ │ ├── IFTTT.imageset/
│ │ │ └── Contents.json
│ │ ├── Instagram.imageset/
│ │ │ └── Contents.json
│ │ ├── Intercom.imageset/
│ │ │ └── Contents.json
│ │ ├── IssuerIcons/
│ │ │ └── Philips.imageset/
│ │ │ └── IssuerIcons
│ │ ├── JetBrains.imageset/
│ │ │ └── Contents.json
│ │ ├── Kickstarter.imageset/
│ │ │ └── Contents.json
│ │ ├── LastPass.imageset/
│ │ │ └── Contents.json
│ │ ├── LinkedIn.imageset/
│ │ │ └── Contents.json
│ │ ├── Linode.imageset/
│ │ │ └── Contents.json
│ │ ├── Lobsters.imageset/
│ │ │ └── Contents.json
│ │ ├── LocalBitcoins.imageset/
│ │ │ └── Contents.json
│ │ ├── Mailchimp.imageset/
│ │ │ └── Contents.json
│ │ ├── Mastodon.imageset/
│ │ │ └── Contents.json
│ │ ├── Mega.imageset/
│ │ │ └── Contents.json
│ │ ├── Microsoft.imageset/
│ │ │ └── Contents.json
│ │ ├── Name.com.imageset/
│ │ │ └── Contents.json
│ │ ├── Netlify.imageset/
│ │ │ └── Contents.json
│ │ ├── Nextcloud.imageset/
│ │ │ └── Contents.json
│ │ ├── NexusMods.imageset/
│ │ │ └── Contents.json
│ │ ├── NiceHash.imageset/
│ │ │ └── Contents.json
│ │ ├── Nintendo.imageset/
│ │ │ └── Contents.json
│ │ ├── Njalla.imageset/
│ │ │ └── Contents.json
│ │ ├── Nodecraft.imageset/
│ │ │ └── Contents.json
│ │ ├── NordPass.imageset/
│ │ │ └── Contents.json
│ │ ├── PaladinExtensions.imageset/
│ │ │ └── Contents.json
│ │ ├── Parler.imageset/
│ │ │ └── Contents.json
│ │ ├── PayPal.imageset/
│ │ │ └── Contents.json
│ │ ├── PhilipsHue.imageset/
│ │ │ └── Contents.json
│ │ ├── Posteo.imageset/
│ │ │ └── Contents.json
│ │ ├── Postmark.imageset/
│ │ │ └── Contents.json
│ │ ├── Privacy.imageset/
│ │ │ └── Contents.json
│ │ ├── ProfitBricks.imageset/
│ │ │ └── Contents.json
│ │ ├── ProtonMail.imageset/
│ │ │ └── Contents.json
│ │ ├── Prusa.imageset/
│ │ │ └── Contents.json
│ │ ├── PrusaAccount.imageset/
│ │ │ └── Contents.json
│ │ ├── Reddit.imageset/
│ │ │ └── Contents.json
│ │ ├── Robinhood.imageset/
│ │ │ └── Contents.json
│ │ ├── RubyGems.imageset/
│ │ │ └── Contents.json
│ │ ├── RuneScape.imageset/
│ │ │ └── Contents.json
│ │ ├── STACK.imageset/
│ │ │ └── Contents.json
│ │ ├── SimpleLogin.imageset/
│ │ │ └── Contents.json
│ │ ├── Slack.imageset/
│ │ │ └── Contents.json
│ │ ├── Snapchat.imageset/
│ │ │ └── Contents.json
│ │ ├── Sony.imageset/
│ │ │ └── Contents.json
│ │ ├── Squarespace.imageset/
│ │ │ └── Contents.json
│ │ ├── StandardNotes.imageset/
│ │ │ └── Contents.json
│ │ ├── Stripe.imageset/
│ │ │ └── Contents.json
│ │ ├── Surfshark.imageset/
│ │ │ └── Contents.json
│ │ ├── TETR.IO.imageset/
│ │ │ └── Contents.json
│ │ ├── Time4VPS.imageset/
│ │ │ └── Contents.json
│ │ ├── TorGuard.imageset/
│ │ │ └── Contents.json
│ │ ├── Tresorit.imageset/
│ │ │ └── Contents.json
│ │ ├── Tumblr.imageset/
│ │ │ └── Contents.json
│ │ ├── TurboTax.imageset/
│ │ │ └── Contents.json
│ │ ├── Tutanota.imageset/
│ │ │ └── Contents.json
│ │ ├── Tweakers.imageset/
│ │ │ └── Contents.json
│ │ ├── Twilio.imageset/
│ │ │ └── Contents.json
│ │ ├── Twitch.imageset/
│ │ │ └── Contents.json
│ │ ├── Twitter.imageset/
│ │ │ └── Contents.json
│ │ ├── Uber.imageset/
│ │ │ └── Contents.json
│ │ ├── Ubisoft.imageset/
│ │ │ └── Contents.json
│ │ ├── Unity.imageset/
│ │ │ └── Contents.json
│ │ ├── VKontakte.imageset/
│ │ │ └── Contents.json
│ │ ├── Wallabag.imageset/
│ │ │ └── Contents.json
│ │ ├── WordPress.imageset/
│ │ │ └── Contents.json
│ │ ├── YNAB.imageset/
│ │ │ └── Contents.json
│ │ ├── Zoom.imageset/
│ │ │ └── Contents.json
│ │ └── ownCloud.imageset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Controllers/
│ │ ├── AccountCreationViewController.swift
│ │ ├── AccountSearchResultsViewController.swift
│ │ ├── AccountUpdateViewController.swift
│ │ ├── AccountsTableViewUpdater.swift
│ │ ├── AccountsViewController.swift
│ │ ├── AlgorithmsViewController.swift
│ │ └── ScanningViewController.swift
│ ├── Extensions/
│ │ ├── Data.swift
│ │ └── UIViewController.swift
│ ├── Info.plist
│ ├── Models/
│ │ ├── Account.swift
│ │ ├── Algorithm.swift
│ │ ├── ExternalDataInterop.swift
│ │ ├── Keychain.swift
│ │ └── Password.swift
│ ├── Protocols/
│ │ ├── AccountCreationDelegate.swift
│ │ ├── AccountUpdateDelegate.swift
│ │ └── AlgorithmSelectionDelegate.swift
│ ├── Tofu.xcconfig
│ └── Views/
│ ├── AccountCell.swift
│ └── CircularProgressView.swift
├── Tofu.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
├── TofuTests/
│ ├── AccountTests.swift
│ ├── DataTests.swift
│ ├── Info.plist
│ └── PasswordTests.swift
└── TofuUITests/
├── Info.plist
└── TofuUITests.swift
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/issuer-icon-request.md
================================================
---
name: Issuer Icon Request
about: Use this template when asking for an icon to be added to the app.
title: Add an icon for Example
labels: icon request
assignees: ''
---
Hi! Could you please add an icon for Example?
Their website is at https://example.com.
When scanning their QR code with Tofu, the account issuer shows up as Example.
================================================
FILE: .gitignore
================================================
xcuserdata
.DS_Store
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of
experience, nationality, personal appearance, race, religion, or sexual identity
and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening,
offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <conduct@tofuauth.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an
incident. Further details of specific enforcement policies may be posted
separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.4, available at
[http://contributor-covenant.org/version/1/4][version].
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
We love pull requests from everyone. By participating in this project, you agree
to abide by its [code of conduct].
[code of conduct]: https://github.com/calleerlandsson/Tofu/blob/master/CODE_OF_CONDUCT.md
## Getting Started
To get started contributing to Tofu, follow these steps:
1. Fork and clone the repo.
2. Make your changes. If you're contributing code, please include tests that
fail without your code and pass with it.
3. Make sure all automated tests pass. One way of running them is to use the ⌘U
keyboard shortcut in Xcode.
4. Submit a pull request
We try to respond to, if not merge, pull requests as soon as we can.
================================================
FILE: GenerateIssuerIconAssets.sh
================================================
#!/usr/bin/env bash
set -euo pipefail
get_name() {
echo $1 | sed -E 's:.+/(.+)\.png:\1:'
}
write_json() {
# JSON copied from Xcode output
cat << EOF > "$2"
{
"images" : [
{
"idiom" : "universal",
"filename" : "${1}.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "${1}@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "${1}@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
EOF
}
cd "$(dirname "$0")"
for file in ./IssuerIcons/*.png; do
name="$(get_name $file)"
echo "Generating icon for ${name}"
imageset="./Tofu/Assets.xcassets/${name}.imageset/"
mkdir -p "$imageset"
sips --resampleWidth 192 "$file" --out "${imageset}${name}@3x.png" >/dev/null
sips --resampleWidth 128 "$file" --out "${imageset}${name}@2x.png" >/dev/null
sips --resampleWidth 64 "$file" --out "${imageset}${name}.png" >/dev/null
write_json "$name" "${imageset}Contents.json"
done
================================================
FILE: LICENSE
================================================
Copyright (c) 2016 Calle Erlandsson
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
================================================
FILE: README.md
================================================
# Tofu
An easy-to-use, open-source two-factor authentication app designed specifically
for iOS.
Tofu generates one-time passwords to help you protect your online accounts.
These passwords are used together with your normal password when you sign into
services like Google, Facebook, Dropbox, Amazon, and GitHub.
Tofu works with all services that provide two-factor authentication using the
HOTP and TOTP algorithms. It does not require a network or cellular connection
and can be used in airplane mode.
## New maintainer
Hey there, [Calle](https://github.com/calleluks) here, the original author of Tofu Authenticator. Over the last few years I haven’t had as much time to work on the app as I had hoped for. While Tofu has been working fine (I still use the app every day), few improvements have been made and no new platform features have been integrated. Luckily, my friend [Daniel Kennett](https://github.com/ikenndac) who is both a long-time user of Tofu and a way more experienced iOS developer than I am, has offered to take over maintenance and ownership of the app going forward. I’m really thankful for being able to place the app in Daniel’s safe hands and look forward to following the future development of it!
## Installation
Tofu is available for free on the App Store.
[](https://itunes.apple.com/app/tofu-authenticator/id1082229305)
## Issuer icons
Here's how you can help add new icons to the app:
1. Fork and clone this repo.
2. Add your icon to the `IssuerIcons/` directory.
The icon should be a square PNG without rounded corners and without borders.
It must be at least 196x196 pixels but we prefer larger sizes such as
1024x1024.
3. Run `./GenerateIssuerIconAssets.sh` from the root of the repo.
4. Add an entry for the icon to [the `imageNames` dictionary](https://github.com/calleerlandsson/Tofu/blob/master/Tofu/AccountCell.swift#L15).
The key should be the string that shows up in the account's Issuer field
when scanning a QR code for the service. The value should be the name of the
icon file.
5. Commit your changes and open a PR.
Here's an example commit for adding a new icon: [692e32a](https://github.com/calleerlandsson/Tofu/commit/692e32a9744bcaa360e4d7db9f00c4e90f6f66ac)
If you don't feel comfortable adding icons yourself, you can ask others to do
so by [opening issues using the Issuer Icon Request template](https://github.com/calleerlandsson/Tofu/issues/new?labels=icon+request&template=issuer-icon-request.md&title=Add+an+icon+for+Example).
================================================
FILE: Tofu/AppDelegate.swift
================================================
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func application(
_ application: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
let rootViewController = window!.rootViewController!
guard let account = Account(url: url) else {
let alert = UIAlertController(
title: "Could Not Import Account",
message: "The account information was not of the expected format.",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .default))
rootViewController.present(alert, animated: true)
return false
}
let accountsViewController = rootViewController.children.first as! AccountsViewController
accountsViewController.createAccount(account)
let alert = UIAlertController(
title: "Account Imported",
message: "Successfully imported \(account.description)",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
rootViewController.present(alert, animated: true)
return true
}
}
================================================
FILE: Tofu/Assets.xcassets/17thShard.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "17thShard.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "17thShard@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "17thShard@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/AWS.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "AWS.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "AWS@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "AWS@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Adobe.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Adobe.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Adobe@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Adobe@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Allegro.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Allegro.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Allegro@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Allegro@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Amazon.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Amazon.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Amazon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Amazon@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/AnonAddy.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "AnonAddy.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "AnonAddy@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "AnonAddy@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon29@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon40@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon60@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "AppIcon83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "AppIcon-iTC.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Atlassian.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Atlassian.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Atlassian@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Atlassian@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Backblaze.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Backblaze.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Backblaze@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Backblaze@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Basecamp.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Basecamp.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Basecamp@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Basecamp@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Binance.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Binance.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Binance@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Binance@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/BitBay.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "BitBay.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "BitBay@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "BitBay@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Bitbucket.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Bitbucket.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Bitbucket@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Bitbucket@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Bitstamp.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Bitstamp.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Bitstamp@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Bitstamp@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Bittrex.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Bittrex.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Bittrex@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Bittrex@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Bitwarden.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Bitwarden.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Bitwarden@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Bitwarden@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/CircularProgressViewBorderThick.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "CircularProgressViewBorderThick@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "CircularProgressViewBorderThick@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
================================================
FILE: Tofu/Assets.xcassets/CircularProgressViewBorderThin.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "CircularProgressViewBorderThin@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "CircularProgressViewBorderThin@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
================================================
FILE: Tofu/Assets.xcassets/Cloudflare.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Cloudflare.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Cloudflare@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Cloudflare@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Coinbase.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Coinbase.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Coinbase@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Coinbase@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Contentful.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Contentful.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Contentful@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Contentful@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Contents.json
================================================
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: Tofu/Assets.xcassets/CorporateTrust.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "CorporateTrust.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "CorporateTrust@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "CorporateTrust@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/CyDIS.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "CyDIS.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "CyDIS@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "CyDIS@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/DNSimple.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "DNSimple.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "DNSimple@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "DNSimple@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/DigitalOcean.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "DigitalOcean.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "DigitalOcean@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "DigitalOcean@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Discord.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Discord.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Discord@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Discord@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Docker.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Docker.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Docker@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Docker@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Dropbox.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Dropbox.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Dropbox@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Dropbox@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/ElectronicArts.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ElectronicArts.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ElectronicArts@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ElectronicArts@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/EpicGames.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "EpicGames.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "EpicGames@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "EpicGames@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Evernote.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Evernote.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Evernote@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Evernote@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Facebook.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Facebook.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Facebook@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Facebook@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/FastMail.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "FastMail.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "FastMail@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "FastMail@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Fidelity.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Fidelity.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Fidelity@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Fidelity@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Figma.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Figma.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Figma@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Figma@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Firefox.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Firefox.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Firefox@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Firefox@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Gandi.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Gandi.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Gandi@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Gandi@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/GitHub.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "GitHub.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "GitHub@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "GitHub@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/GitLab.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "GitLab.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "GitLab@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "GitLab@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Gitea.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Gitea.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Gitea@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Gitea@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/GoDaddy.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "GoDaddy.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "GoDaddy@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "GoDaddy@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Google.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Google.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Google@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Google@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/GreenAddress.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "GreenAddress.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "GreenAddress@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "GreenAddress@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/HEY.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "HEY.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "HEY@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "HEY@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/HackTheBox.imageset/Contents.json
================================================
{
"images" : [
{
"filename" : "hackthebox.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "hackthebox@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "hackthebox@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: Tofu/Assets.xcassets/Heroku.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Heroku.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Heroku@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Heroku@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Hetzner.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Hetzner.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Hetzner@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Hetzner@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/HomeAssistant.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "HomeAssistant.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "HomeAssistant@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "HomeAssistant@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Honeybadger.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Honeybadger.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Honeybadger@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Honeybadger@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Hostek.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Hostek.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Hostek@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Hostek@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Hover.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Hover.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Hover@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Hover@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/HumbleBundle.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "HumbleBundle.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "HumbleBundle@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "HumbleBundle@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/IDme.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "IDme.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "IDme@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "IDme@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/IFTTT.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "IFTTT.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "IFTTT@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "IFTTT@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Instagram.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Instagram.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Instagram@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Instagram@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Intercom.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Intercom.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Intercom@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Intercom@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/JetBrains.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "JetBrains.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "JetBrains@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "JetBrains@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Kickstarter.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Kickstarter.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Kickstarter@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Kickstarter@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/LastPass.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LastPass.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LastPass@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LastPass@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/LinkedIn.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LinkedIn.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LinkedIn@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LinkedIn@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Linode.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Linode.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Linode@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Linode@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Lobsters.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Lobsters.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Lobsters@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Lobsters@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/LocalBitcoins.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LocalBitcoins.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LocalBitcoins@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LocalBitcoins@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Mailchimp.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Mailchimp.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Mailchimp@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Mailchimp@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Mastodon.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Mastodon.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Mastodon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Mastodon@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Mega.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Mega.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Mega@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Mega@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Microsoft.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Microsoft.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Microsoft@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Microsoft@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Name.com.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Name.com.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Name.com@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Name.com@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Netlify.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Netlify.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Netlify@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Netlify@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Nextcloud.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Nextcloud.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Nextcloud@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Nextcloud@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/NexusMods.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "NexusMods.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "NexusMods@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "NexusMods@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/NiceHash.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "NiceHash.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "NiceHash@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "NiceHash@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Nintendo.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Nintendo.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Nintendo@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Nintendo@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Njalla.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Njalla.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Njalla@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Njalla@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Nodecraft.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Nodecraft.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Nodecraft@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Nodecraft@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/NordPass.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "NordPass.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "NordPass@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "NordPass@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/PaladinExtensions.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "PaladinExtensions.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "PaladinExtensions@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "PaladinExtensions@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Parler.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Parler.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Parler@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Parler@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/PayPal.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "PayPal.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "PayPal@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "PayPal@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/PhilipsHue.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "PhilipsHue.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "PhilipsHue@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "PhilipsHue@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Posteo.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Posteo.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Posteo@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Posteo@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Postmark.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Postmark.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Postmark@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Postmark@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Privacy.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Privacy.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Privacy@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Privacy@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/ProfitBricks.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ProfitBricks.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ProfitBricks@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ProfitBricks@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/ProtonMail.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ProtonMail.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ProtonMail@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ProtonMail@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Prusa.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Prusa.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Prusa@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Prusa@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/PrusaAccount.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "PrusaAccount.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "PrusaAccount@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "PrusaAccount@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Reddit.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Reddit.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Reddit@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Reddit@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Robinhood.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Robinhood.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Robinhood@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Robinhood@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/RubyGems.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "RubyGems.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "RubyGems@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "RubyGems@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/RuneScape.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "RuneScape.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "RuneScape@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "RuneScape@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/STACK.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "STACK.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "STACK@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "STACK@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/SimpleLogin.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "SimpleLogin.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "SimpleLogin@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "SimpleLogin@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Slack.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Slack.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Slack@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Slack@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Snapchat.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Snapchat.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Snapchat@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Snapchat@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Sony.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Sony.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Sony@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Sony@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Squarespace.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Squarespace.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Squarespace@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Squarespace@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/StandardNotes.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "StandardNotes.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "StandardNotes@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "StandardNotes@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Stripe.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Stripe.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Stripe@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Stripe@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Surfshark.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Surfshark.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Surfshark@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Surfshark@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/TETR.IO.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "TETR.IO.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "TETR.IO@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "TETR.IO@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Time4VPS.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Time4VPS.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Time4VPS@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Time4VPS@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/TorGuard.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "TorGuard.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "TorGuard@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "TorGuard@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Tresorit.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Tresorit.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Tresorit@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Tresorit@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Tumblr.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Tumblr.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Tumblr@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Tumblr@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/TurboTax.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "TurboTax.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "TurboTax@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "TurboTax@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Tutanota.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Tutanota.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Tutanota@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Tutanota@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Tweakers.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Tweakers.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Tweakers@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Tweakers@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Twilio.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Twilio.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Twilio@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Twilio@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Twitch.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Twitch.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Twitch@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Twitch@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Twitter.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Twitter.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Twitter@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Twitter@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Uber.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Uber.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Uber@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Uber@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Ubisoft.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Ubisoft.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Ubisoft@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Ubisoft@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Unity.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Unity.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Unity@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Unity@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/VKontakte.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "VKontakte.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "VKontakte@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "VKontakte@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Wallabag.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Wallabag.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Wallabag@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Wallabag@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/WordPress.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "WordPress.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "WordPress@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "WordPress@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/YNAB.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "YNAB.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "YNAB@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "YNAB@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/Zoom.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Zoom.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Zoom@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Zoom@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Assets.xcassets/ownCloud.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ownCloud.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ownCloud@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ownCloud@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Tofu/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: Tofu/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="nkx-Kv-qqz">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Add Account-->
<scene sceneID="Cvd-WL-bZB">
<objects>
<viewController id="I3F-x1-Uca" customClass="ScanningViewController" customModule="Tofu" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="ZUh-E7-1wK"/>
<viewControllerLayoutGuide type="bottom" id="SqE-V3-zwh"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Y7o-gx-74K">
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="964-37-yur">
<rect key="frame" x="16" y="272" width="343" height="103.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="To add an account by scanning a QR code, please allow Tofu to access your camera in the Settings application." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QSo-RK-FII">
<rect key="frame" x="0.0" y="45" width="343" height="50.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No Camera Access" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VOq-gD-H4m">
<rect key="frame" x="8" y="8" width="327" height="29"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="VOq-gD-H4m" secondAttribute="trailing" id="2ak-bF-ZNV"/>
<constraint firstItem="VOq-gD-H4m" firstAttribute="top" secondItem="964-37-yur" secondAttribute="topMargin" id="LxM-yn-CnD"/>
<constraint firstItem="VOq-gD-H4m" firstAttribute="leading" secondItem="964-37-yur" secondAttribute="leadingMargin" id="Np2-bg-05n"/>
<constraint firstItem="QSo-RK-FII" firstAttribute="top" secondItem="VOq-gD-H4m" secondAttribute="bottom" constant="8" id="PGI-i7-H6e"/>
<constraint firstItem="QSo-RK-FII" firstAttribute="leading" secondItem="964-37-yur" secondAttribute="leading" id="fc6-je-Fng"/>
<constraint firstAttribute="bottomMargin" secondItem="QSo-RK-FII" secondAttribute="bottom" id="iCy-wQ-HvK"/>
<constraint firstAttribute="trailing" secondItem="QSo-RK-FII" secondAttribute="trailing" id="xmj-ui-9Hl"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="964-37-yur" firstAttribute="centerY" secondItem="Y7o-gx-74K" secondAttribute="centerY" id="YQ2-Bn-vd1"/>
<constraint firstItem="964-37-yur" firstAttribute="leading" secondItem="Y7o-gx-74K" secondAttribute="leadingMargin" id="eHq-nS-la4"/>
<constraint firstAttribute="trailingMargin" secondItem="964-37-yur" secondAttribute="trailing" id="onr-1Y-n1r"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Add Account" id="G5L-6K-n5t">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="t02-9l-NcV">
<connections>
<action selector="didPressCancel:" destination="I3F-x1-Uca" id="9f4-zp-3H8"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="allowCameraAccessView" destination="964-37-yur" id="vKo-Wj-meQ"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="TSJ-k5-Por" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2166" y="96"/>
</scene>
<!--Accounts-->
<scene sceneID="eJy-We-MJo">
<objects>
<tableViewController definesPresentationContext="YES" id="bOu-aX-zXX" customClass="AccountsViewController" customModule="Tofu" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelectionDuringEditing="YES" rowHeight="84" sectionHeaderHeight="28" sectionFooterHeight="28" id="CH7-nX-hsd">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" showsReorderControl="YES" indentationWidth="10" reuseIdentifier="AccountCell" editingAccessoryType="disclosureIndicator" id="xpz-Pg-oK7" customClass="AccountCell" customModule="Tofu" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="84"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="xpz-Pg-oK7" id="Zor-2e-g7q">
<rect key="frame" x="0.0" y="0.0" width="375" height="84"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="oPs-iT-b8F">
<rect key="frame" x="16" y="10" width="64" height="64"/>
<constraints>
<constraint firstAttribute="height" constant="64" id="Dn5-np-gTx"/>
<constraint firstAttribute="width" constant="64" id="zjG-If-dUP"/>
</constraints>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AVc-5o-PUk">
<rect key="frame" x="90" y="10.5" width="259" height="63"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="012 345" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="kuh-hx-0vn">
<rect key="frame" x="0.0" y="-4" width="259" height="53"/>
<fontDescription key="fontDescription" type="system" weight="thin" pointSize="44"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Example (test@example.com)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="S0M-xR-VeY">
<rect key="frame" x="1" y="45" width="258" height="18"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="S0M-xR-VeY" firstAttribute="top" secondItem="kuh-hx-0vn" secondAttribute="bottom" constant="-4" id="DtD-gk-rhr"/>
<constraint firstItem="kuh-hx-0vn" firstAttribute="top" secondItem="AVc-5o-PUk" secondAttribute="top" constant="-4" id="N82-Rp-eOG"/>
<constraint firstItem="kuh-hx-0vn" firstAttribute="top" secondItem="S0M-xR-VeY" secondAttribute="bottom" id="QmR-d0-PvZ"/>
<constraint firstAttribute="bottom" secondItem="S0M-xR-VeY" secondAttribute="bottom" id="U66-XA-dxf"/>
<constraint firstItem="S0M-xR-VeY" firstAttribute="leading" secondItem="AVc-5o-PUk" secondAttribute="leading" constant="1" id="aUW-Tz-Gup"/>
<constraint firstAttribute="trailing" secondItem="S0M-xR-VeY" secondAttribute="trailing" id="bja-Jr-CGz"/>
<constraint firstItem="kuh-hx-0vn" firstAttribute="top" secondItem="S0M-xR-VeY" secondAttribute="bottom" id="ifn-B8-o1g"/>
<constraint firstItem="kuh-hx-0vn" firstAttribute="leading" secondItem="AVc-5o-PUk" secondAttribute="leading" id="rk4-vG-rqV"/>
<constraint firstItem="S0M-xR-VeY" firstAttribute="top" secondItem="kuh-hx-0vn" secondAttribute="top" id="w5J-wK-Gum"/>
<constraint firstAttribute="trailing" secondItem="kuh-hx-0vn" secondAttribute="trailing" id="xv2-Zg-oKh"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="QmR-d0-PvZ"/>
<exclude reference="ifn-B8-o1g"/>
<exclude reference="w5J-wK-Gum"/>
</mask>
</variation>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wMT-Jm-ygj">
<rect key="frame" x="39.5" y="20.5" width="17" height="43"/>
<fontDescription key="fontDescription" type="system" weight="ultraLight" pointSize="36"/>
<color key="textColor" systemColor="tertiaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="AVc-5o-PUk" firstAttribute="leading" secondItem="oPs-iT-b8F" secondAttribute="trailing" constant="10" id="82O-j3-8aN"/>
<constraint firstAttribute="trailingMargin" secondItem="AVc-5o-PUk" secondAttribute="trailing" constant="10" id="Mg9-dJ-khj"/>
<constraint firstItem="wMT-Jm-ygj" firstAttribute="centerX" secondItem="oPs-iT-b8F" secondAttribute="centerX" id="Nqn-2y-prV"/>
<constraint firstItem="wMT-Jm-ygj" firstAttribute="centerY" secondItem="oPs-iT-b8F" secondAttribute="centerY" id="RjH-lT-vDX"/>
<constraint firstItem="oPs-iT-b8F" firstAttribute="centerY" secondItem="Zor-2e-g7q" secondAttribute="centerY" id="rf0-aj-3Nw"/>
<constraint firstItem="oPs-iT-b8F" firstAttribute="leading" secondItem="Zor-2e-g7q" secondAttribute="leadingMargin" id="tz5-ui-ym2"/>
<constraint firstItem="AVc-5o-PUk" firstAttribute="centerY" secondItem="Zor-2e-g7q" secondAttribute="centerY" id="zwv-gB-bgJ"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="accountImageView" destination="oPs-iT-b8F" id="r8w-J4-8lf"/>
<outlet property="identifierLabel" destination="S0M-xR-VeY" id="eXU-1X-Uwo"/>
<outlet property="issuerLabel" destination="wMT-Jm-ygj" id="joU-Ty-sig"/>
<outlet property="valueLabel" destination="kuh-hx-0vn" id="NV7-fj-bZQ"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="bOu-aX-zXX" id="QCb-RT-fC5"/>
<outlet property="delegate" destination="bOu-aX-zXX" id="6qu-NV-7lB"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Accounts" id="QD1-ot-U1H">
<barButtonItem key="rightBarButtonItem" systemItem="add" id="JTL-yM-y0g">
<connections>
<action selector="addAccount:" destination="bOu-aX-zXX" id="Gxq-TA-Oug"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="emptyView" destination="hT8-ZY-D3A" id="eqL-UC-LWT"/>
<segue destination="ERk-zj-Q6A" kind="show" identifier="EditAccountSegue" id="rWD-gI-uHe"/>
<segue destination="fMR-de-62D" kind="presentation" identifier="ScanSegue" id="Eo2-wA-mmS"/>
<segue destination="yTV-zU-3QS" kind="presentation" identifier="EnterManuallySegue" id="sz3-ZJ-SNl"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="RZK-WF-dPC" userLabel="First Responder" sceneMemberID="firstResponder"/>
<view contentMode="scaleToFill" id="hT8-ZY-D3A">
<rect key="frame" x="0.0" y="0.0" width="240" height="128"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NHh-Ks-F0R">
<rect key="frame" x="16" y="21" width="208" height="86.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add one by tapping the add button in the upper right corner." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iub-lm-UUY">
<rect key="frame" x="0.0" y="45" width="208" height="33.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No Accounts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RGz-NF-3aU">
<rect key="frame" x="8" y="8" width="192" height="29"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="iub-lm-UUY" firstAttribute="leading" secondItem="NHh-Ks-F0R" secondAttribute="leading" id="2BF-CA-u6f"/>
<constraint firstItem="iub-lm-UUY" firstAttribute="top" secondItem="RGz-NF-3aU" secondAttribute="bottom" constant="8" id="4ub-V1-vTw"/>
<constraint firstAttribute="trailing" secondItem="iub-lm-UUY" secondAttribute="trailing" id="Bb5-ss-CAr"/>
<constraint firstAttribute="bottomMargin" secondItem="iub-lm-UUY" secondAttribute="bottom" id="EAB-pt-d9C"/>
<constraint firstItem="RGz-NF-3aU" firstAttribute="top" secondItem="NHh-Ks-F0R" secondAttribute="topMargin" id="ilP-Rf-Ydi"/>
<constraint firstAttribute="trailingMargin" secondItem="RGz-NF-3aU" secondAttribute="trailing" id="tvl-mg-btC"/>
<constraint firstItem="RGz-NF-3aU" firstAttribute="leading" secondItem="NHh-Ks-F0R" secondAttribute="leadingMargin" id="zIc-ha-jWl"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="NHh-Ks-F0R" firstAttribute="leading" secondItem="hT8-ZY-D3A" secondAttribute="leadingMargin" id="0NQ-Lv-ccR"/>
<constraint firstAttribute="trailingMargin" secondItem="NHh-Ks-F0R" secondAttribute="trailing" id="2FO-d5-1lx"/>
<constraint firstItem="NHh-Ks-F0R" firstAttribute="centerY" secondItem="hT8-ZY-D3A" secondAttribute="centerY" id="Vdh-wl-iNf"/>
</constraints>
</view>
</objects>
<point key="canvasLocation" x="87" y="459"/>
</scene>
<!--Edit Account-->
<scene sceneID="shn-2G-AZX">
<objects>
<tableViewController id="ERk-zj-Q6A" customClass="AccountUpdateViewController" customModule="Tofu" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="Flh-7t-GjH">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection headerTitle="Display" id="OPD-Ia-5ZL">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="Awa-te-ZQi">
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Awa-te-ZQi" id="rsx-Y5-IhP">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="test@example.com" placeholder="Name" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="HKm-Ia-bHS">
<rect key="frame" x="16" y="0.0" width="359" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="editingChangedForTextField:" destination="paP-7e-SE0" eventType="editingChanged" id="50W-oD-lKM"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="HKm-Ia-bHS" firstAttribute="leading" secondItem="rsx-Y5-IhP" secondAttribute="leadingMargin" id="68u-86-C5j"/>
<constraint firstItem="HKm-Ia-bHS" firstAttribute="top" secondItem="rsx-Y5-IhP" secondAttribute="top" id="Ndc-4D-YGz"/>
<constraint firstAttribute="trailing" secondItem="HKm-Ia-bHS" secondAttribute="trailing" id="SH6-r8-L0e"/>
<constraint firstAttribute="bottom" secondItem="HKm-Ia-bHS" secondAttribute="bottom" id="sTW-FC-ZCP"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="5ET-nQ-wKW">
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="5ET-nQ-wKW" id="G0d-Sj-yaL">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Example" placeholder="Issuer" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="agH-Lg-BOE">
<rect key="frame" x="16" y="0.0" width="359" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="editingChangedForTextField:" destination="paP-7e-SE0" eventType="editingChanged" id="NoQ-LD-MHT"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="agH-Lg-BOE" secondAttribute="bottom" id="08v-fA-sy9"/>
<constraint firstAttribute="trailing" secondItem="agH-Lg-BOE" secondAttribute="trailing" id="6FA-0G-cs8"/>
<constraint firstItem="agH-Lg-BOE" firstAttribute="leading" secondItem="G0d-Sj-yaL" secondAttribute="leadingMargin" id="FrO-J5-4Bd"/>
<constraint firstItem="agH-Lg-BOE" firstAttribute="top" secondItem="G0d-Sj-yaL" secondAttribute="top" id="aMf-1x-7eA"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="ERk-zj-Q6A" id="Lum-DP-HU3"/>
<outlet property="delegate" destination="ERk-zj-Q6A" id="wbU-ej-fRc"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Edit Account" largeTitleDisplayMode="never" id="Ogh-nQ-SzX"/>
<connections>
<outlet property="issuerField" destination="agH-Lg-BOE" id="WTi-Bz-0YE"/>
<outlet property="nameField" destination="HKm-Ia-bHS" id="m5a-sw-8U2"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xAt-39-x93" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="86" y="1210"/>
</scene>
<!--Add Account-->
<scene sceneID="EfE-dR-QMF">
<objects>
<tableViewController id="paP-7e-SE0" customClass="AccountCreationViewController" customModule="Tofu" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="tne-qv-Me8">
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection headerTitle="Display" id="gdN-Ye-UUr">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="WKR-La-e5Q">
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="WKR-La-e5Q" id="vux-RZ-ucS">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="H3V-Eb-wcl">
<rect key="frame" x="16" y="0.0" width="359" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="editingChangedForTextField:" destination="paP-7e-SE0" eventType="editingChanged" id="kkd-hn-Go4"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="H3V-Eb-wcl" firstAttribute="top" secondItem="vux-RZ-ucS" secondAttribute="top" id="1xA-DL-9dH"/>
<constraint firstAttribute="bottom" secondItem="H3V-Eb-wcl" secondAttribute="bottom" id="YTX-rZ-1Kd"/>
<constraint firstAttribute="trailing" secondItem="H3V-Eb-wcl" secondAttribute="trailing" id="cZM-zJ-8w8"/>
<constraint firstItem="H3V-Eb-wcl" firstAttribute="leading" secondItem="vux-RZ-ucS" secondAttribute="leadingMargin" id="iTr-ZG-Doh"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="2sG-OR-j28">
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="2sG-OR-j28" id="CMm-zw-ybS">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Issuer" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="df2-wV-cFm">
<rect key="frame" x="16" y="0.0" width="359" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="editingChangedForTextField:" destination="paP-7e-SE0" eventType="editingChanged" id="R7V-Iv-ySL"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="df2-wV-cFm" firstAttribute="leading" secondItem="CMm-zw-ybS" secondAttribute="leadingMargin" id="5Mi-tK-tcA"/>
<constraint firstAttribute="trailing" secondItem="df2-wV-cFm" secondAttribute="trailing" id="DpX-Pq-Yh4"/>
<constraint firstItem="df2-wV-cFm" firstAttribute="top" secondItem="CMm-zw-ybS" secondAttribute="top" id="JpI-wO-6Zz"/>
<constraint firstAttribute="bottom" secondItem="df2-wV-cFm" secondAttribute="bottom" id="myn-Iv-07v"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Details" id="K0Y-0M-mzD">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="1w4-M9-lQ1">
<rect key="frame" x="0.0" y="199.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="1w4-M9-lQ1" id="ifn-NR-Tgv">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Secret" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="jBF-0n-DLb">
<rect key="frame" x="16" y="0.0" width="359" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet"/>
<connections>
<action selector="editingChangedForTextField:" destination="paP-7e-SE0" eventType="editingChanged" id="3Ui-rk-wo7"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="jBF-0n-DLb" firstAttribute="top" secondItem="ifn-NR-Tgv" secondAttribute="top" id="Mp2-TN-8XS"/>
<constraint firstAttribute="bottom" secondItem="jBF-0n-DLb" secondAttribute="bottom" id="b6v-Nu-b1l"/>
<constraint firstAttribute="trailing" secondItem="jBF-0n-DLb" secondAttribute="trailing" id="eY5-Mn-CaA"/>
<constraint firstItem="jBF-0n-DLb" firstAttribute="leading" secondItem="ifn-NR-Tgv" secondAttribute="leadingMargin" id="fog-T0-q0d"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="buM-CA-edQ" detailTextLabel="ya0-u2-x0a" style="IBUITableViewCellStyleValue1" id="kxL-Xc-94s">
<rect key="frame" x="0.0" y="243.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="kxL-Xc-94s" id="9CF-bF-0me">
<rect key="frame" x="0.0" y="0.0" width="348" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Algorithm" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="buM-CA-edQ">
<rect key="frame" x="16" y="13" width="70.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="SHA1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ya0-u2-x0a">
<rect key="frame" x="301.5" y="13" width="38.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="EPo-Jj-mKT" kind="show" id="qHo-Cb-rBv"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="hM1-Cj-zIU">
<rect key="frame" x="0.0" y="287.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="hM1-Cj-zIU" id="jsg-Cn-TRW">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Eight digits" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xFw-JK-Cmq">
<rect key="frame" x="16" y="11.5" width="278" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="0gM-IY-7nI">
<rect key="frame" x="302" y="6.5" width="51" height="31"/>
</switch>
</subviews>
<constraints>
<constraint firstItem="xFw-JK-Cmq" firstAttribute="leading" secondItem="jsg-Cn-TRW" secondAttribute="leadingMargin" id="7GS-hW-zbc"/>
<constraint firstItem="0gM-IY-7nI" firstAttribute="leading" secondItem="xFw-JK-Cmq" secondAttribute="trailing" constant="8" symbolic="YES" id="Be3-lU-3el"/>
<constraint firstAttribute="trailingMargin" secondItem="0gM-IY-7nI" secondAttribute="trailing" constant="8" id="Hfj-XW-irw"/>
<constraint firstItem="xFw-JK-Cmq" firstAttribute="centerY" secondItem="jsg-Cn-TRW" secondAttribute="centerY" id="V3Y-1W-Oxj"/>
<constraint firstItem="0gM-IY-7nI" firstAttribute="centerY" secondItem="jsg-Cn-TRW" secondAttribute="centerY" id="tHn-uv-f1G"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="fC0-23-SKh">
<rect key="frame" x="0.0" y="331.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="fC0-23-SKh" id="K8h-dm-pIT">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Time based" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YDo-nS-Jmv">
<rect key="frame" x="16" y="11.5" width="278" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="veQ-Xe-FIX">
<rect key="frame" x="302" y="6.5" width="51" height="31"/>
<connections>
<action selector="valueChangedForTimeBasedSwitch" destination="paP-7e-SE0" eventType="valueChanged" id="LED-gv-7Md"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="veQ-Xe-FIX" secondAttribute="trailing" constant="8" id="1Ho-0l-8hV"/>
<constraint firstItem="veQ-Xe-FIX" firstAttribute="leading" secondItem="YDo-nS-Jmv" secondAttribute="trailing" constant="8" symbolic="YES" id="7LY-qa-wD6"/>
<constraint firstItem="YDo-nS-Jmv" firstAttribute="leading" secondItem="K8h-dm-pIT" secondAttribute="leadingMargin" id="QSj-Mj-LF8"/>
<constraint firstItem="YDo-nS-Jmv" firstAttribute="centerY" secondItem="K8h-dm-pIT" secondAttribute="centerY" id="Vx7-mr-n8q"/>
<constraint firstItem="veQ-Xe-FIX" firstAttribute="centerY" secondItem="K8h-dm-pIT" secondAttribute="centerY" id="zGC-UR-w3j"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Gnt-ZA-6Kq">
<rect key="frame" x="0.0" y="375.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Gnt-ZA-6Kq" id="0gY-vK-2qQ">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="30" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="fvK-Gg-1Yw">
<rect key="frame" x="73" y="0.0" width="302" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" keyboardType="numberPad"/>
<connections>
<action selector="editingChangedForTextField:" destination="paP-7e-SE0" eventType="editingChanged" id="bnP-zU-Smv"/>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Period" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vtS-NL-KFI">
<rect key="frame" x="16" y="11.5" width="49" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="fvK-Gg-1Yw" secondAttribute="bottom" id="6b6-ew-ZqF"/>
<constraint firstItem="fvK-Gg-1Yw" firstAttribute="top" secondItem="0gY-vK-2qQ" secondAttribute="top" id="Dv4-1x-9z9"/>
<constraint firstItem="vtS-NL-KFI" firstAttribute="leading" secondItem="0gY-vK-2qQ" secondAttribute="leadingMargin" id="KtX-kB-YYr"/>
<constraint firstItem="fvK-Gg-1Yw" firstAttribute="leading" secondItem="vtS-NL-KFI" secondAttribute="trailing" constant="8" id="aMf-25-YJg"/>
<constraint firstAttribute="trailing" secondItem="fvK-Gg-1Yw" secondAttribute="trailing" id="ebm-GH-tYx"/>
<constraint firstItem="vtS-NL-KFI" firstAttribute="centerY" secondItem="0gY-vK-2qQ" secondAttribute="centerY" id="leD-Y9-JIW"/>
<constraint firstItem="fvK-Gg-1Yw" firstAttribute="leading" secondItem="0gY-vK-2qQ" secondAttribute="leadingMargin" constant="8" id="vOl-EC-hID"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="vOl-EC-hID"/>
</mask>
</variation>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="paP-7e-SE0" id="Aei-rD-IB8"/>
<outlet property="delegate" destination="paP-7e-SE0" id="Shk-pE-Ock"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Add Account" id="CVg-F9-sJ7">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="rqL-eo-CGV">
<connections>
<action selector="didPressCancel:" destination="paP-7e-SE0" id="N8U-nq-O45"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" enabled="NO" systemItem="done" id="SYd-13-MGt">
<connections>
<action selector="didPressDone:" destination="paP-7e-SE0" id="hB0-oY-Pb6"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="algorithmLabel" destination="ya0-u2-x0a" id="M76-3q-IdM"/>
<outlet property="doneItem" destination="SYd-13-MGt" id="bPh-jW-7wC"/>
<outlet property="eightDigitsSwitch" destination="0gM-IY-7nI" id="8mK-d8-bJS"/>
<outlet property="issuerField" destination="df2-wV-cFm" id="FYX-Qv-isO"/>
<outlet property="nameField" destination="H3V-Eb-wcl" id="1V2-DF-66g"/>
<outlet property="periodCounterCell" destination="Gnt-ZA-6Kq" id="PN3-8b-l7t"/>
<outlet property="periodCounterField" destination="fvK-Gg-1Yw" id="nJI-fX-fIQ"/>
<outlet property="periodCounterLabel" destination="vtS-NL-KFI" id="3dV-d1-61Y"/>
<outlet property="secretField" destination="jBF-0n-DLb" id="3Jl-hm-sw3"/>
<outlet property="timeBasedSwitch" destination="veQ-Xe-FIX" id="qMy-mo-JAH"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="gXl-Go-K0m" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2166" y="863"/>
</scene>
<!--Algorithm-->
<scene sceneID="Z6Y-tk-aSh">
<objects>
<tableViewController id="EPo-Jj-mKT" customClass="AlgorithmsViewController" customModule="Tofu" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="PMa-2f-5kk">
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="AlgorithmCell" textLabel="5Py-FS-moK" style="IBUITableViewCellStyleDefault" id="uo2-Up-1mv">
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uo2-Up-1mv" id="Lmz-CM-uYu">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="SHA1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="5Py-FS-moK">
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="EPo-Jj-mKT" id="5Xo-n1-w7n"/>
<outlet property="delegate" destination="EPo-Jj-mKT" id="oNU-NP-HPl"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Algorithm" id="aYa-4m-4vv"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="I5s-KS-GkW" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3076" y="862"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="9ta-rc-HC8">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="nkx-Kv-qqz" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" largeTitles="YES" id="8ap-TA-U0q">
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="bOu-aX-zXX" kind="relationship" relationship="rootViewController" id="d4N-G7-0cE"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="h3W-ZH-MHn" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-750" y="460"/>
</scene>
<!--Account Search Results View Controller-->
<scene sceneID="aqF-ES-gLR">
<objects>
<tableViewController storyboardIdentifier="AccountSearchResultsViewController" id="sSp-X7-dXp" customClass="AccountSearchResultsViewController" customModule="Tofu" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="84" sectionHeaderHeight="28" sectionFooterHeight="28" id="rcD-1Q-m4Q">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" showsReorderControl="YES" indentationWidth="10" reuseIdentifier="AccountCell" editingAccessoryType="disclosureIndicator" id="vUX-eq-yhI" customClass="AccountCell" customModule="Tofu" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="84"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="vUX-eq-yhI" id="zV8-t9-Xea">
<rect key="frame" x="0.0" y="0.0" width="375" height="84"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="OqZ-2E-QdG">
<rect key="frame" x="16" y="10" width="64" height="64"/>
<constraints>
<constraint firstAttribute="height" constant="64" id="7ee-yQ-kfJ"/>
<constraint firstAttribute="width" constant="64" id="Epl-9c-aST"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jfp-qZ-bht">
<rect key="frame" x="39.5" y="20.5" width="17" height="43"/>
<fontDescription key="fontDescription" type="system" weight="ultraLight" pointSize="36"/>
<color key="textColor" systemColor="tertiaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Nf-UQ-uOc">
<rect key="frame" x="90" y="10.5" width="259" height="63"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="012 345" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="vJ5-cX-O3i">
<rect key="frame" x="0.0" y="-4" width="259" height="53"/>
<fontDescription key="fontDescription" type="system" weight="thin" pointSize="44"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Example (test@example.com)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QVG-tW-0HS">
<rect key="frame" x="1" y="45" width="258" height="18"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="QVG-tW-0HS" secondAttribute="bottom" id="6vk-3j-QoM"/>
<constraint firstItem="QVG-tW-0HS" firstAttribute="top" secondItem="vJ5-cX-O3i" secondAttribute="bottom" constant="-4" id="L8O-Dm-nTy"/>
<constraint firstItem="vJ5-cX-O3i" firstAttribute="top" secondItem="QVG-tW-0HS" secondAttribute="bottom" id="LlH-fJ-iBa"/>
<constraint firstItem="vJ5-cX-O3i" firstAttribute="top" secondItem="QVG-tW-0HS" secondAttribute="bottom" id="W4D-nn-rDb"/>
<constraint firstItem="vJ5-cX-O3i" firstAttribute="top" secondItem="7Nf-UQ-uOc" secondAttribute="top" constant="-4" id="a3O-jc-W86"/>
<constraint firstItem="QVG-tW-0HS" firstAttribute="top" secondItem="vJ5-cX-O3i" secondAttribute="top" id="fAb-Bd-2uO"/>
<constraint firstItem="vJ5-cX-O3i" firstAttribute="leading" secondItem="7Nf-UQ-uOc" secondAttribute="leading" id="fpc-Mz-axz"/>
<constraint firstItem="QVG-tW-0HS" firstAttribute="leading" secondItem="7Nf-UQ-uOc" secondAttribute="leading" constant="1" id="gam-gy-x7I"/>
<constraint firstAttribute="trailing" secondItem="vJ5-cX-O3i" secondAttribute="trailing" id="ozS-KQ-4fr"/>
<constraint firstAttribute="trailing" secondItem="QVG-tW-0HS" secondAttribute="trailing" id="zcX-DM-zy2"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="LlH-fJ-iBa"/>
<exclude reference="W4D-nn-rDb"/>
<exclude reference="fAb-Bd-2uO"/>
</mask>
</variation>
</view>
</subviews>
<constraints>
<constraint firstItem="Jfp-qZ-bht" firstAttribute="centerX" secondItem="OqZ-2E-QdG" secondAttribute="centerX" id="5sl-On-QRD"/>
<constraint firstItem="7Nf-UQ-uOc" firstAttribute="leading" secondItem="OqZ-2E-QdG" secondAttribute="trailing" constant="10" id="MKt-9F-EmG"/>
<constraint firstItem="OqZ-2E-QdG" firstAttribute="centerY" secondItem="zV8-t9-Xea" secondAttribute="centerY" id="QQw-hq-Btf"/>
<constraint firstAttribute="trailingMargin" secondItem="7Nf-UQ-uOc" secondAttribute="trailing" constant="10" id="aPk-pg-hba"/>
<constraint firstItem="Jfp-qZ-bht" firstAttribute="centerY" secondItem="OqZ-2E-QdG" secondAttribute="centerY" id="cUS-7J-iy6"/>
<constraint firstItem="OqZ-2E-QdG" firstAttribute="leading" secondItem="zV8-t9-Xea" secondAttribute="leadingMargin" id="gpb-CK-ETn"/>
<constraint firstItem="7Nf-UQ-uOc" firstAttribute="centerY" secondItem="zV8-t9-Xea" secondAttribute="centerY" id="qvC-HZ-VCH"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="accountImageView" destination="OqZ-2E-QdG" id="8DV-Pj-IyK"/>
<outlet property="identifierLabel" destination="QVG-tW-0HS" id="dSH-pR-lh7"/>
<outlet property="issuerLabel" destination="Jfp-qZ-bht" id="Wgk-IQ-USs"/>
<outlet property="valueLabel" destination="vJ5-cX-O3i" id="9Uq-sE-j5u"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="sSp-X7-dXp" id="Iwj-Qa-rpu"/>
<outlet property="delegate" destination="sSp-X7-dXp" id="voW-av-d5P"/>
</connections>
</tableView>
<connections>
<outlet property="emptyView" destination="jac-9u-ovS" id="PNR-uC-9UD"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="PN9-uO-Ydg" userLabel="First Responder" sceneMemberID="firstResponder"/>
<view contentMode="scaleToFill" id="jac-9u-ovS">
<rect key="frame" x="0.0" y="0.0" width="240" height="128"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KJv-Gt-yp3">
<rect key="frame" x="16" y="21" width="208" height="86.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No accounts matching your search query could be found." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aA9-U5-djV">
<rect key="frame" x="0.0" y="45" width="208" height="33.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No Results" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kBP-eM-zUM">
<rect key="frame" x="8" y="8" width="192" height="29"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="aA9-U5-djV" firstAttribute="top" secondItem="kBP-eM-zUM" secondAttribute="bottom" constant="8" id="0A5-lT-i9L"/>
<constraint firstAttribute="trailing" secondItem="aA9-U5-djV" secondAttribute="trailing" id="Ee1-9L-bzp"/>
<constraint firstItem="kBP-eM-zUM" firstAttribute="top" secondItem="KJv-Gt-yp3" secondAttribute="topMargin" id="IhW-lQ-lX2"/>
<constraint firstItem="aA9-U5-djV" firstAttribute="leading" secondItem="KJv-Gt-yp3" secondAttribute="leading" id="KFE-aQ-lgh"/>
<constraint firstItem="kBP-eM-zUM" firstAttribute="leading" secondItem="KJv-Gt-yp3" secondAttribute="leadingMargin" id="QIc-fB-EUQ"/>
<constraint firstAttribute="trailingMargin" secondItem="kBP-eM-zUM" secondAttribute="trailing" id="fEV-aX-CRx"/>
<constraint firstAttribute="bottomMargin" secondItem="aA9-U5-djV" secondAttribute="bottom" id="ghZ-sg-67c"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="KJv-Gt-yp3" firstAttribute="centerY" secondItem="jac-9u-ovS" secondAttribute="centerY" id="9ec-Vm-pkR"/>
<constraint firstItem="KJv-Gt-yp3" firstAttribute="leading" secondItem="jac-9u-ovS" secondAttribute="leadingMargin" id="V8f-4A-uyZ"/>
<constraint firstAttribute="trailingMargin" secondItem="KJv-Gt-yp3" secondAttribute="trailing" id="odp-2T-mw3"/>
</constraints>
</view>
</objects>
<point key="canvasLocation" x="86" y="-426"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="F4a-Ti-NYA">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="yTV-zU-3QS" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="AMg-L0-de5">
<rect key="frame" x="0.0" y="0.0" width="375" height="56"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="paP-7e-SE0" kind="relationship" relationship="rootViewController" id="OJy-JP-9ae"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vkD-Qa-Pd5" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1262" y="864"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="xhX-qS-E8o">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="fMR-de-62D" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="owt-wI-o0t">
<rect key="frame" x="0.0" y="0.0" width="375" height="56"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="I3F-x1-Uca" kind="relationship" relationship="rootViewController" id="p8q-va-NZu"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="pvr-cf-N6M" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1262" y="96"/>
</scene>
</scenes>
<resources>
<systemColor name="groupTableViewBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="tertiaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>
================================================
FILE: Tofu/Controllers/AccountCreationViewController.swift
================================================
import UIKit
private let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .none
return formatter
}()
class AccountCreationViewController: UITableViewController, AlgorithmSelectionDelegate {
@IBOutlet weak var doneItem: UIBarButtonItem!
@IBOutlet weak var nameField: UITextField!
@IBOutlet weak var issuerField: UITextField!
@IBOutlet weak var secretField: UITextField!
@IBOutlet weak var algorithmLabel: UILabel!
@IBOutlet weak var eightDigitsSwitch: UISwitch!
@IBOutlet weak var timeBasedSwitch: UISwitch!
@IBOutlet weak var periodCounterCell: UITableViewCell!
@IBOutlet weak var periodCounterLabel: UILabel!
@IBOutlet weak var periodCounterField: UITextField!
var delegate: AccountCreationDelegate?
private var algorithm = Algorithm.sha1
private var periodString: String?
private var counterString: String?
private var period: Int? {
guard periodCounterField.text?.count ?? 0 > 0 else { return 30 }
return formatter.number(from: periodCounterField.text!)?.intValue
}
private var counter: Int? {
guard periodCounterField.text?.count ?? 0 > 0 else { return 0 }
return formatter.number(from: periodCounterField.text!)?.intValue
}
@IBAction func didPressCancel(_ sender: UIBarButtonItem) {
presentingViewController?.dismiss(animated: true, completion: nil)
}
@IBAction func didPressDone(_ sender: UIBarButtonItem) {
let password = Password()
password.timeBased = timeBasedSwitch.isOn
password.algorithm = algorithm
password.digits = eightDigitsSwitch.isOn ? 8 : 6
password.secret = Data(base32Encoded: secretField.text!)!
if timeBasedSwitch.isOn {
password.period = period!
} else {
password.counter = counter!
}
let account = Account()
account.name = nameField.text
account.issuer = issuerField.text
account.password = password
presentingViewController?.dismiss(animated: true) {
self.delegate?.createAccount(account)
}
}
@IBAction func editingChangedForTextField(_ textField: UITextField) {
validate()
}
@IBAction func valueChangedForTimeBasedSwitch() {
if self.timeBasedSwitch.isOn {
counterString = periodCounterField.text
} else {
periodString = periodCounterField.text
}
UIView.transition(with: periodCounterCell,
duration: 0.2,
options: .transitionCrossDissolve,
animations: {
if self.timeBasedSwitch.isOn {
self.periodCounterLabel.text = "Period"
self.periodCounterField.placeholder = String(30)
self.periodCounterField.text = self.periodString
} else {
self.periodCounterLabel.text = "Counter"
self.periodCounterField.placeholder = String(0)
self.periodCounterField.text = self.counterString
}
}, completion: { _ in
self.validate()
})
}
override func viewDidLoad() {
super.viewDidLoad()
nameField.becomeFirstResponder()
algorithmLabel.text = algorithm.name
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let algorithmsController = segue.destination as? AlgorithmsViewController {
algorithmsController.algorithms = [.sha1, .sha256, .sha512]
algorithmsController.selected = algorithm
algorithmsController.delegate = self
}
}
private func validate() {
doneItem.isEnabled = secretField.text?.count ?? 0 > 0 &&
Data(base32Encoded: secretField.text!) != nil &&
(timeBasedSwitch.isOn ? period != nil : counter != nil)
}
// MARK: AlgorithmSelectionDelegate
func selectAlgorithm(_ algorithm: Algorithm) {
self.algorithm = algorithm
algorithmLabel.text = algorithm.name
}
}
================================================
FILE: Tofu/Controllers/AccountSearchResultsViewController.swift
================================================
import UIKit
private let accountCellIdentifier = "AccountCell"
class AccountSearchResultsViewController: UITableViewController, AccountUpdateDelegate {
@IBOutlet var emptyView: UIView!
var accounts: [Account]! {
didSet {
tableView.reloadData()
tableView.backgroundView = accounts.count == 0 ? emptyView : nil
tableView.separatorStyle = accounts.count == 0 ? .none : .singleLine
}
}
override func viewDidLoad() {
super.viewDidLoad()
let updater = AccountsTableViewUpdater(tableView: tableView)
updater.startUpdating()
NotificationCenter.default.addObserver(
self,
selector: #selector(deselectSelectedTableViewRow),
name: UIMenuController.willHideMenuNotification,
object: nil)
}
@objc func deselectSelectedTableViewRow() {
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
}
// MARK: UITableViewDataSource
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
guard let cellSuperview = cell.superview else {
assertionFailure("The cell does not seem to be in the view hierarchy. How is that even possible!?")
return
}
let menuController = UIMenuController.shared
// If you tap the same cell twice, this condition prevents the menu from being
// hidden and then instantly shown again causing an unpleasant flash.
//
// Since the cell could already be the first responder (from previously showing
// its menu and then scrolling the table view) and the menu could already be
// visible for another cell, we make sure to check both values.
if !(cell.isFirstResponder && menuController.isMenuVisible) {
cell.becomeFirstResponder()
menuController.showMenu(from: cellSuperview, rect: cell.frame)
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return accounts.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: accountCellIdentifier,
for: indexPath) as! AccountCell
cell.account = accounts[indexPath.row]
cell.delegate = self
return cell
}
// MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView,
shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, canPerformAction action: Selector,
forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
return action == #selector(copy(_:))
}
override func tableView(_ tableView: UITableView, performAction action: Selector,
forRowAt indexPath: IndexPath, withSender sender: Any?) {
if action == #selector(copy(_:)) {
let cell = tableView.cellForRow(at: indexPath) as! AccountCell
cell.copy(self)
}
}
// MARK: AccountUpdateDelegate
func updateAccount(_ account: Account) {
(presentingViewController as! AccountUpdateDelegate).updateAccount(account)
let row = accounts.firstIndex { $0 === account }!
let indexPath = IndexPath(row: row, section: 0)
guard let cell = tableView.cellForRow(at: indexPath) as? AccountCell else { return }
cell.updateWithDate(Date())
}
}
================================================
FILE: Tofu/Controllers/AccountUpdateViewController.swift
================================================
import UIKit
class AccountUpdateViewController: UITableViewController {
@IBOutlet weak var nameField: UITextField!
@IBOutlet weak var issuerField: UITextField!
var delegate: AccountUpdateDelegate?
var account: Account!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
nameField.text = account.name
issuerField.text = account.issuer
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
account.name = nameField.text
account.issuer = issuerField.text
delegate?.updateAccount(account)
}
}
================================================
FILE: Tofu/Controllers/AccountsTableViewUpdater.swift
================================================
import UIKit
class AccountsTableViewUpdater: NSObject {
var tableView: UITableView
init(tableView: UITableView) {
self.tableView = tableView
}
func startUpdating() {
let timer = Timer(timeInterval: 1, target: self, selector: #selector(updateCells),
userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
}
@objc func updateCells() {
let now = Date()
for cell in tableView.visibleCells {
let accountCell = cell as! AccountCell
accountCell.updateWithDate(now)
}
}
}
================================================
FILE: Tofu/Controllers/AccountsViewController.swift
================================================
import UIKit
private let accountOrderKey = "persistentRefs"
class AccountsViewController: UITableViewController {
@IBOutlet weak var emptyView: UIView!
private let keychain = Keychain()
private var accounts = [Account]()
private lazy var searchController = makeSearchController()
private lazy var addAccountAlertController = makeAddAccountAlertController()
override func viewDidLoad() {
super.viewDidLoad()
accounts = keychain.accounts
let sortedPersistentRefs = UserDefaults.standard.array(forKey: accountOrderKey) as? [Data] ?? []
accounts.sort { a, b in
let aIndex = sortedPersistentRefs.firstIndex(of: a.persistentRef! as Data) ?? 0
let bIndex = sortedPersistentRefs.firstIndex(of: b.persistentRef! as Data) ?? 0
return aIndex < bIndex
}
persistAccountOrder()
navigationItem.searchController = searchController
let updater = AccountsTableViewUpdater(tableView: tableView)
updater.startUpdating()
updateEditing()
NotificationCenter.default.addObserver(
self,
selector: #selector(deselectSelectedTableViewRow),
name: UIMenuController.willHideMenuNotification,
object: nil)
}
@objc func deselectSelectedTableViewRow() {
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
}
@IBAction func addAccount(_ sender: Any) {
present(addAccountAlertController, animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let navigationController = segue.destination as? UINavigationController {
if let accountCreationViewController = navigationController.topViewController
as? AccountCreationViewController {
accountCreationViewController.delegate = self
} else {
let scanningViewController = navigationController.topViewController
as! ScanningViewController
scanningViewController.delegate = self
}
} else {
let accountUpdateViewController = segue.destination
as! AccountUpdateViewController
let cell = sender as! AccountCell
accountUpdateViewController.delegate = self
accountUpdateViewController.account = cell.account
}
}
private func makeSearchController() -> UISearchController {
let searchResultsController = storyboard!.instantiateViewController(withIdentifier: "AccountSearchResultsViewController") as! AccountSearchResultsViewController
let searchController = UISearchController(searchResultsController: searchResultsController)
searchController.searchResultsUpdater = self
return searchController
}
private func makeAddAccountAlertController() -> UIAlertController {
let title = "Add Account"
let message = "Add an account by scanning a QR code, importing a QR image, or entering a secret manually."
let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
let scanQRCode = UIAlertAction(title: "Scan QR Code", style: .default) { [unowned self] _ in
self.performSegue(withIdentifier: "ScanSegue", sender: self)
}
let importQRCode = UIAlertAction(title: "Import QR Image", style: .default) { [unowned self] _ in
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.allowsEditing = false
imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
} else {
presentErrorAlert(title: "Photo Library Empty",
message: "The photo library is empty and there are no images to import.")
}
}
let enterManually = UIAlertAction(title: "Enter Manually", style: .default) { [unowned self] _ in
self.performSegue(withIdentifier: "EnterManuallySegue", sender: self)
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(scanQRCode)
alertController.addAction(importQRCode)
alertController.addAction(enterManually)
alertController.addAction(cancel)
return alertController
}
private func persistAccountOrder() {
let sortedPersistentRefs = accounts.map { $0.persistentRef! }
UserDefaults.standard.set(sortedPersistentRefs, forKey: accountOrderKey)
}
private func updateEditing() {
if accounts.count == 0 {
tableView.backgroundView = emptyView
tableView.separatorStyle = .none
navigationItem.leftBarButtonItem = nil
setEditing(false, animated: true)
} else {
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
navigationItem.leftBarButtonItem = editButtonItem
}
}
// MARK: UITableViewDataSource
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) ->
Bool {
return true
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath,
to destinationIndexPath: IndexPath) {
accounts.insert(accounts.remove(at: sourceIndexPath.row),
at: destinationIndexPath.row)
persistAccountOrder()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return accounts.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AccountCell",
for: indexPath) as! AccountCell
cell.account = accounts[indexPath.row]
cell.delegate = self
return cell
}
override func tableView(
_ tableView: UITableView,
commit editingStyle: UITableViewCell.EditingStyle,
forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let alertController = UIAlertController(
title: "Deleting This Account Will Not Turn Off Two-Factor Authentication",
message: "Please make sure two-factor authentication is turned off in the issuer's sett" +
"ings before deleting this account to prevent being locked out.",
preferredStyle: .actionSheet)
let deleteAccountAction = UIAlertAction(title: "Delete Account", style: .destructive) { _ in
self.deleteAccountForRowAtIndexPath(indexPath)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(deleteAccountAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
}
private func deleteAccountForRowAtIndexPath(_ indexPath: IndexPath) {
let account = self.accounts[indexPath.row]
guard self.keychain.deleteAccount(account) else {
presentTryAgainAlertWithTitle(
"Could Not Delete Account",
message: "An error occurred when deleting the account from the keychain.") {
self.deleteAccountForRowAtIndexPath(indexPath)
}
return
}
accounts.remove(at: indexPath.row)
persistAccountOrder()
tableView.deleteRows(at: [indexPath], with: .automatic)
updateEditing()
}
// MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.isEditing {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) as? AccountCell {
performSegue(withIdentifier: "EditAccountSegue", sender: cell)
}
} else { // Not editing
if let cell = tableView.cellForRow(at: indexPath) {
guard let cellSuperview = cell.superview else {
assertionFailure("The cell does not seem to be in the view hierarchy. How is that even possible!?")
return
}
let menuController = UIMenuController.shared
// If you tap the same cell twice, this condition prevents the menu from being
// hidden and then instantly shown again causing an unpleasant flash.
//
// Since the cell could already be the first responder (from previously showing
// its menu and then scrolling the table view) and the menu could already be
// visible for another cell, we make sure to check both values.
if !(cell.isFirstResponder && menuController.isMenuVisible) {
cell.becomeFirstResponder()
menuController.showMenu(from: cellSuperview, rect: cell.frame)
}
}
}
}
override func tableView(_ tableView: UITableView,
shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, canPerformAction action: Selector,
forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
return action == #selector(copy(_:))
}
override func tableView(_ tableView: UITableView, performAction action: Selector,
forRowAt indexPath: IndexPath, withSender sender: Any?) {
if action == #selector(copy(_:)) {
let cell = tableView.cellForRow(at: indexPath) as! AccountCell
cell.copy(self)
}
}
}
extension AccountsViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let accountSearchResultsViewController = searchController.searchResultsController
as! AccountSearchResultsViewController
accountSearchResultsViewController.accounts = accounts.filter {
guard let string = searchController.searchBar.text else { return false }
return $0.description.range(of: string, options: .caseInsensitive, range: nil,
locale: nil) != nil
}
}
}
extension AccountsViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
dismiss(animated: true, completion: nil)
guard let selectedQRCode = info[UIImagePickerController.InfoKey.originalImage] as? UIImage,
let detector = CIDetector(ofType: CIDetectorTypeQRCode,
context: nil,
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]),
let ciImage = CIImage(image: selectedQRCode),
let features = detector.features(in: ciImage) as? [CIQRCodeFeature],
let messageString = features.first?.messageString else {
presentErrorAlert(title: "Could Not Detect QR Code",
message: "No QR code was detected in the provided image. Please try importing a different image.")
return
}
guard let qrCodeURL = URL(string: messageString),
let account = Account(url: qrCodeURL) else {
presentErrorAlert(title: "Invalid QR Code",
message: "The QR code detected in the provided image is invalid. Please try a different image.")
return
}
self.createAccount(account)
}
}
extension AccountsViewController: AccountCreationDelegate {
func createAccount(_ account: Account) {
guard keychain.insertAccount(account) else {
presentTryAgainAlertWithTitle(
"Could Not Create Account",
message: "An error occurred when inserting the account into the keychain.") {
self.createAccount(account)
}
return
}
accounts.append(account)
persistAccountOrder()
let lastRow = accounts.count - 1
let indexPaths = [IndexPath(row: lastRow, section: 0)]
tableView.insertRows(at: indexPaths, with: .automatic)
updateEditing()
}
}
extension AccountsViewController: AccountUpdateDelegate {
func updateAccount(_ account: Account) {
guard keychain.updateAccount(account) else {
presentTryAgainAlertWithTitle(
"Could Not Update Account",
message: "An error occurred when persisting the account updates to the keychain.") {
self.updateAccount(account)
}
return
}
let row = accounts.firstIndex { $0 === account }!
let indexPath = IndexPath(row: row, section: 0)
guard let cell = tableView.cellForRow(at: indexPath) as? AccountCell else { return }
cell.updateWithDate(Date())
}
private func presentTryAgainAlertWithTitle(_ title: String, message: String, handler: @escaping () -> Void) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let tryAgainAccountAction = UIAlertAction(title: "Try again", style: .default) { _ in
handler()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(tryAgainAccountAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
}
================================================
FILE: Tofu/Controllers/AlgorithmsViewController.swift
================================================
import UIKit
private let algorithmCellIdentifier = "AlgorithmCell"
class AlgorithmsViewController: UITableViewController {
var algorithms = [Algorithm]()
var selected: Algorithm!
var delegate: AlgorithmSelectionDelegate?
// MARK: UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return algorithms.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: algorithmCellIdentifier,
for: indexPath)
let algorithm = algorithms[indexPath.row]
cell.textLabel?.text = algorithm.name
cell.accessoryType = selected == algorithm ? .checkmark : .none
return cell
}
// MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let previouslySelectedCell = tableView.cellForRow(
at: IndexPath(row: algorithms.firstIndex(of: selected)!, section: 0))!
previouslySelectedCell.accessoryType = .none
let selectedCell = tableView.cellForRow(at: indexPath)!
selectedCell.accessoryType = .checkmark
selected = algorithms[indexPath.row]
delegate?.selectAlgorithm(selected)
}
}
================================================
FILE: Tofu/Controllers/ScanningViewController.swift
================================================
import UIKit
import AVFoundation
class ScanningViewController: UIViewController,
AVCaptureMetadataOutputObjectsDelegate {
@IBOutlet weak var allowCameraAccessView: UIView!
var delegate: AccountCreationDelegate?
private var session = AVCaptureSession()
private let output = AVCaptureMetadataOutput()
private var layer: AVCaptureVideoPreviewLayer?
@IBAction func didPressCancel(_ sender: UIBarButtonItem) {
output.setMetadataObjectsDelegate(nil, queue: nil)
presentingViewController?.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
startScanning()
} else {
AVCaptureDevice.requestAccess(for: .video) { granted in
guard granted else { return }
DispatchQueue.main.async {
self.startScanning()
}
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateLayerFrameAndOrientation()
}
private func startScanning() {
if let device = AVCaptureDevice.default(for: .video),
let input = try? AVCaptureDeviceInput(device: device) {
allowCameraAccessView.isHidden = true
navigationItem.prompt = "Point your camera at a QR code to scan it."
session.addInput(input)
session.addOutput(output)
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
output.metadataObjectTypes = [.qr]
layer = AVCaptureVideoPreviewLayer(session: session)
layer!.videoGravity = .resizeAspectFill
view.layer.addSublayer(layer!)
updateLayerFrameAndOrientation()
session.startRunning()
}
}
private func updateLayerFrameAndOrientation() {
layer?.frame = view.layer.bounds
switch UIDevice.current.orientation {
case .landscapeLeft:
layer?.connection?.videoOrientation = .landscapeRight
case .landscapeRight:
layer?.connection?.videoOrientation = .landscapeLeft
default:
layer?.connection?.videoOrientation = .portrait
}
}
// MARK: AVCaptureMetadataOutputObjectsDelegate
func metadataOutput(
_ output: AVCaptureMetadataOutput,
didOutput metadataObjects: [AVMetadataObject],
from connection: AVCaptureConnection) {
guard presentedViewController == nil, // Not presenting an error alert
metadataObjects.count > 0,
let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
metadataObject.type == .qr,
let urlString = metadataObject.stringValue else { return }
guard let url = URL(string: urlString),
let account = Account(url: url) else {
presentErrorAlert(title: "Invalid QR Code",
message: "The detected QR code is invalid. Please try scanning a different code.")
return
}
output.setMetadataObjectsDelegate(nil, queue: nil)
delegate?.createAccount(account)
presentingViewController?.dismiss(animated: true, completion: nil)
}
}
================================================
FILE: Tofu/Extensions/Data.swift
================================================
import Foundation
private enum DecodedByte {
case valid(UInt8)
case invalid
case padding
}
private let padding: UInt8 = 61 // =
private let byteMappings: [CountableRange<UInt8>] = [
65 ..< 91, // A-Z
50 ..< 56, // 2-7
]
private func decode(byte encodedByte: UInt8) -> DecodedByte {
if encodedByte == padding { return .padding }
var decodedStart: UInt8 = 0
for range in byteMappings {
if range.contains(encodedByte) {
let result = decodedStart + (encodedByte - range.lowerBound)
return .valid(result)
}
decodedStart += range.upperBound - range.lowerBound
}
return .invalid
}
private func decoded(bytes encodedBytes: [UInt8]) -> [UInt8]? {
var decodedBytes = [UInt8]()
decodedBytes.reserveCapacity(encodedBytes.count / 8 * 5)
var decodedByte: UInt8 = 0
var characterCount = 0
var paddingCount = 0
var index = 0
for encodedByte in encodedBytes {
let value: UInt8
switch decode(byte: encodedByte) {
case .valid(let v):
value = v
characterCount += 1
case .invalid:
return nil
case .padding:
paddingCount += 1
continue
}
// Only allow padding at the end of the sequence
if paddingCount > 0 { return nil }
switch index % 8 {
case 0:
decodedByte = value << 3
case 1:
decodedByte |= value >> 2
decodedBytes.append(decodedByte)
decodedByte = value << 6
case 2:
decodedByte |= value << 1
case 3:
decodedByte |= value >> 4
decodedBytes.append(decodedByte)
decodedByte = value << 4
case 4:
decodedByte |= value >> 1
decodedBytes.append(decodedByte)
decodedByte = value << 7
case 5:
decodedByte |= value << 2
case 6:
decodedByte |= value >> 3
decodedBytes.append(decodedByte)
decodedByte = value << 5
case 7:
decodedByte |= value
decodedBytes.append(decodedByte)
default:
fatalError()
}
index += 1
}
let characterCountIsValid = [0, 2, 4, 5, 7].contains(characterCount % 8)
let paddingCountIsValid = paddingCount == 0 || (characterCount + paddingCount) % 8 == 0
guard characterCountIsValid && paddingCountIsValid else { return nil }
return decodedBytes
}
extension Data {
init?(base32Encoded string: String) {
let encodedBytes = Array(string.uppercased().utf8)
guard let decodedBytes = decoded(bytes: encodedBytes) else { return nil }
self.init(decodedBytes)
}
/// Read a given type from a Data's buffer.
///
/// Very much like UnsafeRawBufferPointer's load(fromByteOffset:as:), but doesn't barf if the value
/// isn't at an aligned position.
///
/// - Parameters:
/// - offset: The offset to load the value from.
/// - type: The type of the value.
/// - Returns: The value loaded from the data.
func alignmentSafeLoad<T>(fromByteOffset offset: Int = 0, as type: T.Type) throws -> T {
guard count >= (offset + MemoryLayout<T>.size) else { throw NSError(domain: "com.calleerlandsson.Tofu", code: -1) }
let chunk = subdata(in: offset..<(offset + MemoryLayout<T>.size))
return chunk.withUnsafeBytes({ (bytePointer: UnsafeRawBufferPointer) -> T in
return bytePointer.load(as: T.self)
})
}
}
================================================
FILE: Tofu/Extensions/UIViewController.swift
================================================
import UIKit
extension UIViewController {
func presentErrorAlert(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
================================================
FILE: Tofu/Info.plist
================================================
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>com.calleerlandsson.Tofu.otpauth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>otpauth</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>TOFU_BUNDLE_VERSION</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Used to scan QR codes</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
================================================
FILE: Tofu/Models/Account.swift
================================================
import Foundation
@objc(Account) class Account: NSObject, NSSecureCoding {
static var supportsSecureCoding: Bool { return true }
/// This is a "pointer" to the account in the Keychain, and is set upon encode to/decode from such. It's not
/// included in serialisation or equality checks, since it's not required for exporting to/importing from from
/// elsewhere, and isn't useful for duplicate checking etc.
var persistentRef: Data?
var name: String?
var issuer: String?
var password = Password()
override init() {}
init?(url: URL) {
let label = url.path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
guard let host = url.host, host == "hotp" || host == "totp" else { return nil }
let labelComponents = label.components(separatedBy: ":")
guard labelComponents.count > 0,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems,
queryItems.count > 0
else { return nil }
name = labelComponents.last?.trimmingCharacters(in: CharacterSet.whitespaces)
issuer = labelComponents.count > 1 ? labelComponents.first : nil
password.timeBased = host == "totp"
for queryItem in queryItems {
switch queryItem.name {
case "secret":
guard let secretString = queryItem.value,
let secret = Data(base32Encoded: secretString)
else { break }
password.secret = secret
case "algorithm":
switch queryItem.value {
case .some("SHA256"): password.algorithm = .sha256
case .some("SHA512"): password.algorithm = .sha512
default: break
}
case "digits":
guard let string = queryItem.value, let digits = Int(string) else { break }
if digits < 6 || digits > 9 { return nil }
password.digits = digits
case "issuer": issuer = queryItem.value
case "counter":
guard let string = queryItem.value, let counter = Int(string) else { break }
password.counter = counter
case "period":
guard let string = queryItem.value, let period = Int(string) else { break }
if period < 1 { return nil }
password.period = period
default: break
}
}
if password.secret.count == 0 { return nil }
}
required init?(coder: NSCoder) {
guard let secret = coder.decodeObject(of: NSData.self, forKey: "secret") as? Data else { return nil }
guard coder.containsValue(forKey: "algorithm") else { return nil }
guard let algorithm = Algorithm(rawValue: coder.decodeInt32(forKey: "algorithm")) else { return nil }
password.algorithm = algorithm
password.secret = secret
password.digits = Int(coder.decodeInt32(forKey: "digits"))
password.timeBased = coder.decodeBool(forKey: "timeBased")
password.counter = Int(coder.decodeInt32(forKey: "counter"))
password.period = Int(coder.decodeInt32(forKey: "period"))
name = coder.decodeObject(of: NSString.self, forKey: "name") as? String
issuer = coder.decodeObject(of: NSString.self, forKey: "issuer") as? String
}
func encode(with coder: NSCoder) {
coder.encode(password.timeBased, forKey: "timeBased")
coder.encode(password.algorithm.rawValue, forKey: "algorithm")
coder.encode(Int32(password.digits), forKey: "digits")
coder.encode(password.secret, forKey: "secret")
coder.encode(Int32(password.counter), forKey: "counter")
coder.encode(Int32(password.period), forKey: "period")
coder.encode(name, forKey: "name")
coder.encode(issuer, forKey: "issuer")
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? Account else { return false }
return name == other.name && issuer == other.issuer && password == other.password
}
override var hash: Int {
var hasher = Hasher()
hasher.combine(name)
hasher.combine(issuer)
hasher.combine(password)
return hasher.finalize()
}
override var description: String {
guard let issuer = issuer, issuer.count > 0 else { return name ?? "" }
guard let name = name, name.count > 0 else { return issuer }
return "\(issuer) (\(name))"
}
}
================================================
FILE: Tofu/Models/Algorithm.swift
=================
gitextract_iqplvj7o/
├── .github/
│ └── ISSUE_TEMPLATE/
│ └── issuer-icon-request.md
├── .gitignore
├── AppIcon.sketch
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── GenerateIssuerIconAssets.sh
├── LICENSE
├── README.md
├── Tofu/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ ├── 17thShard.imageset/
│ │ │ └── Contents.json
│ │ ├── AWS.imageset/
│ │ │ └── Contents.json
│ │ ├── Adobe.imageset/
│ │ │ └── Contents.json
│ │ ├── Allegro.imageset/
│ │ │ └── Contents.json
│ │ ├── Amazon.imageset/
│ │ │ └── Contents.json
│ │ ├── AnonAddy.imageset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Atlassian.imageset/
│ │ │ └── Contents.json
│ │ ├── Backblaze.imageset/
│ │ │ └── Contents.json
│ │ ├── Basecamp.imageset/
│ │ │ └── Contents.json
│ │ ├── Binance.imageset/
│ │ │ └── Contents.json
│ │ ├── BitBay.imageset/
│ │ │ └── Contents.json
│ │ ├── Bitbucket.imageset/
│ │ │ └── Contents.json
│ │ ├── Bitstamp.imageset/
│ │ │ └── Contents.json
│ │ ├── Bittrex.imageset/
│ │ │ └── Contents.json
│ │ ├── Bitwarden.imageset/
│ │ │ └── Contents.json
│ │ ├── CircularProgressViewBorderThick.imageset/
│ │ │ └── Contents.json
│ │ ├── CircularProgressViewBorderThin.imageset/
│ │ │ └── Contents.json
│ │ ├── Cloudflare.imageset/
│ │ │ └── Contents.json
│ │ ├── Coinbase.imageset/
│ │ │ └── Contents.json
│ │ ├── Contentful.imageset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── CorporateTrust.imageset/
│ │ │ └── Contents.json
│ │ ├── CyDIS.imageset/
│ │ │ └── Contents.json
│ │ ├── DNSimple.imageset/
│ │ │ └── Contents.json
│ │ ├── DigitalOcean.imageset/
│ │ │ └── Contents.json
│ │ ├── Discord.imageset/
│ │ │ └── Contents.json
│ │ ├── Docker.imageset/
│ │ │ └── Contents.json
│ │ ├── Dropbox.imageset/
│ │ │ └── Contents.json
│ │ ├── ElectronicArts.imageset/
│ │ │ └── Contents.json
│ │ ├── EpicGames.imageset/
│ │ │ └── Contents.json
│ │ ├── Evernote.imageset/
│ │ │ └── Contents.json
│ │ ├── Facebook.imageset/
│ │ │ └── Contents.json
│ │ ├── FastMail.imageset/
│ │ │ └── Contents.json
│ │ ├── Fidelity.imageset/
│ │ │ └── Contents.json
│ │ ├── Figma.imageset/
│ │ │ └── Contents.json
│ │ ├── Firefox.imageset/
│ │ │ └── Contents.json
│ │ ├── Gandi.imageset/
│ │ │ └── Contents.json
│ │ ├── GitHub.imageset/
│ │ │ └── Contents.json
│ │ ├── GitLab.imageset/
│ │ │ └── Contents.json
│ │ ├── Gitea.imageset/
│ │ │ └── Contents.json
│ │ ├── GoDaddy.imageset/
│ │ │ └── Contents.json
│ │ ├── Google.imageset/
│ │ │ └── Contents.json
│ │ ├── GreenAddress.imageset/
│ │ │ └── Contents.json
│ │ ├── HEY.imageset/
│ │ │ └── Contents.json
│ │ ├── HackTheBox.imageset/
│ │ │ └── Contents.json
│ │ ├── Heroku.imageset/
│ │ │ └── Contents.json
│ │ ├── Hetzner.imageset/
│ │ │ └── Contents.json
│ │ ├── HomeAssistant.imageset/
│ │ │ └── Contents.json
│ │ ├── Honeybadger.imageset/
│ │ │ └── Contents.json
│ │ ├── Hostek.imageset/
│ │ │ └── Contents.json
│ │ ├── Hover.imageset/
│ │ │ └── Contents.json
│ │ ├── HumbleBundle.imageset/
│ │ │ └── Contents.json
│ │ ├── IDme.imageset/
│ │ │ └── Contents.json
│ │ ├── IFTTT.imageset/
│ │ │ └── Contents.json
│ │ ├── Instagram.imageset/
│ │ │ └── Contents.json
│ │ ├── Intercom.imageset/
│ │ │ └── Contents.json
│ │ ├── IssuerIcons/
│ │ │ └── Philips.imageset/
│ │ │ └── IssuerIcons
│ │ ├── JetBrains.imageset/
│ │ │ └── Contents.json
│ │ ├── Kickstarter.imageset/
│ │ │ └── Contents.json
│ │ ├── LastPass.imageset/
│ │ │ └── Contents.json
│ │ ├── LinkedIn.imageset/
│ │ │ └── Contents.json
│ │ ├── Linode.imageset/
│ │ │ └── Contents.json
│ │ ├── Lobsters.imageset/
│ │ │ └── Contents.json
│ │ ├── LocalBitcoins.imageset/
│ │ │ └── Contents.json
│ │ ├── Mailchimp.imageset/
│ │ │ └── Contents.json
│ │ ├── Mastodon.imageset/
│ │ │ └── Contents.json
│ │ ├── Mega.imageset/
│ │ │ └── Contents.json
│ │ ├── Microsoft.imageset/
│ │ │ └── Contents.json
│ │ ├── Name.com.imageset/
│ │ │ └── Contents.json
│ │ ├── Netlify.imageset/
│ │ │ └── Contents.json
│ │ ├── Nextcloud.imageset/
│ │ │ └── Contents.json
│ │ ├── NexusMods.imageset/
│ │ │ └── Contents.json
│ │ ├── NiceHash.imageset/
│ │ │ └── Contents.json
│ │ ├── Nintendo.imageset/
│ │ │ └── Contents.json
│ │ ├── Njalla.imageset/
│ │ │ └── Contents.json
│ │ ├── Nodecraft.imageset/
│ │ │ └── Contents.json
│ │ ├── NordPass.imageset/
│ │ │ └── Contents.json
│ │ ├── PaladinExtensions.imageset/
│ │ │ └── Contents.json
│ │ ├── Parler.imageset/
│ │ │ └── Contents.json
│ │ ├── PayPal.imageset/
│ │ │ └── Contents.json
│ │ ├── PhilipsHue.imageset/
│ │ │ └── Contents.json
│ │ ├── Posteo.imageset/
│ │ │ └── Contents.json
│ │ ├── Postmark.imageset/
│ │ │ └── Contents.json
│ │ ├── Privacy.imageset/
│ │ │ └── Contents.json
│ │ ├── ProfitBricks.imageset/
│ │ │ └── Contents.json
│ │ ├── ProtonMail.imageset/
│ │ │ └── Contents.json
│ │ ├── Prusa.imageset/
│ │ │ └── Contents.json
│ │ ├── PrusaAccount.imageset/
│ │ │ └── Contents.json
│ │ ├── Reddit.imageset/
│ │ │ └── Contents.json
│ │ ├── Robinhood.imageset/
│ │ │ └── Contents.json
│ │ ├── RubyGems.imageset/
│ │ │ └── Contents.json
│ │ ├── RuneScape.imageset/
│ │ │ └── Contents.json
│ │ ├── STACK.imageset/
│ │ │ └── Contents.json
│ │ ├── SimpleLogin.imageset/
│ │ │ └── Contents.json
│ │ ├── Slack.imageset/
│ │ │ └── Contents.json
│ │ ├── Snapchat.imageset/
│ │ │ └── Contents.json
│ │ ├── Sony.imageset/
│ │ │ └── Contents.json
│ │ ├── Squarespace.imageset/
│ │ │ └── Contents.json
│ │ ├── StandardNotes.imageset/
│ │ │ └── Contents.json
│ │ ├── Stripe.imageset/
│ │ │ └── Contents.json
│ │ ├── Surfshark.imageset/
│ │ │ └── Contents.json
│ │ ├── TETR.IO.imageset/
│ │ │ └── Contents.json
│ │ ├── Time4VPS.imageset/
│ │ │ └── Contents.json
│ │ ├── TorGuard.imageset/
│ │ │ └── Contents.json
│ │ ├── Tresorit.imageset/
│ │ │ └── Contents.json
│ │ ├── Tumblr.imageset/
│ │ │ └── Contents.json
│ │ ├── TurboTax.imageset/
│ │ │ └── Contents.json
│ │ ├── Tutanota.imageset/
│ │ │ └── Contents.json
│ │ ├── Tweakers.imageset/
│ │ │ └── Contents.json
│ │ ├── Twilio.imageset/
│ │ │ └── Contents.json
│ │ ├── Twitch.imageset/
│ │ │ └── Contents.json
│ │ ├── Twitter.imageset/
│ │ │ └── Contents.json
│ │ ├── Uber.imageset/
│ │ │ └── Contents.json
│ │ ├── Ubisoft.imageset/
│ │ │ └── Contents.json
│ │ ├── Unity.imageset/
│ │ │ └── Contents.json
│ │ ├── VKontakte.imageset/
│ │ │ └── Contents.json
│ │ ├── Wallabag.imageset/
│ │ │ └── Contents.json
│ │ ├── WordPress.imageset/
│ │ │ └── Contents.json
│ │ ├── YNAB.imageset/
│ │ │ └── Contents.json
│ │ ├── Zoom.imageset/
│ │ │ └── Contents.json
│ │ └── ownCloud.imageset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Controllers/
│ │ ├── AccountCreationViewController.swift
│ │ ├── AccountSearchResultsViewController.swift
│ │ ├── AccountUpdateViewController.swift
│ │ ├── AccountsTableViewUpdater.swift
│ │ ├── AccountsViewController.swift
│ │ ├── AlgorithmsViewController.swift
│ │ └── ScanningViewController.swift
│ ├── Extensions/
│ │ ├── Data.swift
│ │ └── UIViewController.swift
│ ├── Info.plist
│ ├── Models/
│ │ ├── Account.swift
│ │ ├── Algorithm.swift
│ │ ├── ExternalDataInterop.swift
│ │ ├── Keychain.swift
│ │ └── Password.swift
│ ├── Protocols/
│ │ ├── AccountCreationDelegate.swift
│ │ ├── AccountUpdateDelegate.swift
│ │ └── AlgorithmSelectionDelegate.swift
│ ├── Tofu.xcconfig
│ └── Views/
│ ├── AccountCell.swift
│ └── CircularProgressView.swift
├── Tofu.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
├── TofuTests/
│ ├── AccountTests.swift
│ ├── DataTests.swift
│ ├── Info.plist
│ └── PasswordTests.swift
└── TofuUITests/
├── Info.plist
└── TofuUITests.swift
Condensed preview — 163 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (286K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/issuer-icon-request.md",
"chars": 343,
"preview": "---\nname: Issuer Icon Request\nabout: Use this template when asking for an icon to be added to the app.\ntitle: Add an ico"
},
{
"path": ".gitignore",
"chars": 21,
"preview": "xcuserdata\n.DS_Store\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3233,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 657,
"preview": "# Contributing\n\nWe love pull requests from everyone. By participating in this project, you agree\nto abide by its [code o"
},
{
"path": "GenerateIssuerIconAssets.sh",
"chars": 1028,
"preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nget_name() {\n echo $1 | sed -E 's:.+/(.+)\\.png:\\1:'\n}\n\nwrite_json() {\n # JSON c"
},
{
"path": "LICENSE",
"chars": 731,
"preview": "Copyright (c) 2016 Calle Erlandsson\n\nPermission to use, copy, modify, and distribute this software for any\npurpose with "
},
{
"path": "README.md",
"chars": 2591,
"preview": "# Tofu\n\nAn easy-to-use, open-source two-factor authentication app designed specifically\nfor iOS.\n\nTofu generates one-tim"
},
{
"path": "Tofu/AppDelegate.swift",
"chars": 1473,
"preview": "import UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n var window: UIWindow?\n \n"
},
{
"path": "Tofu/Assets.xcassets/17thShard.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"17thShard.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/AWS.imageset/Contents.json",
"chars": 367,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"AWS.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Adobe.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Adobe.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Allegro.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Allegro.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Amazon.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Amazon.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/AnonAddy.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"AnonAddy.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 2141,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"20x20\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\""
},
{
"path": "Tofu/Assets.xcassets/Atlassian.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Atlassian.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Backblaze.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Backblaze.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Basecamp.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Basecamp.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Binance.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Binance.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/BitBay.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"BitBay.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Bitbucket.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Bitbucket.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Bitstamp.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Bitstamp.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Bittrex.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Bittrex.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Bitwarden.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Bitwarden.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/CircularProgressViewBorderThick.imageset/Contents.json",
"chars": 461,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "Tofu/Assets.xcassets/CircularProgressViewBorderThin.imageset/Contents.json",
"chars": 459,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "Tofu/Assets.xcassets/Cloudflare.imageset/Contents.json",
"chars": 388,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Cloudflare.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Coinbase.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Coinbase.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Contentful.imageset/Contents.json",
"chars": 388,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Contentful.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "Tofu/Assets.xcassets/CorporateTrust.imageset/Contents.json",
"chars": 400,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"CorporateTrust.png\",\n \"scale\" : \"1x\"\n },"
},
{
"path": "Tofu/Assets.xcassets/CyDIS.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"CyDIS.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/DNSimple.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"DNSimple.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/DigitalOcean.imageset/Contents.json",
"chars": 394,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"DigitalOcean.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Discord.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Discord.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Docker.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Docker.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Dropbox.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Dropbox.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/ElectronicArts.imageset/Contents.json",
"chars": 400,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"ElectronicArts.png\",\n \"scale\" : \"1x\"\n },"
},
{
"path": "Tofu/Assets.xcassets/EpicGames.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"EpicGames.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Evernote.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Evernote.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Facebook.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Facebook.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/FastMail.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"FastMail.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Fidelity.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Fidelity.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Figma.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Figma.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Firefox.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Firefox.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Gandi.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Gandi.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/GitHub.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"GitHub.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/GitLab.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"GitLab.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Gitea.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Gitea.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/GoDaddy.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"GoDaddy.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Google.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Google.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/GreenAddress.imageset/Contents.json",
"chars": 394,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"GreenAddress.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/HEY.imageset/Contents.json",
"chars": 367,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"HEY.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/HackTheBox.imageset/Contents.json",
"chars": 388,
"preview": "{\n \"images\" : [\n {\n \"filename\" : \"hackthebox.png\",\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Heroku.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Heroku.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Hetzner.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Hetzner.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/HomeAssistant.imageset/Contents.json",
"chars": 397,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"HomeAssistant.png\",\n \"scale\" : \"1x\"\n },\n"
},
{
"path": "Tofu/Assets.xcassets/Honeybadger.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Honeybadger.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Hostek.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Hostek.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Hover.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Hover.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/HumbleBundle.imageset/Contents.json",
"chars": 394,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"HumbleBundle.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/IDme.imageset/Contents.json",
"chars": 370,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"IDme.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/IFTTT.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"IFTTT.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Instagram.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Instagram.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Intercom.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Intercom.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/JetBrains.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"JetBrains.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Kickstarter.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Kickstarter.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/LastPass.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"LastPass.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/LinkedIn.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"LinkedIn.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Linode.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Linode.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Lobsters.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Lobsters.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/LocalBitcoins.imageset/Contents.json",
"chars": 397,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"LocalBitcoins.png\",\n \"scale\" : \"1x\"\n },\n"
},
{
"path": "Tofu/Assets.xcassets/Mailchimp.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Mailchimp.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Mastodon.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Mastodon.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Mega.imageset/Contents.json",
"chars": 370,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Mega.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Microsoft.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Microsoft.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Name.com.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Name.com.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Netlify.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Netlify.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Nextcloud.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Nextcloud.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/NexusMods.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"NexusMods.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/NiceHash.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"NiceHash.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Nintendo.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Nintendo.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Njalla.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Njalla.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Nodecraft.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Nodecraft.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/NordPass.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"NordPass.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/PaladinExtensions.imageset/Contents.json",
"chars": 409,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"PaladinExtensions.png\",\n \"scale\" : \"1x\"\n "
},
{
"path": "Tofu/Assets.xcassets/Parler.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Parler.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/PayPal.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"PayPal.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/PhilipsHue.imageset/Contents.json",
"chars": 388,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"PhilipsHue.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Posteo.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Posteo.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Postmark.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Postmark.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Privacy.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Privacy.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/ProfitBricks.imageset/Contents.json",
"chars": 394,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"ProfitBricks.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/ProtonMail.imageset/Contents.json",
"chars": 388,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"ProtonMail.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Prusa.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Prusa.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/PrusaAccount.imageset/Contents.json",
"chars": 394,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"PrusaAccount.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Reddit.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Reddit.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Robinhood.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Robinhood.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/RubyGems.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"RubyGems.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/RuneScape.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"RuneScape.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/STACK.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"STACK.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/SimpleLogin.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"SimpleLogin.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Slack.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Slack.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Snapchat.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Snapchat.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Sony.imageset/Contents.json",
"chars": 370,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Sony.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Squarespace.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Squarespace.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/StandardNotes.imageset/Contents.json",
"chars": 397,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"StandardNotes.png\",\n \"scale\" : \"1x\"\n },\n"
},
{
"path": "Tofu/Assets.xcassets/Stripe.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Stripe.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Surfshark.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Surfshark.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/TETR.IO.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"TETR.IO.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Time4VPS.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Time4VPS.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/TorGuard.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"TorGuard.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Tresorit.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Tresorit.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Tumblr.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Tumblr.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/TurboTax.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"TurboTax.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Tutanota.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Tutanota.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Tweakers.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Tweakers.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/Twilio.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Twilio.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Twitch.imageset/Contents.json",
"chars": 376,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Twitch.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Twitter.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Twitter.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Uber.imageset/Contents.json",
"chars": 370,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Uber.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Ubisoft.imageset/Contents.json",
"chars": 379,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Ubisoft.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "Tofu/Assets.xcassets/Unity.imageset/Contents.json",
"chars": 373,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Unity.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/VKontakte.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"VKontakte.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/Wallabag.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Wallabag.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Assets.xcassets/WordPress.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"WordPress.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "Tofu/Assets.xcassets/YNAB.imageset/Contents.json",
"chars": 370,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"YNAB.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/Zoom.imageset/Contents.json",
"chars": 370,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Zoom.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "Tofu/Assets.xcassets/ownCloud.imageset/Contents.json",
"chars": 382,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"ownCloud.png\",\n \"scale\" : \"1x\"\n },\n {"
},
{
"path": "Tofu/Base.lproj/LaunchScreen.storyboard",
"chars": 1663,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "Tofu/Base.lproj/Main.storyboard",
"chars": 78682,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "Tofu/Controllers/AccountCreationViewController.swift",
"chars": 4261,
"preview": "import UIKit\n\nprivate let formatter: NumberFormatter = {\n let formatter = NumberFormatter()\n formatter.numberStyle"
},
{
"path": "Tofu/Controllers/AccountSearchResultsViewController.swift",
"chars": 4032,
"preview": "import UIKit\n\nprivate let accountCellIdentifier = \"AccountCell\"\n\nclass AccountSearchResultsViewController: UITableViewCo"
},
{
"path": "Tofu/Controllers/AccountUpdateViewController.swift",
"chars": 669,
"preview": "import UIKit\n\nclass AccountUpdateViewController: UITableViewController {\n @IBOutlet weak var nameField: UITextField!\n"
},
{
"path": "Tofu/Controllers/AccountsTableViewUpdater.swift",
"chars": 635,
"preview": "import UIKit\n\nclass AccountsTableViewUpdater: NSObject {\n var tableView: UITableView\n \n init(tableView: UITable"
},
{
"path": "Tofu/Controllers/AccountsViewController.swift",
"chars": 14471,
"preview": "import UIKit\n\nprivate let accountOrderKey = \"persistentRefs\"\n\nclass AccountsViewController: UITableViewController {\n "
},
{
"path": "Tofu/Controllers/AlgorithmsViewController.swift",
"chars": 1609,
"preview": "import UIKit\n\nprivate let algorithmCellIdentifier = \"AlgorithmCell\"\n\nclass AlgorithmsViewController: UITableViewControll"
},
{
"path": "Tofu/Controllers/ScanningViewController.swift",
"chars": 3389,
"preview": "import UIKit\nimport AVFoundation\n\nclass ScanningViewController: UIViewController,\nAVCaptureMetadataOutputObjectsDelegate"
},
{
"path": "Tofu/Extensions/Data.swift",
"chars": 3583,
"preview": "import Foundation\n\nprivate enum DecodedByte {\n case valid(UInt8)\n case invalid\n case padding\n}\n\nprivate let pad"
},
{
"path": "Tofu/Extensions/UIViewController.swift",
"chars": 380,
"preview": "import UIKit\n\nextension UIViewController {\n func presentErrorAlert(title: String, message: String) {\n let aler"
},
{
"path": "Tofu/Info.plist",
"chars": 1952,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Tofu/Models/Account.swift",
"chars": 4575,
"preview": "import Foundation\n\n@objc(Account) class Account: NSObject, NSSecureCoding {\n static var supportsSecureCoding: Bool { "
},
{
"path": "Tofu/Models/Algorithm.swift",
"chars": 809,
"preview": "import Foundation\nimport CommonCrypto\n\nenum Algorithm: Int32 {\n case sha1 = 0\n case sha256 = 1\n case sha512 = 2"
},
{
"path": "Tofu/Models/ExternalDataInterop.swift",
"chars": 6482,
"preview": "import Foundation\nimport CryptoKit\nimport CommonCrypto\n\nclass ExternalDataInterop {\n\n enum ExternalDataInteropError: "
},
{
"path": "Tofu/Models/Keychain.swift",
"chars": 4925,
"preview": "import Foundation\n\nprivate enum KeychainEncodingVersion: UInt8 {\n case version1 = 1\n case version2 = 2\n}\n\nprivate "
},
{
"path": "Tofu/Models/Password.swift",
"chars": 2665,
"preview": "import Foundation\nimport CommonCrypto\n\nclass Password: Equatable, Hashable {\n\n static func == (lhs: Password, rhs: Pa"
},
{
"path": "Tofu/Protocols/AccountCreationDelegate.swift",
"chars": 91,
"preview": "protocol AccountCreationDelegate: AnyObject {\n func createAccount(_ account: Account)\n}\n"
},
{
"path": "Tofu/Protocols/AccountUpdateDelegate.swift",
"chars": 89,
"preview": "protocol AccountUpdateDelegate: AnyObject {\n func updateAccount(_ account: Account)\n}\n"
},
{
"path": "Tofu/Protocols/AlgorithmSelectionDelegate.swift",
"chars": 100,
"preview": "protocol AlgorithmSelectionDelegate: AnyObject {\n func selectAlgorithm(_ algorithm: Algorithm)\n}\n"
},
{
"path": "Tofu/Tofu.xcconfig",
"chars": 142,
"preview": "// Configuration settings file format documentation can be found at:\n// https://help.apple.com/xcode/#/dev745c5c974\n\nMAR"
},
{
"path": "Tofu/Views/AccountCell.swift",
"chars": 8915,
"preview": "import UIKit\n\nprivate struct CaseInsensitiveString: Hashable, ExpressibleByStringLiteral {\n let value: String\n\n in"
},
{
"path": "Tofu/Views/CircularProgressView.swift",
"chars": 1610,
"preview": "import UIKit\n\nclass CircularProgressView: UIView {\n var progress: Double = 0 {\n didSet {\n maskLayer"
},
{
"path": "Tofu.xcodeproj/project.pbxproj",
"chars": 34407,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "Tofu.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 149,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:Tofu.xcodeproj\""
},
{
"path": "Tofu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "TofuTests/AccountTests.swift",
"chars": 5143,
"preview": "import XCTest\n@testable import Tofu\n\nclass AccountTests: XCTestCase {\n func testInitWithURL() {\n var account ="
},
{
"path": "TofuTests/DataTests.swift",
"chars": 2502,
"preview": "import XCTest\n@testable import Tofu\n\nclass DataTests: XCTestCase {\n func testInitBase32Encoded() {\n let exampl"
},
{
"path": "TofuTests/Info.plist",
"chars": 733,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "TofuTests/PasswordTests.swift",
"chars": 4560,
"preview": "import XCTest\n@testable import Tofu\n\nclass PasswordTests: XCTestCase {\n func testValueForDate() {\n let secret "
},
{
"path": "TofuUITests/Info.plist",
"chars": 733,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "TofuUITests/TofuUITests.swift",
"chars": 1247,
"preview": "//\n// TofuUITests.swift\n// TofuUITests\n//\n// Created by Calle Erlandsson on 20/01/16.\n// Copyright © 2016 Calle Erla"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the calleluks/Tofu GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 163 files (247.3 KB), approximately 74.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.