├── Lannister
├── Assets.xcassets
│ ├── Contents.json
│ ├── logo.imageset
│ │ ├── logo.png
│ │ ├── logo@2x.png
│ │ ├── logo@3x.png
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── AppIcon-2.png
│ │ ├── AppIcon-3.png
│ │ ├── AppIcon.png
│ │ ├── AppIcon copy-1.png
│ │ ├── AppIcon copy-2.png
│ │ ├── AppIcon copy-3.png
│ │ ├── AppIcon copy-4.png
│ │ ├── AppIcon copy-5.png
│ │ ├── AppIcon copy-6.png
│ │ ├── AppIcon copy-7.png
│ │ ├── AppIcon copy-8.png
│ │ ├── AppIcon copy-9.png
│ │ ├── AppIcon copy.png
│ │ ├── AppIcon copy-10.png
│ │ ├── AppIcon copy-11.png
│ │ ├── AppIcon copy-12.png
│ │ ├── AppIcon copy-13.png
│ │ ├── AppIcon copy-14.png
│ │ └── Contents.json
│ ├── arrow-up.imageset
│ │ ├── arrow-up.png
│ │ ├── arrow-up@2x.png
│ │ ├── arrow-up@3x.png
│ │ └── Contents.json
│ ├── checkmark.imageset
│ │ ├── checkmark.png
│ │ ├── checkmark@2x.png
│ │ ├── checkmark@3x.png
│ │ └── Contents.json
│ ├── donations.imageset
│ │ ├── donations.png
│ │ ├── donations@2x.png
│ │ ├── donations@3x.png
│ │ └── Contents.json
│ ├── hand-down.imageset
│ │ ├── hand-down.png
│ │ ├── hand-down@2x.png
│ │ ├── hand-down@3x.png
│ │ └── Contents.json
│ ├── settings.imageset
│ │ ├── settings.png
│ │ ├── settings@2x.png
│ │ ├── settings@3x.png
│ │ └── Contents.json
│ ├── arrow-down.imageset
│ │ ├── arrow-down.png
│ │ ├── arrow-down@2x.png
│ │ ├── arrow-down@3x.png
│ │ └── Contents.json
│ ├── arrow-sort.imageset
│ │ ├── arrow-sort.png
│ │ ├── arrow-sort@2x.png
│ │ ├── arrow-sort@3x.png
│ │ └── Contents.json
│ ├── scan-square.imageset
│ │ ├── scan-square.png
│ │ ├── scan-square@2x.png
│ │ ├── scan-square@3x.png
│ │ └── Contents.json
│ ├── cell-dropdown.imageset
│ │ ├── cell-dropdown.png
│ │ ├── cell-dropdown@2x.png
│ │ ├── cell-dropdown@3x.png
│ │ └── Contents.json
│ ├── cell-eth-scan.imageset
│ │ ├── cell-eth-scan.png
│ │ ├── cell-eth-scan@2x.png
│ │ ├── cell-eth-scan@3x.png
│ │ └── Contents.json
│ ├── cell-indicator.imageset
│ │ ├── cell-indicator.png
│ │ ├── cell-indicator@2x.png
│ │ ├── cell-indicator@3x.png
│ │ └── Contents.json
│ ├── settings-sync.imageset
│ │ ├── settings-sync.png
│ │ ├── settings-sync@2x.png
│ │ ├── settings-sync@3x.png
│ │ └── Contents.json
│ ├── settings-export.imageset
│ │ ├── settings-export.png
│ │ ├── settings-export@2x.png
│ │ ├── settings-export@3x.png
│ │ └── Contents.json
│ ├── settings-github.imageset
│ │ ├── settings-github.png
│ │ ├── settings-github@2x.png
│ │ ├── settings-github@3x.png
│ │ └── Contents.json
│ ├── settings-discord.imageset
│ │ ├── settings-discord.png
│ │ ├── settings-discord@2x.png
│ │ ├── settings-discord@3x.png
│ │ └── Contents.json
│ ├── settings-twitter.imageset
│ │ ├── settings-twitter.png
│ │ ├── settings-twitter@2x.png
│ │ ├── settings-twitter@3x.png
│ │ └── Contents.json
│ ├── scan-close-button.imageset
│ │ ├── scan-close-button.png
│ │ ├── scan-close-button@2x.png
│ │ ├── scan-close-button@3x.png
│ │ └── Contents.json
│ ├── settings-currency.imageset
│ │ ├── settings-currency.png
│ │ ├── settings-currency@2x.png
│ │ ├── settings-currency@3x.png
│ │ └── Contents.json
│ ├── settings-passcode.imageset
│ │ ├── settings-passcode.png
│ │ ├── settings-passcode@2x.png
│ │ ├── settings-passcode@3x.png
│ │ └── Contents.json
│ ├── cell-eth-clipboard.imageset
│ │ ├── cell-eth-clipboard.png
│ │ ├── cell-eth-clipboard@2x.png
│ │ ├── cell-eth-clipboard@3x.png
│ │ └── Contents.json
│ └── settings-fingerprint.imageset
│ │ ├── settings-fingerprint.png
│ │ ├── settings-fingerprint@2x.png
│ │ ├── settings-fingerprint@3x.png
│ │ └── Contents.json
├── Data
│ ├── Database
│ │ ├── Models
│ │ │ └── Generated
│ │ │ │ ├── CurrencyManagedObject.swift
│ │ │ │ ├── _HoldingManagedObject.swift
│ │ │ │ ├── _TransactionManagedObject.swift
│ │ │ │ ├── __TransactionManagedObject.swift
│ │ │ │ ├── _CurrencyManagedObject.swift
│ │ │ │ └── __HoldingManagedObject.swift
│ │ └── db.xcdatamodeld
│ │ │ ├── .xccurrentversion
│ │ │ ├── db 2.xcdatamodel
│ │ │ └── contents
│ │ │ └── db.xcdatamodel
│ │ │ └── contents
│ ├── Utils
│ │ └── ERC20ContractsList.swift
│ ├── API
│ │ ├── Services
│ │ │ ├── BaseApiService.swift
│ │ │ ├── EtherScanApiService.swift
│ │ │ ├── CurrencyApiService.swift
│ │ │ └── BlockstackApiService.swift
│ │ └── APIManager.swift
│ ├── Mappers
│ │ ├── CurrencyDto.swift
│ │ ├── TransactionDto.swift
│ │ ├── TokenDto.swift
│ │ └── HoldingDto.swift
│ ├── Repositories Implementations
│ │ ├── PortfolioRepositoryImpl.swift
│ │ ├── HoldingsRepositoryImpl.swift
│ │ └── WalletRepositoryImpl.swift
│ └── User defaults
│ │ └── CurrencyUserDefaults.swift
├── Domain
│ ├── Repositories
│ │ ├── Repository.swift
│ │ ├── PortfolioRepository.swift
│ │ ├── HoldingsRepository.swift
│ │ └── WalletRepository.swift
│ ├── Entities
│ │ ├── Currency.swift
│ │ ├── Transaction.swift
│ │ ├── Token.swift
│ │ └── Holding.swift
│ └── Use cases
│ │ ├── PortfolioUseCase.swift
│ │ ├── HoldingsUseCase.swift
│ │ └── WalletUseCase.swift
├── App
│ ├── Views
│ │ ├── ETHCell.swift
│ │ ├── HoldingNameCell.swift
│ │ ├── HoldingHeaderView.swift
│ │ ├── TransactionTypeCell.swift
│ │ ├── TransactionNameCell.swift
│ │ ├── ColorCodeCell.swift
│ │ ├── HoldingTopCell.swift
│ │ ├── CurrencyCell.swift
│ │ ├── DashboardTopCell.swift
│ │ ├── DonationCell.swift
│ │ ├── TransactionCell.swift
│ │ ├── ColorCodePresetCell.swift
│ │ ├── ColorCodeCustomCell.swift
│ │ ├── HoldingCell.swift
│ │ ├── DashboardHeaderView.swift
│ │ ├── SettingsCell.swift
│ │ └── TotalValueCell.swift
│ ├── Controllers
│ │ ├── LaunchController.swift
│ │ ├── RootController.swift
│ │ ├── CustomQRCodeController.swift
│ │ ├── CurrenciesController.swift
│ │ ├── DonationsController.swift
│ │ ├── AuthController.swift
│ │ ├── ETHCreateHoldingController.swift
│ │ ├── ColorCodesController.swift
│ │ ├── CreateTransactionController.swift
│ │ └── HoldingController.swift
│ └── Utilities
│ │ ├── NumberFormatter.swift
│ │ ├── Parser.swift
│ │ ├── Currencies.swift
│ │ ├── Colors.swift
│ │ └── BioAccessController.swift
├── Info.plist
├── Info-dev.plist
└── Base.lproj
│ └── LaunchScreen.storyboard
├── Lannister.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── andresousa.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── xcshareddata
│ └── xcschemes
│ └── Lannister Beta.xcscheme
├── .gitignore
├── LannisterTests
├── Info.plist
└── LannisterTests.swift
├── LannisterUITests
├── Info.plist
└── LannisterUITests.swift
├── Podfile
├── README.md
└── Podfile.lock
/Lannister/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/logo.imageset/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/logo.imageset/logo.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/logo.imageset/logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/logo.imageset/logo@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/logo.imageset/logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/logo.imageset/logo@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon-2.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon-3.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-up.imageset/arrow-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-up.imageset/arrow-up.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/checkmark.imageset/checkmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/checkmark.imageset/checkmark.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/donations.imageset/donations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/donations.imageset/donations.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/hand-down.imageset/hand-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/hand-down.imageset/hand-down.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings.imageset/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings.imageset/settings.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-down.imageset/arrow-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-down.imageset/arrow-down.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-sort.imageset/arrow-sort.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-sort.imageset/arrow-sort.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-up.imageset/arrow-up@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-up.imageset/arrow-up@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-up.imageset/arrow-up@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-up.imageset/arrow-up@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings.imageset/settings@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings.imageset/settings@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings.imageset/settings@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings.imageset/settings@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-1.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-2.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-3.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-4.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-5.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-6.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-7.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-8.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-9.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-down.imageset/arrow-down@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-down.imageset/arrow-down@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-down.imageset/arrow-down@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-down.imageset/arrow-down@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-sort.imageset/arrow-sort@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-sort.imageset/arrow-sort@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-sort.imageset/arrow-sort@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/arrow-sort.imageset/arrow-sort@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/checkmark.imageset/checkmark@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/checkmark.imageset/checkmark@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/checkmark.imageset/checkmark@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/checkmark.imageset/checkmark@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/donations.imageset/donations@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/donations.imageset/donations@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/donations.imageset/donations@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/donations.imageset/donations@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/hand-down.imageset/hand-down@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/hand-down.imageset/hand-down@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/hand-down.imageset/hand-down@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/hand-down.imageset/hand-down@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-square.imageset/scan-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/scan-square.imageset/scan-square.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-10.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-11.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-12.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-13.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/AppIcon.appiconset/AppIcon copy-14.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-square.imageset/scan-square@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/scan-square.imageset/scan-square@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-square.imageset/scan-square@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/scan-square.imageset/scan-square@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-dropdown.imageset/cell-dropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-dropdown.imageset/cell-dropdown.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-scan.imageset/cell-eth-scan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-eth-scan.imageset/cell-eth-scan.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-indicator.imageset/cell-indicator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-indicator.imageset/cell-indicator.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-sync.imageset/settings-sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-sync.imageset/settings-sync.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-dropdown.imageset/cell-dropdown@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-dropdown.imageset/cell-dropdown@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-dropdown.imageset/cell-dropdown@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-dropdown.imageset/cell-dropdown@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-scan.imageset/cell-eth-scan@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-eth-scan.imageset/cell-eth-scan@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-scan.imageset/cell-eth-scan@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-eth-scan.imageset/cell-eth-scan@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-export.imageset/settings-export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-export.imageset/settings-export.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-github.imageset/settings-github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-github.imageset/settings-github.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-sync.imageset/settings-sync@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-sync.imageset/settings-sync@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-sync.imageset/settings-sync@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-sync.imageset/settings-sync@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-indicator.imageset/cell-indicator@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-indicator.imageset/cell-indicator@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-indicator.imageset/cell-indicator@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-indicator.imageset/cell-indicator@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-discord.imageset/settings-discord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-discord.imageset/settings-discord.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-export.imageset/settings-export@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-export.imageset/settings-export@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-export.imageset/settings-export@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-export.imageset/settings-export@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-github.imageset/settings-github@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-github.imageset/settings-github@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-github.imageset/settings-github@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-github.imageset/settings-github@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-twitter.imageset/settings-twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-twitter.imageset/settings-twitter.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-close-button.imageset/scan-close-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/scan-close-button.imageset/scan-close-button.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-currency.imageset/settings-currency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-currency.imageset/settings-currency.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-discord.imageset/settings-discord@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-discord.imageset/settings-discord@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-discord.imageset/settings-discord@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-discord.imageset/settings-discord@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-passcode.imageset/settings-passcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-passcode.imageset/settings-passcode.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-twitter.imageset/settings-twitter@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-twitter.imageset/settings-twitter@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-twitter.imageset/settings-twitter@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-twitter.imageset/settings-twitter@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-clipboard.imageset/cell-eth-clipboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-eth-clipboard.imageset/cell-eth-clipboard.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-close-button.imageset/scan-close-button@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/scan-close-button.imageset/scan-close-button@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-close-button.imageset/scan-close-button@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/scan-close-button.imageset/scan-close-button@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-currency.imageset/settings-currency@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-currency.imageset/settings-currency@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-currency.imageset/settings-currency@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-currency.imageset/settings-currency@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-passcode.imageset/settings-passcode@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-passcode.imageset/settings-passcode@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-passcode.imageset/settings-passcode@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-passcode.imageset/settings-passcode@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-clipboard.imageset/cell-eth-clipboard@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-eth-clipboard.imageset/cell-eth-clipboard@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-clipboard.imageset/cell-eth-clipboard@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/cell-eth-clipboard.imageset/cell-eth-clipboard@3x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-fingerprint.imageset/settings-fingerprint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-fingerprint.imageset/settings-fingerprint.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-fingerprint.imageset/settings-fingerprint@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-fingerprint.imageset/settings-fingerprint@2x.png
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-fingerprint.imageset/settings-fingerprint@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lannister-capital/lannister-ios/HEAD/Lannister/Assets.xcassets/settings-fingerprint.imageset/settings-fingerprint@3x.png
--------------------------------------------------------------------------------
/Lannister/Data/Database/Models/Generated/CurrencyManagedObject.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @objc(CurrencyManagedObject)
4 | open class CurrencyManagedObject: _CurrencyManagedObject {
5 | // Custom logic goes here.
6 | }
7 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/Models/Generated/_HoldingManagedObject.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @objc(.HoldingManagedObject)
4 | open class .HoldingManagedObject: _.HoldingManagedObject {
5 | // Custom logic goes here.
6 | }
7 |
--------------------------------------------------------------------------------
/Lannister.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/Models/Generated/_TransactionManagedObject.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @objc(.TransactionManagedObject)
4 | open class .TransactionManagedObject: _.TransactionManagedObject {
5 | // Custom logic goes here.
6 | }
7 |
--------------------------------------------------------------------------------
/Lannister/Domain/Repositories/Repository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Repository.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 24/06/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol Repository {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Lannister.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/db.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | db 2.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Lannister/App/Views/ETHCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ETHCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 08/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ETHCell: UITableViewCell {
12 |
13 | @IBOutlet weak var addressLabel : UILabel!
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Lannister/App/Views/HoldingNameCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingNameCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 29/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HoldingNameCell: UITableViewCell {
12 |
13 | @IBOutlet weak var holdingNameTextField : UITextField!
14 | }
15 |
--------------------------------------------------------------------------------
/Lannister/Domain/Repositories/PortfolioRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PortfolioRepository.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 24/06/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol PortfolioRepository : Repository {
12 |
13 | func getEuroTotalValue() -> Double
14 | }
15 |
--------------------------------------------------------------------------------
/Lannister/App/Views/HoldingHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingHeaderView.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 01/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HoldingHeaderView: UICollectionReusableView {
12 |
13 | @IBOutlet weak var valueLabel : UILabel!
14 | }
15 |
--------------------------------------------------------------------------------
/Lannister/App/Views/TransactionTypeCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TransactionTypeCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 08/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TransactionTypeCell: UITableViewCell {
12 |
13 | @IBOutlet weak var transactionTypeTextField : UITextField!
14 | }
15 |
--------------------------------------------------------------------------------
/Lannister/App/Views/TransactionNameCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TransactionNameCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 06/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TransactionNameCell: UITableViewCell {
12 |
13 | @IBOutlet weak var transactionNameTextField : UITextField!
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Lannister/App/Views/ColorCodeCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorCodeCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 29/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ColorCodeCell: UITableViewCell {
12 |
13 | @IBOutlet weak var colorCodeView : UIView!
14 | @IBOutlet weak var colorCodeLabel : UILabel!
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/App/Views/HoldingTopCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingTopCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 14/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HoldingTopCell: UITableViewCell {
12 |
13 | @IBOutlet weak var addButton : UIButton!
14 | @IBOutlet weak var poweredByLabel : UILabel!
15 | }
16 |
--------------------------------------------------------------------------------
/Lannister/App/Views/CurrencyCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CurrencyCell.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 09/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CurrencyCell: UITableViewCell {
12 |
13 | @IBOutlet weak var currencyNameLabel : UILabel!
14 | @IBOutlet weak var currencySymbolLabel : UILabel!
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/Data/Utils/ERC20ContractsList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ERC20List.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 11/11/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | enum ERC20ContractsList : String {
12 | case dai = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"
13 | case cDai = "0xf5dce57282a584d2746faf1593d3121fcac444dc"
14 | }
15 |
--------------------------------------------------------------------------------
/Lannister/App/Views/DashboardTopCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DashboardTopCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 11/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DashboardTopCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var sortButton : UIButton!
14 | @IBOutlet weak var addButton : UIButton!
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/App/Views/DonationCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DonationCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 14/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DonationCell: UITableViewCell {
12 |
13 | @IBOutlet weak var currencyLabel : UILabel!
14 | @IBOutlet weak var addressLabel : UILabel!
15 | @IBOutlet weak var copyLabel : UILabel!
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/App/Views/TransactionCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TransactionCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 01/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TransactionCell: UITableViewCell {
12 |
13 | @IBOutlet weak var colorView : UIView!
14 | @IBOutlet weak var nameLabel : UILabel!
15 | @IBOutlet weak var valueLabel : UILabel!
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/App/Views/ColorCodePresetCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorCodePresetCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 01/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ColorCodePresetCell: UITableViewCell {
12 |
13 | @IBOutlet weak var colorCodeView : UIView!
14 | @IBOutlet weak var colorCodeLabel : UILabel!
15 | @IBOutlet weak var checkmarkImageView : UIImageView!
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/Domain/Repositories/HoldingsRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingsRepository.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 18/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol HoldingsRepository: Repository {
12 |
13 | func updateHoldingsWithComputedProperties(holdings: [Holding]) -> [Holding]
14 | func updateHoldingWithComputedProperties(holding: Holding) -> Holding
15 | }
16 |
--------------------------------------------------------------------------------
/Lannister/App/Views/ColorCodeCustomCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorCodeCustomCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 01/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ColorCodeCustomCell: UITableViewCell {
12 |
13 | @IBOutlet weak var colorCodeView : UIView!
14 | @IBOutlet weak var colorCodeTextField : UITextField!
15 | @IBOutlet weak var checkmarkImageView : UIImageView!
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/App/Views/HoldingCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 27/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HoldingCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var colorView : UIView!
14 | @IBOutlet weak var nameLabel : UILabel!
15 | @IBOutlet weak var valueLabel : UILabel!
16 | @IBOutlet weak var percentageLabel : UILabel!
17 | }
18 |
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "logo@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "logo@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Data/API/Services/BaseApiService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseApiService.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 08/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 | import CoreData
12 | import Groot
13 |
14 | class BaseApiService: NSObject {
15 |
16 | let currencyBaseUrl = "https://api.exchangeratesapi.io"
17 | let currencyCryptoBaseUrl = "https://api.cryptonator.com/api/ticker"
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Lannister/Domain/Entities/Currency.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Currency.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 08/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Currency {
12 |
13 | var code : String!
14 | var name : String!
15 | var symbol : String!
16 | var euroRate : Double!
17 | var holdings : [Holding]!
18 |
19 | init(with dictionary: [String : Any]?) {
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Lannister/App/Views/DashboardHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DashboardHeaderView.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 27/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Charts
11 |
12 | class DashboardHeaderView: UICollectionReusableView {
13 |
14 | @IBOutlet weak var numberOfHoldingsLabel : UILabel!
15 | @IBOutlet weak var totalValueLabel : UILabel!
16 | @IBOutlet weak var pieChartView : PieChartView!
17 | }
18 |
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-up.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "arrow-up.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "arrow-up@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "arrow-up@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/checkmark.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "checkmark.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "checkmark@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "checkmark@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/donations.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "donations.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "donations@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "donations@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/hand-down.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "hand-down.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "hand-down@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "hand-down@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-down.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "arrow-down.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "arrow-down@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "arrow-down@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/arrow-sort.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "arrow-sort.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "arrow-sort@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "arrow-sort@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-square.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "scan-square.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "scan-square@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "scan-square@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-dropdown.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cell-dropdown.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "cell-dropdown@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "cell-dropdown@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-scan.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cell-eth-scan.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "cell-eth-scan@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "cell-eth-scan@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-indicator.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cell-indicator.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "cell-indicator@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "cell-indicator@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-sync.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-sync.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-sync@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-sync@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-export.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-export.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-export@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-export@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-github.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-github.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-github@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-github@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-discord.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-discord.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-discord@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-discord@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-twitter.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-twitter.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-twitter@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-twitter@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/scan-close-button.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "scan-close-button.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "scan-close-button@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "scan-close-button@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-currency.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-currency.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-currency@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-currency@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-passcode.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-passcode.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-passcode@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-passcode@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/Domain/Entities/Transaction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Transaction.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 05/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Transaction {
12 |
13 | var identifier : String!
14 | var name : String!
15 | var type : String!
16 | var value : Double!
17 | var holding : Holding?
18 | var token : Token?
19 |
20 | init(with dictionary: [String : Any]?) {
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/cell-eth-clipboard.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cell-eth-clipboard.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "cell-eth-clipboard@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "cell-eth-clipboard@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister/App/Views/SettingsCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 02/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SettingsCell: UITableViewCell {
12 |
13 | @IBOutlet weak var logo : UIImageView!
14 | @IBOutlet weak var nameLabel : UILabel!
15 | @IBOutlet weak var cellIndicator : UIImageView!
16 | @IBOutlet weak var currencyLabel : UILabel!
17 | @IBOutlet weak var bioAccessSwitch : UISwitch!
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/settings-fingerprint.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "settings-fingerprint.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "settings-fingerprint@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "settings-fingerprint@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Lannister.xcodeproj/xcuserdata/andresousa.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Lannister Beta.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | Lannister.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 27
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Lannister/Domain/Entities/Token.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Token.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 15/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Token {
12 |
13 | // attributes
14 | var address : String!
15 | var name : String?
16 | var code : String?
17 | var value : Double!
18 |
19 | var currency : Currency!
20 | var transactions : [Transaction]?
21 |
22 | // relationships
23 | init(with dictionary: [String : Any]?) {
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Lannister/Domain/Use cases/PortfolioUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PortfolioUseCase.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 24/06/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class PortfolioUseCase : NSObject {
12 |
13 | var repository: Repository?
14 |
15 | convenience init(with repository: Repository) {
16 | self.init()
17 | self.repository = repository
18 | }
19 |
20 | func getEuroTotalValue() -> Double {
21 |
22 | let repo = self.repository as! PortfolioRepository
23 | return repo.getEuroTotalValue()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Lannister/App/Views/TotalValueCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TotalValueCell.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 29/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TotalValueCell: UITableViewCell {
12 |
13 | @IBOutlet weak var titleLabel : UILabel!
14 | @IBOutlet weak var totalValueTextField : UITextField!
15 | @IBOutlet weak var percentageLabel : UILabel!
16 | @IBOutlet weak var currencyLabel : UILabel!
17 | @IBOutlet weak var changeLabel : UILabel!
18 | @IBOutlet weak var cellIndicatorImageView : UIImageView!
19 | }
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 |
3 | ## User settings
4 | xcuserdata
5 |
6 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
7 | *.xcscmblueprint
8 | *.xccheckout
9 |
10 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
11 | build/
12 | DerivedData/
13 | *.xcworkspace
14 | *.xcuserstate
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1
19 | *.mode1v3
20 | *.mode2v3
21 | !default.mode1v3
22 | *.mode2v3
23 | !default.mode2v3
24 | *.perspective
25 | *.perspectivev3
26 | !default.perspectivev3
27 |
28 | # Exclude OS X folder attributes
29 | .DS_Store
30 |
31 | # Exclude temp nibs and swap files
32 | *~.nib
33 | *.swp
34 |
35 | Pods/
--------------------------------------------------------------------------------
/Lannister/Data/API/APIManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIManager.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 17/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 |
12 | class APIManager: SessionManager {
13 |
14 | class var sharedManager: APIManager {
15 | struct Static {
16 | static var sharedInstance = APIManager(configuration: URLSessionConfiguration.default,
17 | delegate: SessionDelegate.init(),
18 | serverTrustPolicyManager: nil)
19 | }
20 | return Static.sharedInstance
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Lannister/Domain/Repositories/WalletRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WalletRepository.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 12/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol WalletRepository : Repository {
12 |
13 | func getBalance(address: String, success: @escaping(Double) -> Void, failure: @escaping(_ error: Error) -> Void)
14 | func getBalanceOfToken(address: String, erc20TokenAddress: String, success: @escaping(Double) -> Void, failure: @escaping(_ error: Error) -> Void)
15 | func getTransactions(address: String, success: @escaping(_ transactions: Array?) -> Void, failure: @escaping(_ error: Error) -> Void)
16 | }
17 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/LaunchController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LaunchController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class LaunchController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | if (UserDefaults.standard.object(forKey: "Auth") == nil) {
17 | DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
18 | AppDelegate.shared.rootViewController.switchToAuthScreen()
19 | })
20 | } else {
21 | AppDelegate.shared.rootViewController.switchToMainScreen()
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Lannister/App/Utilities/NumberFormatter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NumberFormatter.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 09/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension String {
12 | static let numberFormatter = NumberFormatter()
13 | var doubleValue: Double? {
14 | String.numberFormatter.decimalSeparator = "."
15 | if let result = String.numberFormatter.number(from: self) {
16 | return result.doubleValue
17 | } else {
18 | String.numberFormatter.decimalSeparator = ","
19 | if let result = String.numberFormatter.number(from: self) {
20 | return result.doubleValue
21 | }
22 | }
23 | return nil
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/LannisterTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Lannister/Domain/Entities/Holding.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Holding.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 05/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Holding {
12 |
13 | // attributes
14 | var address : String?
15 | var hexColor : String!
16 | var name : String!
17 | var value : Double?
18 |
19 | // relationships
20 | var currency : Currency?
21 | var transactions : [Transaction]?
22 |
23 | // computed properties
24 | var representiveValue : Double?
25 | var representiveCurrency : Currency?
26 | var totalEuroValue : Double?
27 |
28 | init(with dictionary: [String : Any]?) {
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LannisterUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Lannister/Data/API/Services/EtherScanApiService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EtherScanApiService.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 17/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 |
12 | class EtherScanApiService: NSObject {
13 |
14 | internal typealias Response = DataResponse?
15 |
16 | var sharedManager = APIManager.sharedManager
17 |
18 | let baseURL = "https://api.etherscan.io/api"
19 | let apiKey = "GP7SEQH5PPAX47AGETTCU3ZHCMD3WRUP3F"
20 |
21 | func getTransactions(params: Parameters?, returns: @escaping (Response) -> Void) {
22 | sharedManager.request("\(baseURL)", method: .get, parameters: params).validate().responseJSON { response in returns(response) }
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | platform :ios, '11.0'
3 |
4 | def all_pods
5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6 | use_frameworks!
7 | pod 'Alamofire', '4.8.0'
8 | pod 'Blockstack', '1.0.1'
9 | pod 'Charts'
10 | pod 'Groot', '3.0.1'
11 | pod 'MagicalRecord', :git => 'https://github.com/magicalpanda/MagicalRecord.git', :tag => 'v2.3.3'
12 | pod 'BiometricAuthentication'
13 | pod 'SVProgressHUD'
14 | pod 'QRCodeReader.swift', '~> 10.0.0'
15 | pod 'web3swift'
16 | end
17 |
18 | target 'Lannister' do
19 | all_pods
20 | end
21 |
22 | target 'Lannister Beta' do
23 | all_pods
24 | end
25 |
26 | target 'LannisterTests' do
27 | inherit! :search_paths
28 | # Pods for testing
29 | end
30 |
31 | target 'LannisterUITests' do
32 | inherit! :search_paths
33 | # Pods for testing
34 | end
--------------------------------------------------------------------------------
/Lannister/App/Utilities/Parser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Parser.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 29/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension String {
12 |
13 | var parseJSONString: AnyObject? {
14 |
15 | let data = self.data(using: String.Encoding.utf8, allowLossyConversion: false)
16 |
17 | if let jsonData = data {
18 | // Will return an object or nil if JSON decoding fails
19 | do {
20 | return try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
21 | } catch {
22 | print(error)
23 | return nil
24 | }
25 | } else {
26 | // Lossless conversion of the string was not possible
27 | return nil
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Lannister/Domain/Use cases/HoldingsUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingUseCase.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 18/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HoldingsUseCase: NSObject {
12 |
13 | var repository: Repository?
14 |
15 | convenience init(with repository: Repository) {
16 | self.init()
17 | self.repository = repository
18 | }
19 |
20 | func updateHoldingsWithComputedProperties(holdings: [Holding]) -> [Holding] {
21 |
22 | let repo = self.repository as! HoldingsRepository
23 | return repo.updateHoldingsWithComputedProperties(holdings: holdings)
24 | }
25 |
26 | func updateHoldingWithComputedProperties(holding: Holding) -> Holding {
27 |
28 | let repo = self.repository as! HoldingsRepository
29 | return repo.updateHoldingWithComputedProperties(holding: holding)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Lannister/Data/Mappers/CurrencyDto.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CurrencyDto.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 08/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CurrencyDto: NSObject {
12 |
13 | func currency(from managedObject: CurrencyManagedObject) -> Currency {
14 |
15 | var currency = Currency(with: nil)
16 | currency.code = managedObject.code
17 | currency.name = managedObject.name
18 | currency.euroRate = managedObject.euro_rate
19 | currency.symbol = managedObject.symbol
20 | return currency
21 | }
22 |
23 | func currencies(from managedObjects: [CurrencyManagedObject]) -> [Currency] {
24 |
25 | var currencies = Array()
26 | for managedObject in managedObjects {
27 | currencies.append(currency(from: managedObject))
28 | }
29 | return currencies
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/Lannister/Data/Mappers/TransactionDto.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TransactionDto.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 06/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 |
12 | class TransactionDto : NSObject {
13 |
14 | func transaction(from managedObject: TransactionManagedObject) -> Transaction {
15 |
16 | var transaction = Transaction(with: nil)
17 | transaction.identifier = managedObject.id
18 | transaction.name = managedObject.name
19 | transaction.type = managedObject.type
20 | transaction.value = managedObject.value
21 | return transaction
22 | }
23 |
24 | func transactions(from managedObjects: [TransactionManagedObject]) -> [Transaction] {
25 |
26 | var transactions = Array()
27 | for managedObject in managedObjects {
28 | transactions.append(transaction(from: managedObject))
29 | }
30 | return transactions
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/LannisterTests/LannisterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LannisterTests.swift
3 | // LannisterTests
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Lannister
11 |
12 | class LannisterTests: XCTestCase {
13 |
14 | override func setUp() {
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 | func testExample() {
23 | // This is an example of a functional test case.
24 | // Use XCTAssert and related functions to verify your tests produce the correct results.
25 | }
26 |
27 | func testPerformanceExample() {
28 | // This is an example of a performance test case.
29 | self.measure {
30 | // Put the code you want to measure the time of here.
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Lannister/Data/Repositories Implementations/PortfolioRepositoryImpl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PortfolioRepositoryImpl.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 24/06/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MagicalRecord
11 | import CoreData
12 |
13 | class PortfolioRepositoryImpl: PortfolioRepository {
14 |
15 | func getEuroTotalValue() -> Double {
16 |
17 | let holdingsManagedObjects = HoldingManagedObject.mr_findAll(in: NSManagedObjectContext.mr_default())
18 | var holdings = HoldingDto().holdings(from: holdingsManagedObjects as! [HoldingManagedObject])
19 | holdings = HoldingsUseCase(with: HoldingsRepositoryImpl()).updateHoldingsWithComputedProperties(holdings: holdings)
20 |
21 | var euroTotalValue : Double = 0
22 | for holding in holdings {
23 | euroTotalValue += Currencies.getEuroValue(value: holding.representiveValue!, currency: holding.representiveCurrency!)
24 | }
25 |
26 | return euroTotalValue
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Lannister/Data/User defaults/CurrencyUserDefaults.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CurrencyUserDefaults.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 10/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CurrencyUserDefaults: NSObject {
12 |
13 | func setDefaultCurrency(name: String) {
14 | UserDefaults.standard.set(name, forKey: "defaultCurrency")
15 | UserDefaults.standard.synchronize()
16 | }
17 |
18 | func getDefaultCurrencyName() -> String? {
19 | if let currency = UserDefaults.standard.value(forKey: "defaultCurrency") as? String {
20 | return currency
21 | }
22 | return nil
23 | }
24 |
25 | func setDefaultCurrency(code: String) {
26 | UserDefaults.standard.set(code, forKey: "defaultCurrencyCode")
27 | UserDefaults.standard.synchronize()
28 | }
29 |
30 | func getDefaultCurrencyCode() -> String? {
31 | if let currency = UserDefaults.standard.value(forKey: "defaultCurrencyCode") as? String {
32 | return currency
33 | }
34 | return nil
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/LannisterUITests/LannisterUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LannisterUITests.swift
3 | // LannisterUITests
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class LannisterUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 |
16 | // In UI tests it is usually best to stop immediately when a failure occurs.
17 | continueAfterFailure = false
18 |
19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
20 | XCUIApplication().launch()
21 |
22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
23 | }
24 |
25 | override func tearDown() {
26 | // Put teardown code here. This method is called after the invocation of each test method in the class.
27 | }
28 |
29 | func testExample() {
30 | // Use recording to get started writing UI tests.
31 | // Use XCTAssert and related functions to verify your tests produce the correct results.
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Lannister/Data/API/Services/CurrencyApiService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CurrencyAPIService.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 08/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 |
12 | class CurrencyApiService: BaseApiService {
13 |
14 | internal typealias Response = DataResponse?
15 |
16 | override init() {
17 | super.init()
18 | }
19 |
20 | var sharedManager = APIManager.sharedManager
21 |
22 | func getCurrencies(returns: @escaping(Response) -> Void) {
23 | sharedManager.request("\(currencyBaseUrl)/latest", method: .get, parameters: nil)
24 | .validate()
25 | .responseJSON { (response) in returns(response) }
26 | }
27 |
28 | func getBTCfromEur(returns: @escaping(Response) -> Void) {
29 | sharedManager.request("\(currencyCryptoBaseUrl)/eur-btc", method: .get, parameters: nil)
30 | .validate()
31 | .responseJSON { (response) in returns(response) }
32 | }
33 |
34 | func getETHfromEur(returns: @escaping(Response) -> Void) {
35 | sharedManager.request("\(currencyCryptoBaseUrl)/eur-eth", method: .get, parameters: nil)
36 | .validate()
37 | .responseJSON { (response) in returns(response) }
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Lannister/App/Utilities/Currencies.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Currencies.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 10/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 |
12 | struct Currencies {
13 |
14 | static func getDefaultCurrencySymbol() -> String {
15 |
16 | let currencyCode = CurrencyUserDefaults().getDefaultCurrencyCode()!
17 | let currencyManagedObject = CurrencyManagedObject.mr_findFirst(byAttribute: "code", withValue: currencyCode, in: NSManagedObjectContext.mr_default())!
18 | let currency = CurrencyDto().currency(from: currencyManagedObject)
19 |
20 | return currency.symbol
21 | }
22 |
23 | static func getDefaultCurrencyEuroRate() -> Double {
24 |
25 | let currencyCode = CurrencyUserDefaults().getDefaultCurrencyCode()!
26 | let currencyManagedObject = CurrencyManagedObject.mr_findFirst(byAttribute: "code", withValue: currencyCode, in: NSManagedObjectContext.mr_default())!
27 | let currency = CurrencyDto().currency(from: currencyManagedObject)
28 |
29 | return currency.euroRate
30 | }
31 |
32 | static func getEuroValue(value: Double, currency: Currency) -> Double {
33 |
34 | return value / currency.euroRate
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/Lannister/Domain/Use cases/WalletUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WalletUseCase.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 12/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class WalletUseCase : NSObject {
12 |
13 | var repository: Repository?
14 |
15 | convenience init(with repository: Repository) {
16 | self.init()
17 | self.repository = repository
18 | }
19 |
20 | func getBalance(address: String, success: @escaping(Double) -> Void, failure: @escaping(_ error: Error) -> Void) {
21 |
22 | let repo = self.repository as! WalletRepository
23 | return repo.getBalance(address: address, success: success, failure: failure)
24 | }
25 |
26 | func getBalanceOfToken(address: String, erc20TokenAddress: String, success: @escaping(Double) -> Void, failure: @escaping(_ error: Error) -> Void) {
27 | let repo = self.repository as! WalletRepository
28 | return repo.getBalanceOfToken(address: address, erc20TokenAddress: erc20TokenAddress, success: success, failure: failure)
29 | }
30 |
31 | func getTransactions(address: String, success: @escaping(_ transactions: Array?) -> Void, failure: @escaping(_ error: Error) -> Void) {
32 |
33 | let repo = self.repository as! WalletRepository
34 | return repo.getTransactions(address: address, success: success, failure: failure)
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/Lannister/Data/Mappers/TokenDto.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TokenDto.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 15/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TokenDto: NSObject {
12 |
13 | func token(from managedObject: TokenManagedObject) -> Token {
14 |
15 | var token = Token(with: nil)
16 | token.address = managedObject.address
17 | if managedObject.name != nil {
18 | token.name = managedObject.name
19 | }
20 | if managedObject.code != nil {
21 | token.code = managedObject.code
22 | }
23 | token.value = managedObject.value
24 | if managedObject.currency != nil {
25 | token.currency = CurrencyDto().currency(from: managedObject.currency!)
26 | }
27 |
28 | if managedObject.transactions != nil {
29 | if (managedObject.transactions?.allObjects.count)! > 0 {
30 | let transactionsManagedObjects = managedObject.transactions?.allObjects as! [TransactionManagedObject]
31 | let transactions = TransactionDto().transactions(from: transactionsManagedObjects)
32 | token.transactions = transactions
33 | }
34 | }
35 | return token
36 | }
37 |
38 | func tokens(from managedObjects: [TokenManagedObject]) -> [Token] {
39 |
40 | var tokens = Array()
41 | for managedObject in managedObjects {
42 | tokens.append(token(from: managedObject))
43 | }
44 | return tokens
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/Lannister/Data/Mappers/HoldingDto.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingDto.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 05/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 |
12 | class HoldingDto : NSObject {
13 |
14 | func holding(from managedObject: HoldingManagedObject) -> Holding {
15 |
16 | var holding = Holding(with: nil)
17 | if(managedObject.address != nil) {
18 | holding.address = managedObject.address
19 | }
20 | holding.name = managedObject.name
21 | holding.value = managedObject.value?.doubleValue
22 | holding.hexColor = managedObject.hex_color
23 | if managedObject.currency != nil {
24 | holding.currency = CurrencyDto().currency(from: managedObject.currency!)
25 | }
26 | if managedObject.transactions != nil {
27 | if (managedObject.transactions?.allObjects.count)! > 0 {
28 | let transactionsManagedObjects = managedObject.transactions?.allObjects as! [TransactionManagedObject]
29 | let transactions = TransactionDto().transactions(from: transactionsManagedObjects)
30 | holding.transactions = transactions
31 | }
32 | }
33 | return holding
34 | }
35 |
36 | func holdings(from managedObjects: [HoldingManagedObject]) -> [Holding] {
37 |
38 | var holdings = Array()
39 | for managedObject in managedObjects {
40 | holdings.append(holding(from: managedObject))
41 | }
42 | return holdings
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Lannister/App/Utilities/Colors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Colors.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 05/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Colors {
12 |
13 | static func hexStringToUIColor(hex:String) -> UIColor {
14 | var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
15 |
16 | if (cString.hasPrefix("#")) {
17 | cString.remove(at: cString.startIndex)
18 | }
19 |
20 | if ((cString.count) != 6) {
21 | return UIColor.gray
22 | }
23 |
24 | var rgbValue:UInt32 = 0
25 | Scanner(string: cString).scanHexInt32(&rgbValue)
26 |
27 | return UIColor(
28 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
29 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
30 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
31 | alpha: CGFloat(1.0)
32 | )
33 | }
34 |
35 | static func isCustomColor(hex: String) -> Bool {
36 |
37 | var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
38 |
39 | if (cString.hasPrefix("#")) {
40 | cString.remove(at: cString.startIndex)
41 | }
42 |
43 | if cString == "FFBF00" {
44 | return false
45 | } else if cString == "7C8288" {
46 | return false
47 | } else if cString == "E51522" {
48 | return false
49 | } else if cString == "00B382" {
50 | return false
51 | } else if cString == "1538C0" {
52 | return false
53 | } else if cString == "6F0DBE" {
54 | return false
55 | }
56 |
57 | return true
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/RootController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class RootController: UIViewController {
12 |
13 | private var current: UIViewController
14 |
15 | required init?(coder aDecoder: NSCoder) {
16 | self.current = LaunchController()
17 | super.init(coder: aDecoder)
18 | }
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 |
23 | self.current = storyboard?.instantiateViewController(withIdentifier: "launchVC") as! LaunchController
24 | addChild(current)
25 | current.view.frame = view.bounds
26 | view.addSubview(current.view)
27 | current.didMove(toParent: self)
28 | }
29 |
30 | func switchToMainScreen() {
31 | let mainController = self.storyboard?.instantiateViewController(withIdentifier: "dashboardNavVC") as! UINavigationController
32 | animateFadeTransition(to: mainController)
33 | }
34 |
35 | func switchToAuthScreen() {
36 | let authController = self.storyboard?.instantiateViewController(withIdentifier: "authVC") as! AuthController
37 | animateFadeTransition(to: authController)
38 | }
39 |
40 | private func animateFadeTransition(to new: UIViewController, completion: (() -> Void)? = nil) {
41 | current.willMove(toParent: nil)
42 | addChild(new)
43 | self.view.addSubview(new.view)
44 | new.view.alpha = 0
45 | new.view.layoutIfNeeded()
46 |
47 | UIView.animate(withDuration: 0.5, animations: {
48 | new.view.alpha = 1
49 | self.current.view.alpha = 0
50 | }) { (finished) in
51 | self.current.view.removeFromSuperview()
52 | self.current.removeFromParent()
53 | new.didMove(toParent: self)
54 | self.current = new
55 | completion?()
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/Models/Generated/__TransactionManagedObject.swift:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten.
2 | // Make changes to TransactionManagedObject.swift instead.
3 |
4 | import Foundation
5 | import CoreData
6 |
7 | public enum TransactionManagedObjectAttributes: String {
8 | case id = "id"
9 | case name = "name"
10 | case type = "type"
11 | case value = "value"
12 | }
13 |
14 | public enum TransactionManagedObjectRelationships: String {
15 | case holding = "holding"
16 | }
17 |
18 | open class _TransactionManagedObject: NSManagedObject {
19 |
20 | // MARK: - Class methods
21 |
22 | open class func entityName () -> String {
23 | return "TransactionManagedObject"
24 | }
25 |
26 | open class func entity(managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
27 | return NSEntityDescription.entity(forEntityName: self.entityName(), in: managedObjectContext)
28 | }
29 |
30 | @nonobjc
31 | open class func fetchRequest() -> NSFetchRequest {
32 | return NSFetchRequest(entityName: self.entityName())
33 | }
34 |
35 | // MARK: - Life cycle methods
36 |
37 | public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
38 | super.init(entity: entity, insertInto: context)
39 | }
40 |
41 | public convenience init?(managedObjectContext: NSManagedObjectContext) {
42 | guard let entity = _TransactionManagedObject.entity(managedObjectContext: managedObjectContext) else { return nil }
43 | self.init(entity: entity, insertInto: managedObjectContext)
44 | }
45 |
46 | // MARK: - Properties
47 |
48 | @NSManaged open
49 | var id: String?
50 |
51 | @NSManaged open
52 | var name: String!
53 |
54 | @NSManaged open
55 | var type: String!
56 |
57 | @NSManaged open
58 | var value: Double
59 |
60 | // MARK: - Relationships
61 |
62 | @NSManaged open
63 | var holding: HoldingManagedObject
64 |
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/Lannister/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0.4
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleTypeRole
23 | Editor
24 | CFBundleURLName
25 | Lannister
26 | CFBundleURLSchemes
27 |
28 | lannister
29 |
30 |
31 |
32 | CFBundleVersion
33 | $(CURRENT_PROJECT_VERSION)
34 | LSRequiresIPhoneOS
35 |
36 | NSCameraUsageDescription
37 | Allow access to camera to scan a QR code.
38 | NSFaceIDUsageDescription
39 | Keep your holdings information access secure with biometric authentication.
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIMainStoryboardFile
43 | Main
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Lannister/Info-dev.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0.4
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleTypeRole
23 | Editor
24 | CFBundleURLName
25 | Lannister
26 | CFBundleURLSchemes
27 |
28 | lannister
29 |
30 |
31 |
32 | CFBundleVersion
33 | $(CURRENT_PROJECT_VERSION)
34 | LSRequiresIPhoneOS
35 |
36 | NSCameraUsageDescription
37 | Allow access to camera to scan a QR code.
38 | NSFaceIDUsageDescription
39 | Keep your holdings information access secure with biometric authentication.
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIMainStoryboardFile
43 | Main
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/CustomQRCodeController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomQRCodeController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 09/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import QRCodeReader
11 |
12 | protocol CustomQRCodeControllerDelegate {
13 | func scanned(result: QRCodeReaderResult?)
14 | }
15 |
16 | class CustomQRCodeController: UIViewController {
17 |
18 | var delegate : CustomQRCodeControllerDelegate!
19 | @IBOutlet weak var closeButton : UIButton!
20 | @IBOutlet weak var scannerOverlay : UIImageView!
21 |
22 | lazy var readerVC: QRCodeReaderViewController = {
23 | let builder = QRCodeReaderViewControllerBuilder {
24 | $0.reader = QRCodeReader(metadataObjectTypes: [.qr], captureDevicePosition: .back)
25 |
26 | // Configure the view controller (optional)
27 | $0.showTorchButton = false
28 | $0.showSwitchCameraButton = false
29 | $0.showCancelButton = false
30 | $0.showOverlayView = false
31 | $0.rectOfInterest = CGRect(x: 0.2, y: 0.2, width: 0.6, height: 0.6)
32 | }
33 |
34 | return QRCodeReaderViewController(builder: builder)
35 | }()
36 |
37 | override func viewDidLoad() {
38 | super.viewDidLoad()
39 |
40 | readerVC.completionBlock = { (result: QRCodeReaderResult?) in
41 |
42 | self.dismiss(animated: true, completion: {
43 | self.delegate.scanned(result: result)
44 | })
45 | }
46 |
47 | self.addChild(readerVC)
48 | readerVC.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: UIScreen.main.bounds.size.height-183)
49 | view.addSubview(readerVC.view)
50 | readerVC.didMove(toParent: self)
51 |
52 | view.bringSubviewToFront(closeButton)
53 | view.bringSubviewToFront(scannerOverlay)
54 | }
55 |
56 | @IBAction func cancel() {
57 |
58 | self.dismiss(animated: true, completion: nil)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lannister iOS 👑
2 |
3 | Lannister allows anyone to keep track and analyze the distribution of their holdings both in crypto and fiat (banks, deposits, savings, etc).
4 |
5 | 
6 |
7 | 💭 Inspiration
8 | We started building Lannister because we needed a way to keep track of our holdings both in crypto and fiat (banks, deposits, savings, etc). It was also fundamental to have security and privacy in mind. No central server that could compromise our identity and financial data.
9 |
10 | 📊 What it does
11 | Lannister allows anyone to keep track of their holdings, follow its progress and easily analyze their distribution. It's simple to use for anyone who wants to take control of their financial life.
12 |
13 | ⭐️ Unique features
14 | Convenient: crypto and fiat, all-in-one place (supporting now 12 fiat and 2 crypto currencies with more to come)
15 | Secure: runs locally on your phone, supports biometric access, optionally syncs with secure end-to-end encryption and decentralized storage via Blockstack infrastructure
16 | Open: completely open-source
17 |
18 | 💻 Platforms
19 | We're building Lannister for iOS and web, though only iOS is available at the moment. Both platforms will be able to sync information via Blockstack's infrastructure and keep information securely stored. Android is also coming in the next months.
20 |
21 | 🔮 Future plans
22 | Here's a list of some of the features we're planning:
23 |
24 | - Predictions
25 | - Retrospective analysis
26 | - Financial goals and recommendations
27 |
28 | ## Roadmap
29 |
30 | To get an overview of the current development status head over to [our roadmap](https://github.com/lannister-capital/lannister-ios/projects/1).
31 |
32 | ## Contribute
33 |
34 | ### Setup
35 |
36 | Clone the project and install the required dependencies via cocoapods with:
37 |
38 | `pod install`
39 |
40 | Open `Lannister.xcworkspace` and you're ready 🎉
41 |
42 | ### Guidelines
43 |
44 | Work in progress 🏗
45 |
46 | ## License
47 |
48 | lannister-ios is under the GPLv3 and the MPLv2 license.
49 |
50 | See [LICENSE](https://github.com/lannister-capital/lannister-ios/blob/master/LICENSE.md) for more license info.
51 |
52 | ---
53 |
54 | Made at [Done Sunday](http://donesunday.com/) 🌞 by [@alvesjtiago](https://twitter.com/alvesjtiago) and [@\_andre_sousa](https://twitter.com/_andre_sousa).
55 |
56 | Join us on [Discord](https://discord.gg/JpCBs2X) to help shape Lannister's future.
57 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/CurrenciesController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CurrenciesController.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 09/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 |
12 | protocol CurrenciesDelegate {
13 | func selectedCurrency(currency: Currency)
14 | }
15 |
16 | class CurrenciesController: UIViewController {
17 |
18 | @IBOutlet weak var tableView : UITableView!
19 | var currencies : [Currency]!
20 | var delegate : CurrenciesDelegate!
21 | var shouldSetGlobalCurrency = false
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 |
26 | navigationController!.navigationBar.titleTextAttributes =
27 | [NSAttributedString.Key.font: UIFont(name: "AvenirNext-Medium", size: 18)!,
28 | NSAttributedString.Key.foregroundColor : UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)]
29 | navigationController?.navigationBar.tintColor = UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)
30 |
31 | navigationItem.title = "Currencies"
32 |
33 | getCurrencies()
34 | }
35 |
36 | func getCurrencies() {
37 |
38 | let currenciesManagedObjects = CurrencyManagedObject.mr_findAll(in: NSManagedObjectContext.mr_default())
39 | currencies = CurrencyDto().currencies(from: currenciesManagedObjects as! [CurrencyManagedObject])
40 | currencies = currencies.sorted(by: { $0.name < $1.name })
41 | tableView.reloadData()
42 | }
43 |
44 | }
45 |
46 | extension CurrenciesController : UITableViewDataSource {
47 |
48 | func numberOfSections(in tableView: UITableView) -> Int {
49 | return 1
50 | }
51 |
52 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
53 | return currencies.count
54 | }
55 |
56 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
57 | let cell = tableView.dequeueReusableCell(withIdentifier: "currencyCellId", for: indexPath) as! CurrencyCell
58 | let currency = currencies[indexPath.row]
59 | cell.currencyNameLabel.text = currency.name.capitalized
60 | cell.currencySymbolLabel.text = currency.symbol
61 | return cell
62 | }
63 | }
64 |
65 | extension CurrenciesController : UITableViewDelegate {
66 |
67 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
68 |
69 | let currency = currencies[indexPath.row]
70 |
71 | if shouldSetGlobalCurrency {
72 | CurrencyUserDefaults().setDefaultCurrency(code: currency.code)
73 | }
74 |
75 | delegate.selectedCurrency(currency: currency)
76 | navigationController?.popViewController(animated: true)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Lannister/Data/Repositories Implementations/HoldingsRepositoryImpl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingsRepositoryImpl.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 18/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 |
12 | class HoldingsRepositoryImpl: HoldingsRepository {
13 |
14 | func updateHoldingsWithComputedProperties(holdings: [Holding]) -> [Holding] {
15 |
16 | var newHoldings : [Holding] = []
17 | for holding in holdings {
18 | let updatedHolding = updateHoldingWithComputedProperties(holding: holding)
19 | newHoldings.append(updatedHolding)
20 | }
21 |
22 | return newHoldings
23 | }
24 |
25 | func updateHoldingWithComputedProperties(holding: Holding) -> Holding {
26 |
27 | var updatedHolding = holding
28 | if holding.address != nil {
29 | // get tokens (only eth for now)
30 | let predicateAddress = NSPredicate(format: "address == %@", holding.address!)
31 | let predicateCode = NSPredicate(format: "code == %@", "ETH")
32 | let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateAddress, predicateCode])
33 |
34 | var tokenManagedObjects = TokenManagedObject.mr_findAll(with: compoundPredicate, in: NSManagedObjectContext.mr_default()) as! [TokenManagedObject]
35 | if tokenManagedObjects.count == 0 {
36 | let tokenManagedObject = TokenManagedObject(context: NSManagedObjectContext.mr_default())
37 | tokenManagedObject.address = holding.address
38 | tokenManagedObject.code = "ETH"
39 | let currencyManagedObject = CurrencyManagedObject.mr_findFirst(byAttribute: "code", withValue: "ETH", in: NSManagedObjectContext.mr_default())
40 | tokenManagedObject.currency = currencyManagedObject
41 | NSManagedObjectContext.mr_default().mr_saveToPersistentStoreAndWait()
42 | tokenManagedObjects = TokenManagedObject.mr_findAll(with: compoundPredicate, in: NSManagedObjectContext.mr_default()) as! [TokenManagedObject]
43 | }
44 | let tokens = TokenDto().tokens(from: tokenManagedObjects)
45 | updatedHolding.representiveValue = tokens[0].value
46 | updatedHolding.representiveCurrency = tokens[0].currency
47 | for token in tokens {
48 | updatedHolding.totalEuroValue = Currencies.getEuroValue(value: token.value, currency: token.currency)
49 | }
50 | } else {
51 | updatedHolding.representiveValue = holding.value!
52 | updatedHolding.representiveCurrency = holding.currency!
53 | updatedHolding.totalEuroValue = Currencies.getEuroValue(value: holding.value!, currency: holding.currency!)
54 | }
55 | return updatedHolding
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/DonationsController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DonationsController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 14/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DonationsController: UIViewController {
12 |
13 | @IBOutlet weak var tableView : UITableView!
14 | var btcAddress = "3HbYALZzQYCzeNHvSFkW7JG5dEGHWbnV2j"
15 | var ethAddress = "0x291268FcF2c6c686fd542376Bf6Ea926fCA63C91"
16 | var daiAddress = "0x394a29F426F6505d40854ABb730D1c8DE29C8C87"
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 |
22 | navigationController!.navigationBar.titleTextAttributes =
23 | [NSAttributedString.Key.font: UIFont(name: "AvenirNext-Medium", size: 18)!,
24 | NSAttributedString.Key.foregroundColor : UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)]
25 | navigationController?.navigationBar.tintColor = UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)
26 |
27 | navigationItem.title = "Donate"
28 | }
29 |
30 |
31 | }
32 |
33 | extension DonationsController : UITableViewDataSource {
34 |
35 | func numberOfSections(in tableView: UITableView) -> Int {
36 | return 1
37 | }
38 |
39 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
40 | return 3
41 | }
42 |
43 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
44 | let cell = tableView.dequeueReusableCell(withIdentifier: "donationsCellId", for: indexPath) as! DonationCell
45 | if indexPath.row == 0 {
46 | cell.currencyLabel.text = "BTC address"
47 | cell.addressLabel.text = btcAddress
48 | } else if indexPath.row == 1 {
49 | cell.currencyLabel.text = "ETH address"
50 | cell.addressLabel.text = ethAddress
51 | } else {
52 | cell.currencyLabel.text = "DAI address"
53 | cell.addressLabel.text = daiAddress
54 | }
55 | return cell
56 | }
57 | }
58 |
59 | extension DonationsController : UITableViewDelegate {
60 |
61 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
62 |
63 | tableView.deselectRow(at: indexPath, animated: true)
64 |
65 | if indexPath.row == 0 {
66 | UIPasteboard.general.string = btcAddress
67 | } else if indexPath.row == 1 {
68 | UIPasteboard.general.string = ethAddress
69 | } else {
70 | UIPasteboard.general.string = daiAddress
71 | }
72 |
73 | let cell = tableView.cellForRow(at: indexPath) as! DonationCell
74 | cell.copyLabel.text = "Copied"
75 |
76 | DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
77 | cell.copyLabel.text = "Copy"
78 | })
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Lannister/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "AppIcon copy-12.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "AppIcon copy-7.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "AppIcon copy-9.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "AppIcon copy-3.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "AppIcon copy-4.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "AppIcon copy-2.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "AppIcon-3.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "AppIcon-2.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "AppIcon copy-14.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "AppIcon copy-10.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "AppIcon copy-13.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "AppIcon copy-8.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "AppIcon copy-11.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "AppIcon copy-5.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "AppIcon copy-6.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "AppIcon copy-1.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "size" : "83.5x83.5",
101 | "idiom" : "ipad",
102 | "filename" : "AppIcon copy.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "1024x1024",
107 | "idiom" : "ios-marketing",
108 | "filename" : "AppIcon.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | }
116 | }
--------------------------------------------------------------------------------
/Lannister/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/Models/Generated/_CurrencyManagedObject.swift:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten.
2 | // Make changes to CurrencyManagedObject.swift instead.
3 |
4 | import Foundation
5 | import CoreData
6 |
7 | public enum CurrencyManagedObjectAttributes: String {
8 | case code = "code"
9 | case euro_rate = "euro_rate"
10 | case name = "name"
11 | case symbol = "symbol"
12 | }
13 |
14 | public enum CurrencyManagedObjectRelationships: String {
15 | case holdings = "holdings"
16 | }
17 |
18 | public enum CurrencyManagedObjectUserInfo: String {
19 | case identityAttributes = "identityAttributes"
20 | }
21 |
22 | open class _CurrencyManagedObject: NSManagedObject {
23 |
24 | // MARK: - Class methods
25 |
26 | open class func entityName () -> String {
27 | return "CurrencyManagedObject"
28 | }
29 |
30 | open class func entity(managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
31 | return NSEntityDescription.entity(forEntityName: self.entityName(), in: managedObjectContext)
32 | }
33 |
34 | @nonobjc
35 | open class func fetchRequest() -> NSFetchRequest {
36 | return NSFetchRequest(entityName: self.entityName())
37 | }
38 |
39 | // MARK: - Life cycle methods
40 |
41 | public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
42 | super.init(entity: entity, insertInto: context)
43 | }
44 |
45 | public convenience init?(managedObjectContext: NSManagedObjectContext) {
46 | guard let entity = _CurrencyManagedObject.entity(managedObjectContext: managedObjectContext) else { return nil }
47 | self.init(entity: entity, insertInto: managedObjectContext)
48 | }
49 |
50 | // MARK: - Properties
51 |
52 | @NSManaged open
53 | var code: String!
54 |
55 | @NSManaged open
56 | var euro_rate: Double // Optional scalars not supported
57 |
58 | @NSManaged open
59 | var name: String!
60 |
61 | @NSManaged open
62 | var symbol: String!
63 |
64 | // MARK: - Relationships
65 |
66 | @NSManaged open
67 | var holdings: NSSet
68 |
69 | open func holdingsSet() -> NSMutableSet {
70 | return self.holdings.mutableCopy() as! NSMutableSet
71 | }
72 |
73 | }
74 |
75 | extension _CurrencyManagedObject {
76 |
77 | open func addHoldings(_ objects: NSSet) {
78 | let mutable = self.holdings.mutableCopy() as! NSMutableSet
79 | mutable.union(objects as Set)
80 | self.holdings = mutable.copy() as! NSSet
81 | }
82 |
83 | open func removeHoldings(_ objects: NSSet) {
84 | let mutable = self.holdings.mutableCopy() as! NSMutableSet
85 | mutable.minus(objects as Set)
86 | self.holdings = mutable.copy() as! NSSet
87 | }
88 |
89 | open func addHoldingsObject(_ value: HoldingManagedObject) {
90 | let mutable = self.holdings.mutableCopy() as! NSMutableSet
91 | mutable.add(value)
92 | self.holdings = mutable.copy() as! NSSet
93 | }
94 |
95 | open func removeHoldingsObject(_ value: HoldingManagedObject) {
96 | let mutable = self.holdings.mutableCopy() as! NSMutableSet
97 | mutable.remove(value)
98 | self.holdings = mutable.copy() as! NSSet
99 | }
100 |
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/Models/Generated/__HoldingManagedObject.swift:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten.
2 | // Make changes to HoldingManagedObject.swift instead.
3 |
4 | import Foundation
5 | import CoreData
6 |
7 | public enum HoldingManagedObjectAttributes: String {
8 | case hex_color = "hex_color"
9 | case id = "id"
10 | case name = "name"
11 | case value = "value"
12 | }
13 |
14 | public enum HoldingManagedObjectRelationships: String {
15 | case currency = "currency"
16 | case transactions = "transactions"
17 | }
18 |
19 | open class _HoldingManagedObject: NSManagedObject {
20 |
21 | // MARK: - Class methods
22 |
23 | open class func entityName () -> String {
24 | return "HoldingManagedObject"
25 | }
26 |
27 | open class func entity(managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
28 | return NSEntityDescription.entity(forEntityName: self.entityName(), in: managedObjectContext)
29 | }
30 |
31 | @nonobjc
32 | open class func fetchRequest() -> NSFetchRequest {
33 | return NSFetchRequest(entityName: self.entityName())
34 | }
35 |
36 | // MARK: - Life cycle methods
37 |
38 | public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
39 | super.init(entity: entity, insertInto: context)
40 | }
41 |
42 | public convenience init?(managedObjectContext: NSManagedObjectContext) {
43 | guard let entity = _HoldingManagedObject.entity(managedObjectContext: managedObjectContext) else { return nil }
44 | self.init(entity: entity, insertInto: managedObjectContext)
45 | }
46 |
47 | // MARK: - Properties
48 |
49 | @NSManaged open
50 | var hex_color: String?
51 |
52 | @NSManaged open
53 | var id: String?
54 |
55 | @NSManaged open
56 | var name: String!
57 |
58 | @NSManaged open
59 | var value: Double // Optional scalars not supported
60 |
61 | // MARK: - Relationships
62 |
63 | @NSManaged open
64 | var currency: CurrencyManagedObject
65 |
66 | @NSManaged open
67 | var transactions: NSSet
68 |
69 | open func transactionsSet() -> NSMutableSet {
70 | return self.transactions.mutableCopy() as! NSMutableSet
71 | }
72 |
73 | }
74 |
75 | extension _HoldingManagedObject {
76 |
77 | open func addTransactions(_ objects: NSSet) {
78 | let mutable = self.transactions.mutableCopy() as! NSMutableSet
79 | mutable.union(objects as Set)
80 | self.transactions = mutable.copy() as! NSSet
81 | }
82 |
83 | open func removeTransactions(_ objects: NSSet) {
84 | let mutable = self.transactions.mutableCopy() as! NSMutableSet
85 | mutable.minus(objects as Set)
86 | self.transactions = mutable.copy() as! NSSet
87 | }
88 |
89 | open func addTransactionsObject(_ value: TransactionManagedObject) {
90 | let mutable = self.transactions.mutableCopy() as! NSMutableSet
91 | mutable.add(value)
92 | self.transactions = mutable.copy() as! NSSet
93 | }
94 |
95 | open func removeTransactionsObject(_ value: TransactionManagedObject) {
96 | let mutable = self.transactions.mutableCopy() as! NSMutableSet
97 | mutable.remove(value)
98 | self.transactions = mutable.copy() as! NSSet
99 | }
100 |
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/Lannister.xcodeproj/xcshareddata/xcschemes/Lannister Beta.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Alamofire (4.8.0)
3 | - BigInt (3.1.0):
4 | - SipHash (~> 1.2)
5 | - BiometricAuthentication (3.0)
6 | - Blockstack (1.0.1):
7 | - CryptoSwift (= 0.15.0)
8 | - PromisesSwift
9 | - STRegex
10 | - Charts (3.4.0):
11 | - Charts/Core (= 3.4.0)
12 | - Charts/Core (3.4.0)
13 | - CryptoSwift (0.15.0)
14 | - EthereumABI (1.1.1):
15 | - BigInt (~> 3.1)
16 | - CryptoSwift (~> 0.13)
17 | - EthereumAddress (~> 1.0.0)
18 | - EthereumAddress (1.0.0):
19 | - CryptoSwift (~> 0.13)
20 | - Groot (3.0.1):
21 | - Groot/Swift (= 3.0.1)
22 | - Groot/Swift (3.0.1)
23 | - MagicalRecord (2.3.3):
24 | - MagicalRecord/Core (= 2.3.3)
25 | - MagicalRecord/Core (2.3.3)
26 | - PromiseKit (6.4.1):
27 | - PromiseKit/CorePromise (= 6.4.1)
28 | - PromiseKit/Foundation (= 6.4.1)
29 | - PromiseKit/UIKit (= 6.4.1)
30 | - PromiseKit/CorePromise (6.4.1)
31 | - PromiseKit/Foundation (6.4.1):
32 | - PromiseKit/CorePromise
33 | - PromiseKit/UIKit (6.4.1):
34 | - PromiseKit/CorePromise
35 | - PromisesObjC (1.2.8)
36 | - PromisesSwift (1.2.8):
37 | - PromisesObjC (= 1.2.8)
38 | - QRCodeReader.swift (10.0.0)
39 | - secp256k1_swift (1.0.3)
40 | - SipHash (1.2.2)
41 | - Starscream (3.1.0)
42 | - STRegex (2.1.0)
43 | - SVProgressHUD (2.2.5)
44 | - SwiftRLP (1.1):
45 | - BigInt (~> 3.1)
46 | - web3swift (2.1.3):
47 | - BigInt (= 3.1)
48 | - CryptoSwift (= 0.15.0)
49 | - EthereumABI (= 1.1.1)
50 | - EthereumAddress (= 1.0.0)
51 | - PromiseKit (= 6.4.1)
52 | - secp256k1_swift (= 1.0.3)
53 | - Starscream (= 3.1.0)
54 | - SwiftRLP (= 1.1)
55 |
56 | DEPENDENCIES:
57 | - Alamofire (= 4.8.0)
58 | - BiometricAuthentication
59 | - Blockstack (= 1.0.1)
60 | - Charts
61 | - Groot (= 3.0.1)
62 | - MagicalRecord (from `https://github.com/magicalpanda/MagicalRecord.git`, tag `v2.3.3`)
63 | - QRCodeReader.swift (~> 10.0.0)
64 | - SVProgressHUD
65 | - web3swift
66 |
67 | SPEC REPOS:
68 | trunk:
69 | - Alamofire
70 | - BigInt
71 | - BiometricAuthentication
72 | - Blockstack
73 | - Charts
74 | - CryptoSwift
75 | - EthereumABI
76 | - EthereumAddress
77 | - Groot
78 | - PromiseKit
79 | - PromisesObjC
80 | - PromisesSwift
81 | - QRCodeReader.swift
82 | - secp256k1_swift
83 | - SipHash
84 | - Starscream
85 | - STRegex
86 | - SVProgressHUD
87 | - SwiftRLP
88 | - web3swift
89 |
90 | EXTERNAL SOURCES:
91 | MagicalRecord:
92 | :git: https://github.com/magicalpanda/MagicalRecord.git
93 | :tag: v2.3.3
94 |
95 | CHECKOUT OPTIONS:
96 | MagicalRecord:
97 | :git: https://github.com/magicalpanda/MagicalRecord.git
98 | :tag: v2.3.3
99 |
100 | SPEC CHECKSUMS:
101 | Alamofire: 3ec537f71edc9804815215393ae2b1a8ea33a844
102 | BigInt: 76b5dfdfa3e2e478d4ffdf161aeede5502e2742f
103 | BiometricAuthentication: e39b1a55040d679d5dbc4fa90d76e067c432de7f
104 | Blockstack: 8dadf3fb26408d2a102690a8e45fc1068b403ab5
105 | Charts: 74c9f256eaf0460c0c416522d1cf8c634ea6b286
106 | CryptoSwift: 769f58a9e89f64e8796c2e59ce5f002dc81a2438
107 | EthereumABI: f040f5429e5a4366d028c88b88d9441e137593af
108 | EthereumAddress: f476e1320dca3a0024431e713ede7a09c7eb7796
109 | Groot: a668afbcf0be88d76c0a26c714cfa4638ceaca66
110 | MagicalRecord: e3dfdee87c234c6d460dbd5d8a32e3cb4de223aa
111 | PromiseKit: 4c76a6506638034e3d7bede97b2ff7743f7bd2dc
112 | PromisesObjC: c119f3cd559f50b7ae681fa59dc1acd19173b7e6
113 | PromisesSwift: 37bad6f4daddb02f7c9c531efe91e8b21c13ee2f
114 | QRCodeReader.swift: 74d0378eb0c7807828552b49626bbc8307495aee
115 | secp256k1_swift: 4fc5c4b2d2c6d21ee8ccb868cdc92da12f38bed9
116 | SipHash: fad90a4683e420c52ef28063063dbbce248ea6d4
117 | Starscream: 08172b481e145289c4930cb567230fb55897cfa4
118 | STRegex: dfa420d93d8c1402956233b3879ec1fc14b45fbe
119 | SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
120 | SwiftRLP: 5512899925f1a9e1c78c902ed3bf857880e814a0
121 | web3swift: 23cc365dd3b28e9b990813965af4bd031e108f64
122 |
123 | PODFILE CHECKSUM: 72dee91fbd733d689e133975f6f7a74082763356
124 |
125 | COCOAPODS: 1.8.1
126 |
--------------------------------------------------------------------------------
/Lannister/App/Utilities/BioAccessController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BioAccessController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 07/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import BiometricAuthentication
12 |
13 | let CancelTitle = "Cancel"
14 | let OKTitle = "OK"
15 | typealias AlertViewController = UIAlertController
16 |
17 | struct AlertAction {
18 |
19 | var title: String = ""
20 | var type: UIAlertAction.Style? = .default
21 | var enable: Bool? = true
22 | var selected: Bool? = false
23 |
24 | init(title: String, type: UIAlertAction.Style? = .default, enable: Bool? = true, selected: Bool? = false) {
25 | self.title = title
26 | self.type = type
27 | self.enable = enable
28 | self.selected = selected
29 | }
30 | }
31 |
32 | extension UIAlertController {
33 | }
34 |
35 | extension UIViewController {
36 |
37 | // Show Alert or Action sheet
38 | func getAlertViewController(type: UIAlertController.Style, with title: String?, message: String?, actions:[AlertAction], showCancel: Bool , actionHandler:@escaping ((_ title: String) -> ())) -> AlertViewController {
39 |
40 | let alertController = UIAlertController(title: title, message: message, preferredStyle: type)
41 |
42 | // items
43 | var actionItems: [UIAlertAction] = []
44 |
45 | // add actions
46 | for (index, action) in actions.enumerated() {
47 |
48 | let actionButton = UIAlertAction(title: action.title, style: action.type!, handler: { (actionButton) in
49 | actionHandler(actionButton.title ?? "")
50 | })
51 |
52 | actionButton.isEnabled = action.enable!
53 | if type == .actionSheet { actionButton.setValue(action.selected, forKey: "checked") }
54 | actionButton.setAssociated(object: index)
55 |
56 | actionItems.append(actionButton)
57 | alertController.addAction(actionButton)
58 | }
59 |
60 | // add cancel button
61 | if showCancel {
62 | let cancelAction = UIAlertAction(title: CancelTitle, style: .cancel, handler: { (action) in
63 | actionHandler(action.title!)
64 | })
65 | alertController.addAction(cancelAction)
66 | }
67 | return alertController
68 | }
69 |
70 |
71 | func showAlert(title: String, message: String) {
72 |
73 | let okAction = AlertAction(title: OKTitle)
74 | let alertController = getAlertViewController(type: .alert, with: title, message: message, actions: [okAction], showCancel: false) { (button) in
75 | }
76 | present(alertController, animated: true, completion: nil)
77 | }
78 |
79 | func showLoginSucessAlert() {
80 | showAlert(title: "Success", message: "Login successful")
81 | }
82 |
83 | func showErrorAlert(message: String) {
84 | showAlert(title: "Error", message: message)
85 | }
86 |
87 | func showGotoSettingsAlert(message: String) {
88 | let settingsAction = AlertAction(title: "Go to settings")
89 |
90 | let alertController = getAlertViewController(type: .alert, with: "Error", message: message, actions: [settingsAction], showCancel: true, actionHandler: { (buttonText) in
91 | if buttonText == CancelTitle { return }
92 |
93 | // open settings
94 | let url = URL(string: UIApplication.openSettingsURLString)
95 | if UIApplication.shared.canOpenURL(url!) {
96 | UIApplication.shared.open(url!, options: [:])
97 | }
98 |
99 | })
100 | present(alertController, animated: true, completion: nil)
101 | }
102 |
103 | // show passcode authentication
104 | func showPasscodeAuthentication(message: String) {
105 |
106 | BioMetricAuthenticator.authenticateWithPasscode(reason: message) { [weak self] (result) in
107 | switch result {
108 | case .success( _):
109 | self?.showLoginSucessAlert() // passcode authentication success
110 | case .failure(let error):
111 | print(error.message())
112 | }
113 | }
114 | }
115 |
116 | }
117 |
118 | /// NSObject associated object
119 | public extension NSObject {
120 |
121 | /// keys
122 | private struct AssociatedKeys {
123 | static var descriptiveName = "associatedObject"
124 | }
125 |
126 | /// set associated object
127 | @objc func setAssociated(object: Any) {
128 | objc_setAssociatedObject(self, &AssociatedKeys.descriptiveName, object, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
129 | }
130 |
131 | /// get associated object
132 | @objc func associatedObject() -> Any? {
133 | return objc_getAssociatedObject(self, &AssociatedKeys.descriptiveName)
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Lannister/Data/Repositories Implementations/WalletRepositoryImpl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WalletRepositoryImpl.swift
3 | // Lannister
4 | //
5 | // Created by Andre Sousa on 12/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 | import CoreData
12 | import Web3swift
13 | import EthereumAddress
14 | import BigInt
15 |
16 | class WalletRepositoryImpl: WalletRepository {
17 |
18 | func getBalance(address: String, success: @escaping(Double) -> Void, failure: @escaping(_ error: Error) -> Void) {
19 |
20 | print("getBalance address \(address)")
21 | DispatchQueue.global(qos: .background).async {
22 | let walletAddress = EthereumAddress(address, ignoreChecksum: true)!
23 | let web3 = Web3.InfuraMainnetWeb3(accessToken: "c7b6351e2ba84d3e94d1b33c14bb9a16") // Mainnet Infura Endpoint Provider
24 | let balanceResult = try! web3.eth.getBalance(address: walletAddress)
25 | let balanceString = Web3.Utils.formatToEthereumUnits(balanceResult, toUnits: .eth, decimals: 3)!
26 |
27 | print("balanceString \(balanceString)")
28 | success(balanceString.doubleValue!)
29 | }
30 | }
31 |
32 | func getBalanceOfToken(address: String, erc20TokenAddress: String, success: @escaping(Double) -> Void, failure: @escaping(_ error: Error) -> Void) {
33 |
34 | DispatchQueue.global(qos: .background).async {
35 | let walletAddress = EthereumAddress(address)! // Your wallet address
36 | let erc20ContractAddress = EthereumAddress(erc20TokenAddress)!
37 | let web3 = Web3.InfuraMainnetWeb3(accessToken: "c7b6351e2ba84d3e94d1b33c14bb9a16") // Mainnet Infura Endpoint Provider
38 | let contract = web3.contract(Web3.Utils.erc20ABI, at: erc20ContractAddress, abiVersion: 2)!
39 | var options = TransactionOptions.defaultOptions
40 | options.from = walletAddress
41 | options.gasPrice = .automatic
42 | options.gasLimit = .automatic
43 | let method = "balanceOf"
44 | let tx = contract.read(
45 | method,
46 | parameters: [walletAddress] as [AnyObject],
47 | extraData: Data(),
48 | transactionOptions: options)!
49 | let tokenBalance = try! tx.call()
50 | let balanceBigUInt = tokenBalance["0"] as! BigUInt
51 | let balanceString = Web3.Utils.formatToEthereumUnits(balanceBigUInt, toUnits: .eth, decimals: 3)!
52 | success(balanceString.doubleValue!)
53 | }
54 | }
55 |
56 | func getTransactions(address: String, success: @escaping(_ transactions: Array?) -> Void, failure: @escaping(_ error: Error) -> Void) {
57 |
58 | let service = EtherScanApiService()
59 |
60 | let params = ["module": "account",
61 | "action": "txlist",
62 | "address": address,
63 | "startblock": 0,
64 | "block": 99999999,
65 | "sort": "asc",
66 | "apikey": service.apiKey] as [String : Any]
67 |
68 | UIApplication.shared.isNetworkActivityIndicatorVisible = true
69 |
70 | service.getTransactions(params: params) { response in
71 |
72 | UIApplication.shared.isNetworkActivityIndicatorVisible = false
73 |
74 | switch (response!.result) {
75 | case .success(let JSON):
76 | print("got transactions: \(JSON)")
77 |
78 | if let status = (JSON as AnyObject).object(forKey: "status") as? String {
79 | if status == "1" {
80 | let transactionsJSON = (JSON as AnyObject).object(forKey: "result") as! NSArray
81 | var transactions : [Transaction] = []
82 | for transactionJSON in transactionsJSON {
83 | if let transactionDic = transactionJSON as? [String: Any] {
84 | var transaction = Transaction(with: transactionDic)
85 | transaction.identifier = transactionDic["blockHash"] as? String
86 | transaction.value = ((transactionDic["value"] as? String)?.doubleValue)! / 1000000000000000000.0
87 | if (transactionDic["from"] as! String).lowercased() == address.lowercased() {
88 | transaction.type = "debit"
89 | transaction.name = transactionDic["to"] as? String
90 | } else {
91 | transaction.type = "credit"
92 | transaction.name = transactionDic["from"] as? String
93 | }
94 | transactions.append(transaction)
95 | }
96 | }
97 | success(transactions)
98 | } else {
99 | success(nil)
100 | }
101 | } else {
102 | success(nil)
103 | }
104 |
105 | case .failure(let error):
106 | print("error getAppointments - > \n \(error.localizedDescription) \n")
107 | failure(error)
108 | }
109 | }
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/AuthController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Blockstack
11 |
12 | class AuthController: UIViewController {
13 |
14 | @IBOutlet weak var signInButton : UIButton!
15 |
16 | override func viewDidLoad() {
17 | super.viewDidLoad()
18 |
19 | signInButton.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.25).cgColor
20 | signInButton.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
21 | signInButton.layer.shadowOpacity = 0.3
22 | signInButton.layer.shadowRadius = 0.0
23 | signInButton.layer.masksToBounds = false
24 | }
25 |
26 | @IBAction func signIn() {
27 |
28 | DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
29 |
30 | UserDefaults.standard.setValue("auth", forKey: "Auth")
31 | let writeAuthScope = AuthScope(rawValue: "store_write")
32 | let publishDataAuthScope = AuthScope(rawValue: "publish_data")
33 |
34 | Blockstack.shared.signIn(redirectURI: URL(string: "https://lannister.capital/redirect-mobile.html")!,
35 | appDomain: URL(string: "https://lannister.capital")!,
36 | manifestURI: nil,
37 | scopes: [writeAuthScope!, publishDataAuthScope!]) { authResult in
38 | switch authResult {
39 | case .success(let userData):
40 | print("Sign in SUCCESS", userData.profile?.name as Any)
41 | DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
42 | AppDelegate.shared.updateCurrencies()
43 | self.checkUserData()
44 | })
45 | case .cancelled:
46 | print("Sign in CANCELLED")
47 | case .failed(let error):
48 | print("Sign in FAILED, error: ", error ?? "n/a")
49 | }
50 | }
51 | })
52 | }
53 |
54 | @IBAction func syncLater() {
55 |
56 | DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
57 |
58 | UserDefaults.standard.setValue("skippedAuth", forKey: "Auth")
59 | UserDefaults.standard.synchronize()
60 | AppDelegate.shared.updateCurrencies()
61 | AppDelegate.shared.rootViewController.switchToMainScreen()
62 | })
63 | }
64 |
65 | func checkUserData() {
66 |
67 | BlockstackApiService().checkUserData { hasData in
68 | if hasData {
69 | DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
70 | let alert = UIAlertController(title: "You already have holdings on your Blockstack account.",
71 | message: "You have to choose between keeping the ones you already have on your Blockstack account or overwrite them with the ones you have locally on the iPhone app right now.",
72 | preferredStyle: .alert)
73 | alert.addAction(UIAlertAction(title: "Use local data", style: UIAlertAction.Style.default, handler: { _ in
74 | self.writeNewData()
75 | }))
76 | alert.addAction(UIAlertAction(title: "Use data from Blockstack", style: UIAlertAction.Style.default, handler: { _ in
77 | self.readData()
78 | }))
79 | self.present(alert, animated: true, completion: nil)
80 | })
81 | } else {
82 | self.writeNewData()
83 | }
84 | }
85 | }
86 |
87 | func writeNewData() {
88 |
89 | BlockstackApiService().send(returns: { errorMessage in
90 | DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
91 | if errorMessage != nil {
92 | let alert = UIAlertController(title: "Error",
93 | message: errorMessage,
94 | preferredStyle: .alert)
95 | alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
96 | self.present(alert, animated: true, completion: nil)
97 | }
98 | AppDelegate.shared.rootViewController.switchToMainScreen()
99 | })
100 | })
101 | }
102 |
103 | func readData() {
104 | BlockstackApiService().sync(returns: { errorMessage in
105 | DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
106 | if errorMessage != nil {
107 | let alert = UIAlertController(title: "Error",
108 | message: errorMessage,
109 | preferredStyle: .alert)
110 | alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
111 | self.present(alert, animated: true, completion: nil)
112 | }
113 | AppDelegate.shared.rootViewController.switchToMainScreen()
114 | })
115 | })
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/Lannister/Data/API/Services/BlockstackApiService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlockstackApiService.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 14/05/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Blockstack
11 | import MagicalRecord
12 | import Groot
13 |
14 | class BlockstackApiService: BaseApiService {
15 |
16 | let dbVersion = "1.0.4"
17 |
18 | override init() {
19 | super.init()
20 | }
21 |
22 | func send(returns: @escaping(String?) -> Void) {
23 |
24 | let holdingsManagedObjects = HoldingManagedObject.mr_findAll(in: NSManagedObjectContext.mr_default()) as! [HoldingManagedObject]
25 | var holdingsArray : [Any] = []
26 | for holding in holdingsManagedObjects {
27 | var holdingDic =
28 | ["hex_color" : holding.hex_color!,
29 | "id": holding.id!,
30 | "name": holding.name!] as [String : Any]
31 | if holding.value != nil {
32 | holdingDic["value"] = holding.value!.doubleValue
33 | }
34 | if holding.currency?.code != nil {
35 | holdingDic["currency_code"] = holding.currency!.code!
36 | }
37 | if holding.address != nil {
38 | holdingDic["address"] = holding.address!
39 | }
40 | holdingsArray.append(holdingDic)
41 | }
42 | let versionString = dbVersion
43 | let lannisterDictionary = ["db_version": versionString, "holdings": holdingsArray] as [String : Any]
44 | guard let data = try? JSONSerialization.data(withJSONObject: lannisterDictionary, options: []) else {
45 | return
46 | }
47 | let jsonString = String(data: data, encoding: String.Encoding.utf8)!
48 | Blockstack.shared.putFile(to: "db.json", text: jsonString, encrypt: true, completion: { (file, error) in
49 | print("overwrite db json")
50 | if error != nil {
51 | print("error \(String(describing: error?.localizedDescription))")
52 | returns(error?.localizedDescription)
53 | } else {
54 | returns(nil)
55 | }
56 | })
57 | }
58 |
59 | func sync(returns: @escaping(String?) -> Void) {
60 |
61 | Blockstack.shared.getFile(at: "db.json", decrypt: true) { (response, error) in
62 | if let decryptedResponse = response as? DecryptedValue {
63 | let responseString = decryptedResponse.plainText
64 | let versionString = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
65 | if let parsedResponse = responseString!.parseJSONString as? [String: Any] {
66 | if let version = parsedResponse["db_version"] as? String {
67 | if version != versionString {
68 | let errorMessage = "You're using on old version of the app. Please update the app on the App Store to be able to sync your holdings."
69 | returns(errorMessage)
70 | } else {
71 | if let parsedHoldings = parsedResponse["holdings"] as? Array {
72 | let localHoldings = HoldingManagedObject.mr_findAll(in: NSManagedObjectContext.mr_default()) as! [HoldingManagedObject]
73 | let remoteHoldings = parsedHoldings as! Array
74 | let remoteHoldingsIds = remoteHoldings.map({ (holding: JSONDictionary) -> String in
75 | holding["id"] as! String
76 | })
77 | for holding in localHoldings {
78 | if holding.address == nil && !remoteHoldingsIds.contains(holding.id!) {
79 | holding.mr_deleteEntity(in: NSManagedObjectContext.mr_default())
80 | }
81 | }
82 | _ = self.parseHoldings(holdings: parsedHoldings as NSArray)
83 | NSManagedObjectContext.mr_default().mr_saveToPersistentStoreAndWait()
84 | returns(nil)
85 | }
86 | }
87 | }
88 | }
89 | print("error \(String(describing: error?.localizedDescription))")
90 | returns(error?.localizedDescription)
91 | }
92 | if error != nil {
93 | print("error \(String(describing: error?.localizedDescription))")
94 | }
95 | }
96 | }
97 |
98 | func checkUserData(returns: @escaping(Bool) -> Void) {
99 |
100 | Blockstack.shared.getFile(at: "db.json", decrypt: true) { (response, error) in
101 | if (response as? DecryptedValue) != nil {
102 | returns(true)
103 | } else {
104 | returns(false)
105 | }
106 | }
107 | }
108 |
109 | internal func parseHoldings(holdings: NSArray) -> Array? {
110 |
111 | var newHoldings : Array? = Array()
112 | for holding in holdings {
113 | let holdingJson : JSONDictionary = holding as! JSONDictionary
114 | var newHolding = HoldingManagedObject.mr_findFirst(byAttribute: "id", withValue: holdingJson["id"]!, in: NSManagedObjectContext.mr_default())
115 | if newHolding == nil {
116 | newHolding = HoldingManagedObject(context: NSManagedObjectContext.mr_default())
117 | }
118 | if let address = holdingJson["address"] as? String {
119 | newHolding!.address = address
120 | }
121 | newHolding!.id = holdingJson["id"] as? String
122 | newHolding!.hex_color = holdingJson["hex_color"] as? String
123 | newHolding!.name = holdingJson["name"] as? String
124 | newHolding!.value = holdingJson["value"] as? NSNumber
125 | if holdingJson["currency_code"] != nil {
126 | let currency = CurrencyManagedObject.mr_findFirst(byAttribute: "code", withValue: holdingJson["currency_code"]!, in: NSManagedObjectContext.mr_default())
127 | newHolding!.currency = currency
128 | }
129 |
130 | newHoldings?.append(newHolding!)
131 | }
132 | return newHoldings
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/db.xcdatamodeld/db 2.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/Lannister/Data/Database/db.xcdatamodeld/db.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/ETHCreateHoldingController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ETHCreateHoldingController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 08/07/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import QRCodeReader
11 |
12 | protocol ETHDelegate {
13 | func importedAddress(address: String)
14 | func insertedValue(value: Double)
15 | }
16 |
17 | class ETHCreateHoldingController: UIViewController {
18 |
19 | @IBOutlet weak var valueTextField : UITextField!
20 | @IBOutlet weak var tableView : UITableView!
21 | var toolBar = UIToolbar()
22 | var delegate : ETHDelegate!
23 | var holdingValue : Double!
24 |
25 |
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 |
29 | navigationItem.title = "ETH"
30 |
31 | navigationController!.navigationBar.titleTextAttributes =
32 | [NSAttributedString.Key.font: UIFont(name: "AvenirNext-Medium", size: 18)!,
33 | NSAttributedString.Key.foregroundColor : UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)]
34 | navigationController?.navigationBar.tintColor = UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)
35 |
36 | navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
37 | navigationController?.navigationBar.shadowImage = UIImage()
38 |
39 | if holdingValue != nil {
40 | valueTextField.text = "\(holdingValue!)"
41 | }
42 |
43 | let btnDone = UIBarButtonItem(title: "Save", style: .done, target: self, action: #selector(saveValue))
44 | let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
45 | let clearButton = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearValue))
46 |
47 | toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 44))
48 | toolBar.isUserInteractionEnabled = true
49 | toolBar.barStyle = .default
50 | toolBar.isTranslucent = false
51 | toolBar.items = [clearButton, spaceButton, btnDone]
52 |
53 | valueTextField.inputAccessoryView = toolBar
54 |
55 | let tap = UITapGestureRecognizer(target: self, action: #selector(removeKeyboard))
56 | tap.cancelsTouchesInView = false
57 | view.addGestureRecognizer(tap)
58 | }
59 |
60 | @objc func removeKeyboard() {
61 | clearValue()
62 | self.view.endEditing(true)
63 | }
64 |
65 | @objc func clearValue() {
66 | valueTextField.text = ""
67 | }
68 |
69 | @objc func saveValue() {
70 | if let newValue = valueTextField.text?.doubleValue {
71 | delegate.insertedValue(value: newValue)
72 | navigationController?.popViewController(animated: true)
73 | }
74 | }
75 |
76 | func showInvalidAddressWarning() {
77 |
78 | DispatchQueue.main.async {
79 | let msg = "Invalid address"
80 | let alert = UIAlertController(title: "Error",
81 | message: msg,
82 | preferredStyle: .alert)
83 | alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
84 | self.present(alert, animated: true, completion: nil)
85 | }
86 | }
87 |
88 | func scanAction() {
89 |
90 | let customQRCodeController = storyboard?.instantiateViewController(withIdentifier: "customQRCodeVC") as! CustomQRCodeController
91 | customQRCodeController.delegate = self
92 | self.present(customQRCodeController, animated: true, completion: nil)
93 | }
94 | }
95 |
96 | extension ETHCreateHoldingController : UITableViewDataSource {
97 |
98 | func numberOfSections(in tableView: UITableView) -> Int {
99 | return 1
100 | }
101 |
102 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
103 | return 2
104 | }
105 |
106 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
107 |
108 | if indexPath.row == 0 {
109 | let cell = tableView.dequeueReusableCell(withIdentifier: "clipboardCellId", for: indexPath) as! ETHCell
110 |
111 | if let pasteboardString = UIPasteboard.general.string {
112 | let first2 = String(pasteboardString.prefix(2))
113 | if first2 == "0x" {
114 | cell.addressLabel.text = UIPasteboard.general.string
115 | }
116 | }
117 | return cell
118 | } else {
119 | let cell = tableView.dequeueReusableCell(withIdentifier: "scanCellId", for: indexPath)
120 | return cell
121 | }
122 | }
123 | }
124 |
125 | extension ETHCreateHoldingController : UITableViewDelegate {
126 |
127 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
128 |
129 | tableView.deselectRow(at: indexPath, animated: true)
130 |
131 | if indexPath.row == 0 {
132 | if let pasteboardString = UIPasteboard.general.string {
133 | let first2 = String(pasteboardString.prefix(2))
134 | if first2 == "0x" {
135 | delegate.importedAddress(address: pasteboardString)
136 | navigationController?.popViewController(animated: true)
137 | return
138 | }
139 | }
140 | showInvalidAddressWarning()
141 |
142 | } else {
143 | scanAction()
144 | }
145 | }
146 | }
147 |
148 | extension ETHCreateHoldingController : CustomQRCodeControllerDelegate {
149 |
150 | func scanned(result: QRCodeReaderResult?) {
151 |
152 | if let resultString = result?.value {
153 | let first2 = String(resultString.prefix(2))
154 | let ethereumUrl = String(resultString.prefix(9))
155 | if first2 == "0x" {
156 | DispatchQueue.main.async {
157 | self.delegate.importedAddress(address: resultString)
158 | self.navigationController?.popViewController(animated: true)
159 | }
160 | } else if ethereumUrl == "ethereum:" {
161 | DispatchQueue.main.async {
162 | let range = resultString.range(of: ethereumUrl)
163 | self.delegate.importedAddress(address: String(resultString[range!.upperBound...]))
164 | self.navigationController?.popViewController(animated: true)
165 | }
166 | } else {
167 | showInvalidAddressWarning()
168 | }
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/ColorCodesController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorCodesController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol ColorCodesDelegate {
12 | func newColorCode(hex: String)
13 | }
14 |
15 | class ColorCodesController: UIViewController {
16 |
17 | @IBOutlet weak var tableView : UITableView!
18 | var delegate : ColorCodesDelegate!
19 | var activeField : UITextField?
20 | var hexString : String?
21 | let selection = UISelectionFeedbackGenerator()
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 |
26 | // Do any additional setup after loading the view.
27 | navigationItem.title = "Color Codes"
28 | navigationController!.navigationBar.titleTextAttributes =
29 | [NSAttributedString.Key.font: UIFont(name: "AvenirNext-Medium", size: 18)!,
30 | NSAttributedString.Key.foregroundColor : UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)]
31 | navigationController?.navigationBar.tintColor = UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)
32 |
33 | registerForKeyboardNotifications()
34 | }
35 |
36 | override func viewDidAppear(_ animated: Bool) {
37 | super.viewDidAppear(animated)
38 |
39 | if hexString != nil {
40 | if Colors.isCustomColor(hex: hexString!) {
41 | if let cell = tableView.cellForRow(at: IndexPath(row: 6, section: 0)) as? ColorCodeCustomCell {
42 | cell.colorCodeTextField.becomeFirstResponder()
43 | }
44 | }
45 | }
46 | }
47 |
48 | func registerForKeyboardNotifications() {
49 | NotificationCenter.default.addObserver(self,
50 | selector: #selector(keyboardWasShown(aNotification:)),
51 | name: UIResponder.keyboardDidShowNotification,
52 | object: nil)
53 | NotificationCenter.default.addObserver(self,
54 | selector: #selector(keyboardWillBeHidden(aNotification:)),
55 | name: UIResponder.keyboardWillHideNotification,
56 | object: nil)
57 | }
58 |
59 | @objc func keyboardWasShown(aNotification: NSNotification) {
60 | let info = aNotification.userInfo as! [String: AnyObject],
61 | kbSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue.size,
62 | contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)
63 |
64 | self.tableView.contentInset = contentInsets
65 | self.tableView.scrollIndicatorInsets = contentInsets
66 |
67 | // If active text field is hidden by keyboard, scroll it so it's visible
68 | // Your app might not need or want this behavior.
69 | var aRect = self.view.frame
70 | aRect.size.height -= kbSize.height
71 |
72 | if !aRect.contains(activeField!.frame.origin) {
73 | self.tableView.scrollRectToVisible(activeField!.frame, animated: true)
74 | }
75 | }
76 |
77 | @objc func keyboardWillBeHidden(aNotification: NSNotification) {
78 | let contentInsets = UIEdgeInsets.zero
79 | self.tableView.contentInset = contentInsets
80 | self.tableView.scrollIndicatorInsets = contentInsets
81 | }
82 |
83 | }
84 |
85 |
86 | extension ColorCodesController : UITableViewDataSource {
87 |
88 | func numberOfSections(in tableView: UITableView) -> Int {
89 | return 1
90 | }
91 |
92 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
93 | return 7
94 | }
95 |
96 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
97 |
98 | if indexPath.row == 6 {
99 | let cell = tableView.dequeueReusableCell(withIdentifier: "colorCodeCustomCellId", for: indexPath) as! ColorCodeCustomCell
100 | cell.selectionStyle = .none
101 | cell.colorCodeView.layer.borderWidth = 1
102 | cell.colorCodeView.layer.borderColor = UIColor(red: 151/255, green: 151/255, blue: 151/255, alpha: 1).cgColor
103 | if hexString != nil {
104 | if Colors.isCustomColor(hex: hexString!) {
105 | var hex = hexString!
106 | if (hex.hasPrefix("#")) {
107 | hex.remove(at: hex.startIndex)
108 | }
109 | cell.colorCodeTextField.text = "#\(hex)"
110 | cell.colorCodeView.backgroundColor = Colors.hexStringToUIColor(hex: hex)
111 | }
112 | }
113 | return cell
114 |
115 | } else {
116 | let cell = tableView.dequeueReusableCell(withIdentifier: "colorCodePresetCellId", for: indexPath) as! ColorCodePresetCell
117 | if indexPath.row == 0 {
118 | cell.colorCodeLabel.text = "Gold - #FFBF00"
119 | cell.colorCodeView.backgroundColor = UIColor(red: 255/255, green: 191/255, blue: 0, alpha: 1)
120 | } else if indexPath.row == 1 {
121 | cell.colorCodeLabel.text = "Silver - #7C8288"
122 | cell.colorCodeView.backgroundColor = UIColor(red: 124/255, green: 130/255, blue: 136/255, alpha: 1)
123 | } else if indexPath.row == 2 {
124 | cell.colorCodeLabel.text = "Ruby - #E51522"
125 | cell.colorCodeView.backgroundColor = UIColor(red: 229/255, green: 21/255, blue: 34/255, alpha: 1)
126 | } else if indexPath.row == 3 {
127 | cell.colorCodeLabel.text = "Emmerald - #00B382"
128 | cell.colorCodeView.backgroundColor = UIColor(red: 0, green: 179/255, blue: 130/255, alpha: 1)
129 | } else if indexPath.row == 4 {
130 | cell.colorCodeLabel.text = "Saphire - #1538C0"
131 | cell.colorCodeView.backgroundColor = UIColor(red: 21/255, green: 56/255, blue: 192/255, alpha: 1)
132 | } else if indexPath.row == 5 {
133 | cell.colorCodeLabel.text = "Amethyst - #6F0DBE"
134 | cell.colorCodeView.backgroundColor = UIColor(red: 111/255, green: 13/255, blue: 190/255, alpha: 1)
135 | }
136 | return cell
137 |
138 | }
139 | }
140 | }
141 |
142 | extension ColorCodesController : UITableViewDelegate {
143 |
144 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
145 |
146 | if indexPath.row < 6 {
147 | tableView.deselectRow(at: indexPath, animated: true)
148 | let cell = tableView.cellForRow(at: indexPath) as! ColorCodePresetCell
149 | selection.selectionChanged()
150 | delegate.newColorCode(hex: cell.colorCodeLabel.text!)
151 | navigationController?.popViewController(animated: true)
152 | } else {
153 | let cell = tableView.cellForRow(at: indexPath) as! ColorCodeCustomCell
154 | cell.colorCodeTextField.becomeFirstResponder()
155 | }
156 | }
157 | }
158 |
159 | extension ColorCodesController : UITextFieldDelegate {
160 |
161 | func textFieldShouldReturn(_ textField: UITextField) -> Bool {
162 | view.endEditing(true)
163 | if textField.text! != "" {
164 | selection.selectionChanged()
165 | delegate.newColorCode(hex: textField.text!)
166 | navigationController?.popViewController(animated: true)
167 | }
168 | return true
169 | }
170 |
171 | func textFieldDidBeginEditing(_ textField: UITextField) {
172 | self.activeField = textField
173 | }
174 |
175 | func textFieldDidEndEditing(_ textField: UITextField) {
176 | self.activeField = nil
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/CreateTransactionController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CreateTransactionController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 | import Blockstack
12 |
13 | protocol CreateTransactionDelegate {
14 | func newTransaction(newHolding: Holding)
15 | }
16 |
17 | class CreateTransactionController: UIViewController {
18 |
19 | @IBOutlet weak var tableView : UITableView!
20 | var transaction : Transaction!
21 | var holding : Holding!
22 | var transactionType = ["Credit", "Debit"]
23 | var selectedTransaction : String! = "Credit"
24 | var transactionPickerView : UIPickerView!
25 | var toolBar = UIToolbar()
26 | var delegate : CreateTransactionDelegate!
27 | let impact = UIImpactFeedbackGenerator()
28 | let selection = UISelectionFeedbackGenerator()
29 |
30 | override func viewDidLoad() {
31 | super.viewDidLoad()
32 |
33 | navigationController!.navigationBar.titleTextAttributes =
34 | [NSAttributedString.Key.font: UIFont(name: "AvenirNext-Medium", size: 18)!,
35 | NSAttributedString.Key.foregroundColor : UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)]
36 | navigationController?.navigationBar.tintColor = UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)
37 |
38 | transactionPickerView = UIPickerView(frame: CGRect(x: 0, y: view.frame.size.height, width: view.frame.size.width, height: 200))
39 | transactionPickerView.delegate = self
40 | transactionPickerView.dataSource = self as UIPickerViewDataSource
41 | transactionPickerView.showsSelectionIndicator = true
42 |
43 | let btnDone = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(removeKeyboard))
44 | let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
45 |
46 | toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 44))
47 | toolBar.isUserInteractionEnabled = true
48 | toolBar.barStyle = .default
49 | toolBar.isTranslucent = false
50 | toolBar.items = [spaceButton, btnDone]
51 | }
52 |
53 | @objc func removeKeyboard() {
54 | self.view.endEditing(true)
55 | }
56 |
57 | @IBAction func save() {
58 |
59 | let indexPath = IndexPath(row: 0, section: 0)
60 | let cell = tableView.cellForRow(at: indexPath) as! TransactionNameCell
61 | let transactionNameTextField = cell.transactionNameTextField
62 |
63 | if transactionNameTextField!.text == "" {
64 | impact.impactOccurred()
65 | let alert = UIAlertController(title: "Oops!", message: "Enter a name for this transaction.", preferredStyle: .alert)
66 | alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: ""), style: .default, handler: nil))
67 | self.present(alert, animated: true, completion: nil)
68 | return
69 | }
70 |
71 | let indexPathValue = IndexPath(row: 2, section: 0)
72 | let cellForValue = tableView.cellForRow(at: indexPathValue) as! TotalValueCell
73 | let valueTextField = cellForValue.totalValueTextField
74 | if valueTextField?.text?.doubleValue == nil || valueTextField?.text?.doubleValue == 0 {
75 | impact.impactOccurred()
76 | let alert = UIAlertController(title: "Oops!", message: "Invalid value.", preferredStyle: .alert)
77 | alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: ""), style: .default, handler: nil))
78 | self.present(alert, animated: true, completion: nil)
79 | return
80 | }
81 |
82 | print("save")
83 |
84 | var newTransaction : TransactionManagedObject
85 | if transaction == nil {
86 | newTransaction = TransactionManagedObject(context: NSManagedObjectContext.mr_default())
87 | newTransaction.id = newTransaction.objectID.uriRepresentation().lastPathComponent
88 | let transactionDate = Date()
89 | let dateFormatter = DateFormatter()
90 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
91 | let dateString = dateFormatter.string(from: transactionDate)
92 | newTransaction.date = dateString
93 | } else {
94 | newTransaction = TransactionManagedObject.mr_findFirst(byAttribute: "id", withValue: transaction.identifier!, in: NSManagedObjectContext.mr_default())!
95 | }
96 |
97 | newTransaction.name = transactionNameTextField!.text
98 |
99 | let holdingManagedObject = HoldingManagedObject.mr_findFirst(byAttribute: "name", withValue: holding.name!, in: NSManagedObjectContext.mr_default())
100 | newTransaction.holding = holdingManagedObject
101 |
102 | if selectedTransaction == "Credit" {
103 | newTransaction.type = "credit"
104 | } else {
105 | newTransaction.type = "debit"
106 | }
107 |
108 | if let totalValue = valueTextField?.text?.doubleValue {
109 | newTransaction.value = totalValue
110 | if selectedTransaction == "Credit" {
111 | print("totalValue \(totalValue)")
112 | if transaction == nil {
113 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue + totalValue as NSNumber
114 | } else {
115 | if transaction.type == "credit" {
116 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue + (totalValue-transaction.value!) as NSNumber
117 | } else {
118 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue + (totalValue+transaction.value) as NSNumber
119 | }
120 | }
121 | } else {
122 | if transaction == nil {
123 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue - totalValue as NSNumber
124 | } else {
125 | if transaction.type == "debit" {
126 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue - (totalValue-transaction.value!) as NSNumber
127 | } else {
128 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue - (totalValue+transaction.value!) as NSNumber
129 | }
130 | }
131 | }
132 |
133 | }
134 |
135 | NSManagedObjectContext.mr_default().mr_saveToPersistentStoreAndWait()
136 | selection.selectionChanged()
137 |
138 | if Blockstack.shared.isUserSignedIn() {
139 | BlockstackApiService().send { errorMessage in
140 | if errorMessage != nil {
141 | self.impact.impactOccurred()
142 | let alert = UIAlertController(title: "Error",
143 | message: errorMessage,
144 | preferredStyle: .alert)
145 | alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
146 | self.present(alert, animated: true, completion: nil)
147 | }
148 | }
149 | }
150 |
151 | if delegate != nil {
152 | var newHolding = HoldingDto().holding(from: holdingManagedObject!)
153 | newHolding = HoldingsUseCase(with: HoldingsRepositoryImpl()).updateHoldingWithComputedProperties(holding: newHolding)
154 | delegate.newTransaction(newHolding: newHolding)
155 | }
156 |
157 | navigationController?.popViewController(animated: true)
158 | }
159 | }
160 |
161 | extension CreateTransactionController : UITableViewDataSource {
162 |
163 | func numberOfSections(in tableView: UITableView) -> Int {
164 | return 1
165 | }
166 |
167 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
168 | return 3
169 | }
170 |
171 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
172 |
173 | if indexPath.row == 0 {
174 | let cell = tableView.dequeueReusableCell(withIdentifier: "transactionNameCellId", for: indexPath) as! TransactionNameCell
175 | if transaction != nil {
176 | cell.transactionNameTextField.text = transaction.name
177 | }
178 | return cell
179 |
180 | } else if indexPath.row == 1 {
181 | let cell = tableView.dequeueReusableCell(withIdentifier: "typeCellId", for: indexPath) as! TransactionTypeCell
182 | if transaction?.type == "debit" {
183 | cell.transactionTypeTextField.text = "Debit"
184 | }
185 | cell.transactionTypeTextField.inputView = transactionPickerView
186 | cell.transactionTypeTextField.inputAccessoryView = toolBar
187 | return cell
188 |
189 | } else {
190 | let cell = tableView.dequeueReusableCell(withIdentifier: "valueCellId", for: indexPath) as! TotalValueCell
191 | cell.currencyLabel.text = holding.currency!.symbol
192 | if transaction != nil {
193 | cell.totalValueTextField.text = "\(transaction.value!)"
194 | }
195 | return cell
196 | }
197 | }
198 | }
199 |
200 | extension CreateTransactionController : UITableViewDelegate {
201 |
202 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
203 |
204 | tableView.deselectRow(at: indexPath, animated: true)
205 | if indexPath.row == 1 {
206 | let cell = tableView.cellForRow(at: indexPath) as! TransactionTypeCell
207 | cell.transactionTypeTextField.becomeFirstResponder()
208 | } else {
209 | removeKeyboard()
210 | }
211 | }
212 | }
213 |
214 | extension CreateTransactionController : UITextFieldDelegate {
215 |
216 | func textFieldShouldReturn(_ textField: UITextField) -> Bool {
217 | view.endEditing(true)
218 | return true
219 | }
220 | }
221 |
222 | extension CreateTransactionController : UIPickerViewDataSource {
223 |
224 | func numberOfComponents(in pickerView: UIPickerView) -> Int {
225 | return 1
226 | }
227 |
228 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
229 | return transactionType.count
230 | }
231 |
232 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
233 | return transactionType[row]
234 | }
235 | }
236 |
237 | extension CreateTransactionController : UIPickerViewDelegate {
238 |
239 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
240 | let cell = tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as! TransactionTypeCell
241 | cell.transactionTypeTextField.text = transactionType[row]
242 | selectedTransaction = transactionType[row]
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/Lannister/App/Controllers/HoldingController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HoldingController.swift
3 | // Lannister
4 | //
5 | // Created by André Sousa on 26/04/2019.
6 | // Copyright © 2019 André Sousa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MagicalRecord
11 | import Charts
12 | import Blockstack
13 | import Web3swift
14 |
15 | class HoldingController: UIViewController {
16 |
17 | var euroTotalValue : Double!
18 | @IBOutlet weak var barView : UIView!
19 | @IBOutlet weak var valueLabel : UILabel!
20 | @IBOutlet weak var defaultCurrencyValueLabel : UILabel!
21 | @IBOutlet weak var pieChartViewTopConstraint : NSLayoutConstraint!
22 | @IBOutlet weak var pieChartView : PieChartView!
23 | @IBOutlet weak var tableView : UITableView!
24 | var holding : Holding!
25 | var currency : Currency!
26 | var transactions : [Transaction]! = []
27 | var pieChartDataEntries = [PieChartDataEntry]()
28 | var pieChartDataColors = [UIColor]()
29 | var numberFormatter = NumberFormatter()
30 | var percentFormatter = NumberFormatter()
31 |
32 |
33 | override func viewDidLoad() {
34 | super.viewDidLoad()
35 |
36 | navigationItem.title = "Holding"
37 |
38 | // Set number formatter display
39 | numberFormatter.numberStyle = .decimal
40 | numberFormatter.minimumFractionDigits = 2
41 | numberFormatter.maximumFractionDigits = 2
42 |
43 | percentFormatter.numberStyle = .decimal
44 | percentFormatter.minimumFractionDigits = 0
45 | percentFormatter.maximumFractionDigits = 2
46 |
47 | navigationController!.navigationBar.titleTextAttributes =
48 | [NSAttributedString.Key.font: UIFont(name: "AvenirNext-Medium", size: 18)!,
49 | NSAttributedString.Key.foregroundColor : UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)]
50 | navigationController?.navigationBar.tintColor = UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)
51 |
52 | navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
53 | navigationController?.navigationBar.shadowImage = UIImage()
54 |
55 | let editButton = UIBarButtonItem()
56 | editButton.title = "Edit"
57 | editButton.target = self
58 | editButton.action = #selector(editHolding)
59 | navigationItem.rightBarButtonItem = editButton
60 |
61 | let backItem = UIBarButtonItem()
62 | backItem.title = ""
63 | navigationItem.backBarButtonItem = backItem
64 |
65 | updateHolding()
66 | updatePieChart()
67 | }
68 |
69 | override func viewWillAppear(_ animated: Bool) {
70 | super.viewWillAppear(animated)
71 |
72 | updateTransactions()
73 | }
74 |
75 | func updateHolding() {
76 |
77 | navigationItem.title = holding.name
78 | barView.backgroundColor = Colors.hexStringToUIColor(hex: holding.hexColor)
79 | euroTotalValue = PortfolioUseCase(with: PortfolioRepositoryImpl()).getEuroTotalValue()
80 | barView.backgroundColor = Colors.hexStringToUIColor(hex: holding.hexColor)
81 | navigationItem.title = holding.name
82 |
83 | var holdingCurrency = holding.currency
84 | var holdingValue : Double? = holding.value
85 | if holding.address != nil {
86 | // Get ETH
87 | let token = getToken()
88 | holdingValue = token.value
89 | holdingCurrency = token.currency
90 | currency = holdingCurrency
91 | } else {
92 | currency = holding.currency
93 | }
94 |
95 | let formattedNumber = numberFormatter.string(for: NSNumber(value: holdingValue!))
96 | valueLabel.text = String(format: "%@%@", holdingCurrency!.symbol, formattedNumber ?? "--")
97 |
98 | if holdingCurrency!.symbol == Currencies.getDefaultCurrencySymbol() {
99 | // remove label
100 | if defaultCurrencyValueLabel != nil {
101 | defaultCurrencyValueLabel.removeFromSuperview()
102 | defaultCurrencyValueLabel = nil
103 | }
104 | pieChartViewTopConstraint.constant = 8
105 | } else {
106 | let euroValue = Currencies.getEuroValue(value: holdingValue!, currency: holdingCurrency!)
107 | let currencyValue = euroValue * Currencies.getDefaultCurrencyEuroRate()
108 | let formattedNumber = numberFormatter.string(for: NSNumber(value: currencyValue))
109 | defaultCurrencyValueLabel.text = String(format: "%@%@", Currencies.getDefaultCurrencySymbol(), formattedNumber ?? "--")
110 | }
111 | }
112 |
113 | func updatePieChart() {
114 |
115 | pieChartDataEntries.removeAll()
116 | pieChartDataColors.removeAll()
117 |
118 | var holdingCurrency = holding.currency
119 | var holdingValue : Double? = holding.value
120 | if holding.address != nil {
121 | // Get ETH
122 | let token = getToken()
123 | holdingValue = token.value
124 | holdingCurrency = token.currency
125 | }
126 | if holdingValue! < 0 {
127 | holdingValue = 0
128 | }
129 |
130 | let euroValue = Currencies.getEuroValue(value: holdingValue!, currency: holdingCurrency!)
131 |
132 | let pieChartDataEntry = PieChartDataEntry(value: euroValue, label: nil)
133 | pieChartDataEntries.append(pieChartDataEntry)
134 | pieChartDataColors.append(Colors.hexStringToUIColor(hex: holding.hexColor))
135 |
136 | let secondPieChartDataEntry = PieChartDataEntry(value: euroTotalValue-euroValue, label: nil)
137 | pieChartDataEntries.append(secondPieChartDataEntry)
138 | pieChartDataColors.append(UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 0.7))
139 |
140 | pieChartView.chartDescription?.text = ""
141 | let percentage = percentFormatter.string(for: NSNumber(value: euroValue/euroTotalValue*100))
142 | let attributedString = NSAttributedString(string: "\(percentage ?? "")%",
143 | attributes: [ NSAttributedString.Key.font: UIFont(name: "AvenirNext-DemiBold", size: 20)!,
144 | NSAttributedString.Key.foregroundColor: UIColor(red: 118/255, green: 134/255, blue: 162/255, alpha: 1)])
145 | pieChartView.centerAttributedText = attributedString
146 | pieChartView.drawHoleEnabled = true
147 | pieChartView.holeColor = UIColor.clear
148 | pieChartView.drawEntryLabelsEnabled = false
149 | pieChartView.holeRadiusPercent = 0.80
150 | pieChartView.legend.enabled = false
151 |
152 | let chartDataSet = PieChartDataSet(entries: pieChartDataEntries, label: nil)
153 | chartDataSet.colors = pieChartDataColors
154 | chartDataSet.drawValuesEnabled = false
155 | let chartData = PieChartData(dataSet: chartDataSet)
156 | pieChartView.data = chartData
157 | }
158 |
159 | func updateTransactions() {
160 |
161 | if holding.address == nil {
162 | transactions = holding.transactions
163 | tableView.reloadData()
164 | } else {
165 | // Get ETH token
166 | let token = getToken()
167 |
168 | if token.transactions != nil {
169 | self.transactions = token.transactions
170 | self.tableView.reloadData()
171 | } else {
172 | getTransactions()
173 | }
174 | }
175 | }
176 |
177 | func getTransactions() {
178 |
179 | WalletUseCase(with: WalletRepositoryImpl()).getTransactions(address: holding.address!, success: { transactionsObjects in
180 | DispatchQueue.main.async {
181 | if transactionsObjects != nil {
182 | self.transactions = transactionsObjects
183 | self.tableView.reloadData()
184 |
185 | // save new transactions to local db
186 | let oldTransactions = self.getTokenManagedObject().transactions
187 | if (oldTransactions?.count)! > 0 {
188 | for transaction in oldTransactions! {
189 | (transaction as! TransactionManagedObject).mr_deleteEntity(in: NSManagedObjectContext.mr_default())
190 | }
191 | }
192 | for transaction in self.transactions {
193 | let newTransaction = TransactionManagedObject(context: NSManagedObjectContext.mr_default())
194 | newTransaction.name = transaction.name
195 | newTransaction.value = transaction.value
196 | newTransaction.type = transaction.type
197 | newTransaction.id = transaction.identifier
198 | }
199 | NSManagedObjectContext.mr_default().mr_saveToPersistentStoreAndWait()
200 |
201 | } else {
202 | let token = self.getToken()
203 | if token.transactions == nil {
204 | // Display warning to user
205 | let alert = UIAlertController(title: "Error",
206 | message: "Unable to fetch transactions this time. Try again by tapping on the \"Refresh\" button.",
207 | preferredStyle: .alert)
208 | alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
209 | self.present(alert, animated: true, completion: nil)
210 | }
211 | }
212 | }
213 | }) { error in
214 | print("could not get transactions")
215 | }
216 | }
217 |
218 | func getTokenManagedObject() -> TokenManagedObject {
219 |
220 | let predicateAddress = NSPredicate(format: "address == %@", holding.address!)
221 | let predicateCode = NSPredicate(format: "code == %@", "ETH")
222 | let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateAddress, predicateCode])
223 |
224 | let tokenManagedObject = TokenManagedObject.mr_findFirst(with: compoundPredicate, in: NSManagedObjectContext.mr_default())!
225 |
226 | return tokenManagedObject
227 | }
228 |
229 | func getToken() -> Token {
230 |
231 | let token = TokenDto().token(from: getTokenManagedObject())
232 |
233 | return token
234 | }
235 |
236 | @IBAction func createTransaction() {
237 | let createTransactionVC = storyboard?.instantiateViewController(withIdentifier: "transactionVC") as! CreateTransactionController
238 | createTransactionVC.holding = holding
239 | createTransactionVC.delegate = self
240 | navigationController?.pushViewController(createTransactionVC, animated: true)
241 | }
242 |
243 | @IBAction func refreshTransactions() {
244 |
245 | getTransactions()
246 | }
247 |
248 | @objc func editHolding() {
249 |
250 | let createHoldingVC = storyboard?.instantiateViewController(withIdentifier: "createHoldingVC") as! CreateHoldingController
251 | createHoldingVC.holding = holding
252 | createHoldingVC.euroTotalValue = euroTotalValue
253 | createHoldingVC.delegate = self
254 | navigationController?.pushViewController(createHoldingVC, animated: true)
255 | }
256 |
257 | }
258 |
259 | extension HoldingController : UITableViewDataSource {
260 |
261 | func numberOfSections(in tableView: UITableView) -> Int {
262 | return 1
263 | }
264 |
265 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
266 | if transactions == nil || transactions?.count == 0 {
267 | return 1
268 | }
269 | return transactions.count+1
270 | }
271 |
272 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
273 | if indexPath.row == 0 && holding.address != nil {
274 | return 54
275 | }
276 | return 44
277 | }
278 |
279 | func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
280 | if holding.address == nil {
281 | return true
282 | }
283 | return false
284 | }
285 |
286 | func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
287 | if editingStyle == .delete {
288 |
289 | let transaction = transactions[indexPath.row-1]
290 |
291 | let holdingManagedObject = HoldingManagedObject.mr_findFirst(byAttribute: "name", withValue: holding.name!, in: NSManagedObjectContext.mr_default())
292 | if transaction.type == "credit" {
293 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue - transaction.value! as NSNumber
294 | } else {
295 | holdingManagedObject!.value = holdingManagedObject!.value!.doubleValue + transaction.value! as NSNumber
296 | }
297 |
298 | let transactionManagedObject = TransactionManagedObject.mr_findFirst(byAttribute: "id", withValue: transaction.identifier!, in: NSManagedObjectContext.mr_default())
299 | transactionManagedObject?.mr_deleteEntity(in: NSManagedObjectContext.mr_default())
300 | NSManagedObjectContext.mr_default().mr_saveToPersistentStoreAndWait()
301 |
302 | if Blockstack.shared.isUserSignedIn() {
303 | BlockstackApiService().send { errorMessage in
304 | if errorMessage != nil {
305 | let alert = UIAlertController(title: "Error",
306 | message: errorMessage,
307 | preferredStyle: .alert)
308 | alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
309 | self.present(alert, animated: true, completion: nil)
310 | }
311 | }
312 | }
313 |
314 | transactions.remove(at: indexPath.row-1)
315 |
316 | holding = HoldingDto().holding(from: holdingManagedObject!)
317 |
318 | euroTotalValue = PortfolioUseCase(with: PortfolioRepositoryImpl()).getEuroTotalValue()
319 | updateHolding()
320 | updatePieChart()
321 |
322 | tableView.deleteRows(at: [indexPath], with: .fade)
323 | }
324 |
325 | }
326 |
327 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
328 | if indexPath.row == 0 {
329 | let cell = tableView.dequeueReusableCell(withIdentifier: "topCellId", for: indexPath) as! HoldingTopCell
330 | cell.addButton.layer.cornerRadius = 4
331 | if holding.address == nil {
332 | if cell.poweredByLabel != nil {
333 | cell.poweredByLabel.removeFromSuperview()
334 | }
335 | } else {
336 | cell.addButton.setTitle("Refresh Transactions", for: .normal)
337 | cell.addButton.removeTarget(self, action: #selector(createTransaction), for: .touchUpInside)
338 | cell.addButton.addTarget(self, action: #selector(refreshTransactions), for: .touchUpInside)
339 | }
340 | return cell
341 | } else {
342 | let cell = tableView.dequeueReusableCell(withIdentifier: "transactionCellId", for: indexPath) as! TransactionCell
343 | let transaction = transactions[indexPath.row-1]
344 | cell.nameLabel.text = transaction.name
345 | let formattedNumber = numberFormatter.string(for: NSNumber(value: transaction.value!))
346 | if(transaction.type == "credit") {
347 | cell.valueLabel.text = "+ \(currency!.symbol!)\(formattedNumber ?? "--")"
348 | cell.colorView.backgroundColor = Colors.hexStringToUIColor(hex: "00B382")
349 | cell.valueLabel.textColor = Colors.hexStringToUIColor(hex: "00B382")
350 | } else {
351 | cell.valueLabel.text = "- \(currency!.symbol!)\(formattedNumber ?? "--")"
352 | cell.colorView.backgroundColor = Colors.hexStringToUIColor(hex: "E60243")
353 | cell.valueLabel.textColor = Colors.hexStringToUIColor(hex: "E60243")
354 | }
355 | return cell
356 | }
357 | }
358 |
359 | }
360 |
361 | extension HoldingController : UITableViewDelegate {
362 |
363 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
364 |
365 | if indexPath.row > 0 && holding.address == nil {
366 | tableView.deselectRow(at: indexPath, animated: true)
367 |
368 | let transaction = transactions[indexPath.row-1]
369 |
370 | let transactionVC = storyboard?.instantiateViewController(withIdentifier: "transactionVC") as! CreateTransactionController
371 | transactionVC.transaction = transaction
372 | transactionVC.holding = holding
373 | transactionVC.delegate = self
374 | navigationController?.pushViewController(transactionVC, animated: true)
375 | }
376 | }
377 | }
378 |
379 | extension HoldingController : EditHoldingDelegate {
380 |
381 | func updateHolding(newHolding: Holding) {
382 |
383 | holding = newHolding
384 | updateHolding()
385 | updatePieChart()
386 | }
387 | }
388 |
389 | extension HoldingController : CreateTransactionDelegate {
390 |
391 | func newTransaction(newHolding: Holding) {
392 |
393 | holding = newHolding
394 | updateHolding()
395 | updatePieChart()
396 | }
397 | }
398 |
--------------------------------------------------------------------------------