2018-09-23 16:01:05 +00:00
//
// X C a l l b a c k U R L . s w i f t
// T u s k e r
//
// C r e a t e d b y S h a d o w f a c t s o n 9 / 2 3 / 1 8 .
// C o p y r i g h t © 2 0 1 8 S h a d o w f a c t s . A l l r i g h t s r e s e r v e d .
//
2018-09-24 01:10:45 +00:00
import UIKit
typealias XCBAction = ( _ url : XCallbackURL , _ session : XCBSession , _ silent : Bool ? ) -> Void
2018-09-23 16:01:05 +00:00
struct XCallbackURLSpec {
let path : String
2018-09-24 01:35:33 +00:00
let type : XCBActionType
2018-09-23 16:01:05 +00:00
let arguments : [ String : Bool ]
2018-09-24 01:10:45 +00:00
let canRunSilently : Bool
let action : XCBAction
2018-09-23 16:01:05 +00:00
2018-09-24 01:35:33 +00:00
init ( type : XCBActionType , arguments : [ String : Bool ] , canRunSilently : Bool , action : @ escaping XCBAction ) {
self . path = type . path
2018-09-24 01:10:45 +00:00
self . type = type
self . canRunSilently = canRunSilently
self . action = action
var arguments = arguments
if canRunSilently {
arguments [ " silent " ] = true
}
2018-09-23 16:01:05 +00:00
self . arguments = arguments
2018-09-24 01:10:45 +00:00
}
func handle ( url : XCallbackURL ) -> Bool {
let session = XCBManager . createSession ( type : type , url : url )
if canRunSilently && url . silent {
if let source = url . source {
let permission = Preferences . shared . silentActions [ source ] ? ? . undecided
switch permission {
case . accepted :
action ( url , session , true )
case . rejected :
action ( url , session , false )
case . undecided :
let alert = UIAlertController ( title : " \( source ) wants to perform actions silently " , message : " Accepting will allow \( source ) to perform actions without your confirmation, rejecting will always prompt for confirmation. " , preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : " Accept " , style : . default , handler : { ( _ ) in
Preferences . shared . silentActions [ source ] = . accepted
self . action ( url , session , true )
} ) )
alert . addAction ( UIAlertAction ( title : " Reject " , style : . default , handler : { ( _ ) in
Preferences . shared . silentActions [ source ] = . rejected
self . action ( url , session , false )
} ) )
UIApplication . shared . keyWindow ! . rootViewController ! . present ( alert , animated : true )
}
} else {
session . complete ( with : . error , additionalData : [
" error " : " Cannot perform silent action without source app, x-source parameter must be specified "
] )
}
} else {
action ( url , session , nil )
}
return true
2018-09-23 16:01:05 +00:00
}
}
struct XCallbackURL {
let path : String
let arguments : [ String : String ]
2018-09-24 01:10:45 +00:00
let silent : Bool
2018-09-23 23:04:39 +00:00
let source : String ?
2018-09-23 16:01:05 +00:00
let success : URL ?
let error : URL ?
let cancel : URL ?
init ( spec : XCallbackURLSpec , components : URLComponents ) {
self . path = spec . path
let queryItems = components . queryItems !
self . arguments = spec . arguments . reduce ( into : [ String : String ] ( ) , { ( result , el ) in
if let value = queryItems . first ( where : { $0 . name = = el . key } ) ? . value {
result [ el . key ] = value
}
} )
2018-09-24 01:10:45 +00:00
if spec . canRunSilently {
silent = Bool ( arguments [ " silent " ] ? ? " false " ) ? ? false
} else {
silent = false
}
2018-09-23 23:04:39 +00:00
source = queryItems . first ( where : { $0 . name = = " x-source " } ) . flatMap { $0 . value }
2018-09-23 16:01:05 +00:00
success = queryItems . first ( where : { $0 . name = = " x-success " } ) . flatMap { $0 . value } . flatMap { URL ( string : $0 ) }
error = queryItems . first ( where : { $0 . name = = " x-error " } ) . flatMap { $0 . value } . flatMap { URL ( string : $0 ) }
cancel = queryItems . first ( where : { $0 . name = = " x-cancel " } ) . flatMap { $0 . value } . flatMap { URL ( string : $0 ) }
}
}
extension XCallbackURLSpec {
func matches ( _ components : URLComponents ) -> Bool {
2018-09-24 01:10:45 +00:00
guard path = = components . path else { return false }
for ( name , optional ) in arguments {
2018-09-23 16:01:05 +00:00
if ( ! optional && components . queryItems ? . first ( where : { $0 . name = = name } ) = = nil ) {
return false
}
}
return true
}
}