Skip to content

Instantly share code, notes, and snippets.

@felix-schwarz
Forked from pablocarmu/Actions.swift
Last active November 7, 2018 21:25
Show Gist options
  • Save felix-schwarz/0e85ec0ddd378da221a343469cf0a507 to your computer and use it in GitHub Desktop.
Save felix-schwarz/0e85ec0ddd378da221a343469cf0a507 to your computer and use it in GitHub Desktop.
Actions through extensions
import UIKit
import ownCloudSDK
enum ActionCategory {
case normal
case destructive
case informal
case edit
case save
}
enum ActionPosition : Int {
case none = -1
case first = 100
case beforeMiddle = 200
case middle = 300
case afterMiddle = 400
case last = 500
static func between(_ position1: ActionPosition, and position2: ActionPosition) -> ActionPosition {
return ActionPosition(rawValue: ((position1.rawValue + position2.rawValue)/2))!
}
func shift(by offset: Int) -> ActionPosition {
return ActionPosition(rawValue: self.rawValue + offset)!
}
}
typealias ActionCompletionHandler = ((Error?) -> Void)
typealias ActionProgressHandler = ((Progress) -> Void)
extension OCExtensionType {
static let action: OCExtensionType = OCExtensionType("app.action")
}
extension OCExtensionLocationIdentifier {
static let tableRow: OCExtensionLocationIdentifier = OCExtensionLocationIdentifier("tableRow") //!< Present as table row action
static let moreItem: OCExtensionLocationIdentifier = OCExtensionLocationIdentifier("moreItem") //!< Present in "more" card view for a single item
static let moreFolder: OCExtensionLocationIdentifier = OCExtensionLocationIdentifier("moreFolder") //!< Present in "more" options for a whole folder
static let toolbar: OCExtensionLocationIdentifier = OCExtensionLocationIdentifier("toolbar") //!< Present in a toolbar
}
class ActionExtension: OCExtension {
// MARK: - Custom Instance Properties.
var name: String
var category: ActionCategory
// MARK: - Init & Deinit
init(name: String, category: ActionCategory = .normal, identifier: OCExtensionIdentifier, locations: [OCExtensionLocationIdentifier]?, features: [String : Any]?, objectProvider: OCExtensionObjectProvider?, customMatcher: OCExtensionCustomContextMatcher?) {
self.name = name
self.category = category
super.init(identifier: identifier, type: .action, locations: locations, features: features, objectProvider: objectProvider, customMatcher: customMatcher)
}
}
class ActionContext: OCExtensionContext {
// MARK: - Custom Instance Properties.
weak var viewController: UIViewController?
weak var core: OCCore?
weak var query: OCQuery?
var items: [OCItem]
// MARK: - Init & Deinit.
init(viewController: UIViewController, core: OCCore, query: OCQuery? = nil, items: [OCItem], location: OCExtensionLocation, requirements: [String : Any]? = nil, preferences: [String : Any]? = nil) {
self.items = items
super.init()
self.viewController = viewController
self.core = core
self.location = location
self.query = query
self.requirements = requirements
self.preferences = preferences
}
}
class Action : NSObject {
// MARK: - Extension metadata
class var identifier : OCExtensionIdentifier? { return nil }
class var category : ActionCategory? { return .normal }
class var name : String? { return nil }
class var locations : [OCExtensionLocationIdentifier]? { return nil }
class var features : [String : Any]? { return nil }
// MARK: - Extension creation
class var actionExtension : ActionExtension {
let objectProvider : OCExtensionObjectProvider = { (_ rawExtension, _ context, _ error) -> Any? in
if let actionExtension = rawExtension as? ActionExtension,
let actionContext = context as? ActionContext {
return self.init(for: actionExtension, with: actionContext)
}
return nil
}
let customMatcher : OCExtensionCustomContextMatcher = { (context, priority) -> OCExtensionPriority in
if let actionContext = context as? ActionContext,
self.applicablePosition(forContext: actionContext) == .none {
// Exclude actions whose applicablePosition returns .none
return .noMatch
}
// Additional filtering (f.ex. via OCClassSettings, Settings) goes here
return priority
}
return ActionExtension(name: name!, category: category!, identifier: identifier!, locations: locations, features: features, objectProvider: objectProvider, customMatcher: customMatcher)
}
// MARK: - Extension matching
class func applicablePosition(forContext: ActionContext) -> ActionPosition {
return .middle
}
// MARK: - Finding actions
class func sortedApplicableActions(for context: ActionContext) -> [Action] {
var sortedActions : [Action] = []
if let matches = try? OCExtensionManager.shared.provideExtensions(for: context) {
for match in matches {
if let action = match.extension.provideObject(for: context) as? Action {
sortedActions.append(action)
}
}
}
sortedActions.sort { (action1, action2) -> Bool in
return action1.position.rawValue < action2.position.rawValue
}
return sortedActions
}
// MARK: - Action metadata
var context : ActionContext
var actionExtension: ActionExtension
var core : OCCore
// MARK: - Action creation
required init(for actionExtension: ActionExtension, with context: ActionContext) {
self.actionExtension = actionExtension
self.context = context
self.core = context.core!
super.init()
}
// MARK: - Execution metadata
var progressHandler : ActionProgressHandler? // to be filled before calling run(), provideStaticRow(), provideContextualAction(), etc. if desired
var completionHandler : ActionCompletionHandler? // to be filled before calling run(), provideStaticRow(), provideContextualAction(), etc. if desired
// MARK: - Action implementation
func run() {
if completionHandler != nil {
completionHandler!(nil)
}
}
// MARK: - Action UI elements
func provideStaticRow() -> StaticTableViewRow? {
return StaticTableViewRow(buttonWithAction: { (row, sender) in
self.run()
}, title: actionExtension.name, style: actionExtension.category == .destructive ? .destructive : .plain, identifier: actionExtension.identifier.rawValue)
}
func provideContextualAction() -> UIContextualAction? {
return UIContextualAction(style: actionExtension.category == .destructive ? .destructive : .normal, title: self.actionExtension.name, handler: { (action, view, uiCompletionHandler) in
uiCompletionHandler(true)
self.run()
})
}
// MARK: - Action metadata
func iconForLocation(_ location: OCExtensionLocationIdentifier) -> UIImage? {
return nil
}
var icon : UIImage? {
if let locationIdentifier = context.location?.identifier {
return self.iconForLocation(locationIdentifier)
}
return nil
}
var position : ActionPosition {
return type(of: self).applicablePosition(forContext: context)
}
}
class MoveAction : Action {
override class var identifier : OCExtensionIdentifier? { return OCExtensionIdentifier("com.owncloud.action.move") }
override class var category : ActionCategory? { return .normal }
override class var name : String? { return "Move".localized }
// MARK: - Extension matching
override class func applicablePosition(forContext: ActionContext) -> ActionPosition {
// Examine items in context
return .middle
}
// MARK: - Action implementation
override func run() {
guard context.items.count > 0, let viewController = context.viewController else {
completionHandler?(NSError(ocError: .errorInsufficientParameters))
return
}
let item = context.items[0]
let directoryPickerViewController = ClientDirectoryPickerViewController(core: core, path: "/", completion: { (selectedDirectory) in
if let progress = self.core.move(item, to: selectedDirectory, withName: item.name, options: nil, resultHandler: { (error, _, _, _) in
self.completionHandler?(error)
}) {
self.progressHandler?(progress)
}
})
let pickerNavigationController = ThemeNavigationController(rootViewController: directoryPickerViewController)
viewController.navigationController?.present(pickerNavigationController, animated: true)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment