Skip to content

Commit

Permalink
Open in trust (trustwallet#425)
Browse files Browse the repository at this point in the history
* Add open in trust action extension

* Add URLNavigatorCoordinator

* 1. OpenInTrustError conforms to LocalizedError
2. update scheme (browse -> browser)
3. lazy init viewModel
4. show browser tab

* code cleanup

* present alert in main thread

* Add localizable strings

* Add URLSchemes constant; use error.localizedDescription

* separate trust://

* update Podfile.lock

* Fix tests
  • Loading branch information
vikmeup authored Mar 14, 2018
1 parent cc322b6 commit 32837d2
Show file tree
Hide file tree
Showing 62 changed files with 928 additions and 21 deletions.
45 changes: 45 additions & 0 deletions OpenInTrust/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>OpenInTrust</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<string>1</string>
</dict>
</dict>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).OpenInTrustController</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.ui-services</string>
</dict>
<key>LSHasLocalizedDisplayName</key>
<true/>
</dict>
</plist>
6 changes: 6 additions & 0 deletions OpenInTrust/Media.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
157 changes: 157 additions & 0 deletions OpenInTrust/Media.xcassets/ExtensionIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions OpenInTrust/OpenInTrust.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
11 changes: 11 additions & 0 deletions OpenInTrust/Types/DispatchMainSafe.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright SIX DAY LLC. All rights reserved.

import Foundation

public func dispatch_main_safe(block: @escaping () -> Void) {
if Thread.isMainThread {
block()
} else {
DispatchQueue.main.async(execute: block)
}
}
21 changes: 21 additions & 0 deletions OpenInTrust/Types/OpenInTrustError+Message.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright SIX DAY LLC. All rights reserved.

import Foundation

extension OpenInTrustError {

public var errorDescription: String? {
switch self {
case .invalidContext:
return NSLocalizedString("openintrust.error.context", value: "Extension context is invalid", comment: "")
case .invalidProvider:
return NSLocalizedString("openintrust.error.provider", value: "Extension item provider is invalid", comment: "")
case .invalidURL:
return NSLocalizedString("openintrust.error.url", value: "Can't find valid url", comment: "")
case .loadItemFailed:
return NSLocalizedString("openintrust.error.loadItem", value: "Can't find valid url", comment: "")
case .cancel:
return NSLocalizedString("openintrust.error.cancel", value: "Canceled by user", comment: "")
}
}
}
11 changes: 11 additions & 0 deletions OpenInTrust/Types/OpenInTrustError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright SIX DAY LLC. All rights reserved.

import UIKit

enum OpenInTrustError: LocalizedError {
case invalidContext
case invalidProvider
case invalidURL
case loadItemFailed
case cancel
}
13 changes: 13 additions & 0 deletions OpenInTrust/Types/OpenInTrustScheme.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright SIX DAY LLC. All rights reserved.

import Foundation

struct SchemeBuilder {
let target: URL
let scheme = "trust"

func build() -> URL {
let url = URL(string: "\(scheme)://browser?target=\(target.absoluteString)") ?? URL(string: "\(scheme)://")!
return url
}
}
97 changes: 97 additions & 0 deletions OpenInTrust/ViewControllers/OpenInTrustController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright SIX DAY LLC. All rights reserved.

import UIKit
import MobileCoreServices

class OpenInTrustController: UIViewController {

lazy var viewModel: OpenInTrustViewModel = {
let viewModel = OpenInTrustViewModel(context: self.extensionContext ?? NSExtensionContext())
return viewModel
}()
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}

override var prefersStatusBarHidden: Bool {
return false
}

override func loadView() {
view = UIView()
view.backgroundColor = .white
view.alpha = 0.3
}

override func viewDidLoad() {
super.viewDidLoad()
run()
}

func run() {
guard extensionContext != .none else {
showError(.invalidContext)
return
}
guard let provider = viewModel.findItemProvider() else {
return showError(.invalidProvider)
}

viewModel.loadItem(provider: provider) { [unowned self] (result) in
switch result {
case .success(let url):
self.openInTrust(url: url)
case .failure(let error):
self.showError(error)
}
}
}

func done() {
self.viewModel.context.completeRequest(returningItems: nil, completionHandler: nil)
}

func cancel(_ error: OpenInTrustError) {
self.viewModel.context.cancelRequest(withError: error)
}

private func openInTrust(url: URL) {
let alert = UIAlertController(title: viewModel.confirmAlertTitle, message: String(format: viewModel.confirmAlertMessage, url.absoluteString), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: self.viewModel.alertOK, style: .default, handler: { [unowned self] _ in
let builder = SchemeBuilder(target: url)
self.openUrl(builder.build())
self.done()
}))
alert.addAction(UIAlertAction(title: self.viewModel.alertCancel, style: .cancel, handler: { [unowned self] _ in
self.done()
}))
alert.popoverPresentationController?.sourceView = view
alert.popoverPresentationController?.sourceRect = view.frame
dispatch_main_safe {
self.present(alert, animated: true, completion: nil)
}
}

private func openUrl(_ url: URL) {
var responder = self as UIResponder?
while let r = responder {
let sel = NSSelectorFromString("openURL:")
if r.responds(to: sel) {
r.perform(sel, with: url)
}
responder = r.next
}
}

private func showError(_ error: OpenInTrustError) {
let alert = UIAlertController(title: viewModel.errorAlertTitle, message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: viewModel.alertOK, style: .default, handler: { [unowned self] _ in
self.cancel(error)
}))
alert.popoverPresentationController?.sourceView = view
alert.popoverPresentationController?.sourceRect = view.frame
dispatch_main_safe {
self.present(alert, animated: true, completion: nil)
}
}
}
Loading

0 comments on commit 32837d2

Please sign in to comment.