Skip to content
This repository was archived by the owner on Jan 17, 2019. It is now read-only.

Commit d7eab34

Browse files
authored
Merge pull request #98 from JohnSundell/actions-api-powerup
Power up actions API to support content operations & async, chainable actions
2 parents 2e15fe3 + 9fb805a commit d7eab34

32 files changed

+655
-72
lines changed

HubFramework.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@
197197
8A4C28151DB5120400152429 /* HUBComponentGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HUBComponentGestureRecognizer.h; sourceTree = "<group>"; };
198198
8A4C28161DB5120400152429 /* HUBComponentGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUBComponentGestureRecognizer.m; sourceTree = "<group>"; };
199199
8A4C28191DB6464B00152429 /* HUBComponentWithSelectionState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HUBComponentWithSelectionState.h; sourceTree = "<group>"; };
200+
8A4EB8B01DBA47E90004588C /* HUBAsyncAction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HUBAsyncAction.h; sourceTree = "<group>"; };
200201
8A58E0E21C5A77C700F41A5C /* HUBJSONPathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUBJSONPathTests.m; sourceTree = "<group>"; };
201202
8A58E1461C5B79A900F41A5C /* HUBMutableJSONPathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUBMutableJSONPathTests.m; sourceTree = "<group>"; };
202203
8A58E1481C5FA62E00F41A5C /* HUBComponentImageDataBuilderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUBComponentImageDataBuilderTests.m; sourceTree = "<group>"; };
@@ -340,6 +341,8 @@
340341
8AD151801D9966080008E182 /* HUBURLSessionMock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUBURLSessionMock.m; sourceTree = "<group>"; };
341342
8AD151821D9968390008E182 /* HUBURLSessionDataTaskMock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HUBURLSessionDataTaskMock.h; sourceTree = "<group>"; };
342343
8AD151831D9968390008E182 /* HUBURLSessionDataTaskMock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUBURLSessionDataTaskMock.m; sourceTree = "<group>"; };
344+
8AD585B51DB4D38700DB7606 /* HUBActionPerformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HUBActionPerformer.h; sourceTree = "<group>"; };
345+
8AD585B91DB4F49600DB7606 /* HUBContentOperationActionPerformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HUBContentOperationActionPerformer.h; sourceTree = "<group>"; };
343346
8AD732011D9AD30100E4B427 /* HUBDefaultConnectivityStateResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HUBDefaultConnectivityStateResolver.h; sourceTree = "<group>"; };
344347
8AD732021D9AD30100E4B427 /* HUBDefaultConnectivityStateResolver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUBDefaultConnectivityStateResolver.m; sourceTree = "<group>"; };
345348
8AD7335F1D9BE6F800E4B427 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
@@ -524,8 +527,10 @@
524527
isa = PBXGroup;
525528
children = (
526529
8A2A72EE1D4B749600141619 /* HUBAction.h */,
530+
8A4EB8B01DBA47E90004588C /* HUBAsyncAction.h */,
527531
8A0C356C1D7DB656007C32D9 /* HUBActionFactory.h */,
528532
8A2A72ED1D4B744A00141619 /* HUBActionRegistry.h */,
533+
8AD585B51DB4D38700DB7606 /* HUBActionPerformer.h */,
529534
8A6529F51D82D1C7007B1A15 /* HUBActionHandler.h */,
530535
F663FE7C1D10BECE003E19B6 /* HUBActionContext.h */,
531536
8A6529F61D82D7BE007B1A15 /* HUBActionTrigger.h */,
@@ -740,6 +745,7 @@
740745
8A40B12D1CAAA71500EBDDE2 /* HUBContentOperation.h */,
741746
8AF82D831D12EEFA00D1B933 /* HUBContentOperationWithInitialContent.h */,
742747
8A6525371D815F4C007B1A15 /* HUBContentOperationActionObserver.h */,
748+
8AD585B91DB4F49600DB7606 /* HUBContentOperationActionPerformer.h */,
743749
8A5D7A471CB7D2DB00B987BA /* HUBContentReloadPolicy.h */,
744750
52977AC61DA7D0890064629E /* HUBBlockContentOperationFactory.h */,
745751
);

demo/HubFrameworkDemo.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
8A27299A1D9546BB00EF43FC /* RootContentOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A2729991D9546BB00EF43FC /* RootContentOperationFactory.swift */; };
1111
8A27299E1D9546ED00EF43FC /* RootContentOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A27299D1D9546ED00EF43FC /* RootContentOperation.swift */; };
1212
8A2729A21D95486A00EF43FC /* URL+ViewURIs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A2729A11D95486A00EF43FC /* URL+ViewURIs.swift */; };
13+
8A374CFF1DBE659E0013E592 /* TodoListActionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A374CFC1DBE659E0013E592 /* TodoListActionFactory.swift */; };
14+
8A374D001DBE659E0013E592 /* TodoListAddAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A374CFD1DBE659E0013E592 /* TodoListAddAction.swift */; };
15+
8A374D011DBE659E0013E592 /* TodoListContentOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A374CFE1DBE659E0013E592 /* TodoListContentOperation.swift */; };
1316
8A42E0AB1D8C376B004FAC33 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A42E0A91D8C376B004FAC33 /* AppDelegate.swift */; };
1417
8A42E0B21D8C3779004FAC33 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8A42E0AE1D8C3779004FAC33 /* Assets.xcassets */; };
1518
8A42E0B31D8C3779004FAC33 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A42E0AF1D8C3779004FAC33 /* LaunchScreen.storyboard */; };
@@ -65,6 +68,9 @@
6568
8A2729991D9546BB00EF43FC /* RootContentOperationFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootContentOperationFactory.swift; sourceTree = "<group>"; };
6669
8A27299D1D9546ED00EF43FC /* RootContentOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootContentOperation.swift; sourceTree = "<group>"; };
6770
8A2729A11D95486A00EF43FC /* URL+ViewURIs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+ViewURIs.swift"; sourceTree = "<group>"; };
71+
8A374CFC1DBE659E0013E592 /* TodoListActionFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoListActionFactory.swift; sourceTree = "<group>"; };
72+
8A374CFD1DBE659E0013E592 /* TodoListAddAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoListAddAction.swift; sourceTree = "<group>"; };
73+
8A374CFE1DBE659E0013E592 /* TodoListContentOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoListContentOperation.swift; sourceTree = "<group>"; };
6874
8A42E0921D8C36A3004FAC33 /* HubFrameworkDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HubFrameworkDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
6975
8A42E0A91D8C376B004FAC33 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7076
8A42E0AE1D8C3779004FAC33 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -107,6 +113,16 @@
107113
/* End PBXFrameworksBuildPhase section */
108114

109115
/* Begin PBXGroup section */
116+
8A374CFA1DBE65920013E592 /* Todo list */ = {
117+
isa = PBXGroup;
118+
children = (
119+
8A374CFC1DBE659E0013E592 /* TodoListActionFactory.swift */,
120+
8A374CFD1DBE659E0013E592 /* TodoListAddAction.swift */,
121+
8A374CFE1DBE659E0013E592 /* TodoListContentOperation.swift */,
122+
);
123+
name = "Todo list";
124+
sourceTree = "<group>";
125+
};
110126
8A42E0891D8C36A3004FAC33 = {
111127
isa = PBXGroup;
112128
children = (
@@ -198,6 +214,7 @@
198214
8AD5EDB11D9A7CE4004B2CEA /* GitHub Search */,
199215
8AD72F4F1D9ABCDD00E4B427 /* Pretty pictures */,
200216
8ABCCEA71D9D1AAB005113B5 /* Really long list */,
217+
8A374CFA1DBE65920013E592 /* Todo list */,
201218
);
202219
name = Features;
203220
sourceTree = "<group>";
@@ -352,6 +369,7 @@
352369
8AD72F571D9AC22600E4B427 /* ImageComponent.swift in Sources */,
353370
8A2729A21D95486A00EF43FC /* URL+ViewURIs.swift in Sources */,
354371
8A42E1F91D8C476C004FAC33 /* ComponentFallbackHandler.swift in Sources */,
372+
8A374D001DBE659E0013E592 /* TodoListAddAction.swift in Sources */,
355373
8ABCCEAC1D9D1AE4005113B5 /* ReallyLongListContentOperationFactory.swift in Sources */,
356374
8AD5EDCE1D9AA294004B2CEA /* LabelComponent.swift in Sources */,
357375
8AD5EDCA1D9A9DBC004B2CEA /* GitHubSearchActivityIndicatorContentOperation.swift in Sources */,
@@ -360,12 +378,14 @@
360378
8A42E0AB1D8C376B004FAC33 /* AppDelegate.swift in Sources */,
361379
8AD5EDC41D9A92EC004B2CEA /* GitHubSearchCustomDataKeys.swift in Sources */,
362380
8A27299E1D9546ED00EF43FC /* RootContentOperation.swift in Sources */,
381+
8A374CFF1DBE659E0013E592 /* TodoListActionFactory.swift in Sources */,
363382
8ABCCEAA1D9D1AC3005113B5 /* ReallyLongListContentOperation.swift in Sources */,
364383
8AD5EDB91D9A7DEC004B2CEA /* DefaultComponentFactory.swift in Sources */,
365384
8AD5EDC61D9A9674004B2CEA /* HUBJSONSchemaRegistry+CustomSchemas.swift in Sources */,
366385
8AD5EDC81D9A9C3C004B2CEA /* ActivityIndicatorComponent.swift in Sources */,
367386
8AD72F551D9AC18100E4B427 /* PrettyPicturesContentOperationFactory.swift in Sources */,
368387
8AD5EDC21D9A90F7004B2CEA /* GitHubSearchResultsContentOperation.swift in Sources */,
388+
8A374D011DBE659E0013E592 /* TodoListContentOperation.swift in Sources */,
369389
8AD14FED1D9958A90008E182 /* UIImageView+Animation.swift in Sources */,
370390
8A42E1F71D8C468C004FAC33 /* ComponentLayoutManager.swift in Sources */,
371391
8AD72F531D9ABDB100E4B427 /* PrettyPicturesContentOperation.swift in Sources */,

demo/sources/AppDelegate.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import HubFramework
4343
registerGitHubSearchFeature()
4444
registerPrettyPicturesFeature()
4545
registerReallyLongListFeature()
46+
registerTodoListFeature()
4647

4748
return true
4849
}
@@ -122,6 +123,25 @@ import HubFramework
122123
)
123124
}
124125

126+
private func registerTodoListFeature() {
127+
let contentOperationFactory = HUBBlockContentOperationFactory() { _ in
128+
return [TodoListContentOperation()]
129+
}
130+
131+
hubManager.featureRegistry.registerFeature(
132+
withIdentifier: "todoList",
133+
viewURIPredicate: HUBViewURIPredicate(viewURI: .todoListViewURI),
134+
title: "Todo List",
135+
contentOperationFactories: [contentOperationFactory],
136+
contentReloadPolicy: nil,
137+
customJSONSchemaIdentifier: nil,
138+
actionHandler: nil,
139+
viewControllerScrollHandler: nil
140+
)
141+
142+
hubManager.actionRegistry.register(TodoListActionFactory(), forNamespace: TodoListActionFactory.namespace)
143+
}
144+
125145
// MARK: - Opening view URIs
126146

127147
@discardableResult private func open(viewURI: URL, animated: Bool) -> Bool {

demo/sources/RootContentOperation.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ class RootContentOperation: NSObject, HUBContentOperation {
4848
reallyLongListRowBuilder.subtitle = "A feature that renders 10,000 rows"
4949
reallyLongListRowBuilder.targetBuilder.uri = .reallyLongListViewURI
5050

51+
let todoListRowBuilder = viewModelBuilder.builderForBodyComponentModel(withIdentifier: "todoList")
52+
todoListRowBuilder.title = "Todo list"
53+
todoListRowBuilder.subtitle = "A feature for adding todo items to a list"
54+
todoListRowBuilder.targetBuilder.uri = .todoListViewURI
55+
5156
delegate?.contentOperationDidFinish(self)
5257
}
5358
}

demo/sources/SearchBarComponent.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class SearchBarComponent: NSObject, HUBComponentActionPerformer, UISearchBarDele
4242
static let debounceInterval = 0.3
4343

4444
var view: UIView?
45-
var actionDelegate: HUBComponentActionDelegate?
45+
weak var actionPerformer: HUBActionPerformer?
4646

4747
var debounceTimer: Timer?
4848

@@ -95,7 +95,7 @@ class SearchBarComponent: NSObject, HUBComponentActionPerformer, UISearchBarDele
9595
debounceTimer?.invalidate()
9696
self.debounceTimer = Timer.scheduledTimer(withTimeInterval: SearchBarComponent.debounceInterval, repeats: false) { (_) in
9797
let customData = [SearchBarComponentCustomDataKeys.text: searchText]
98-
self.actionDelegate?.component(self, performActionWith: actionIdentifier, customData: customData)
98+
self.actionPerformer?.performAction(withIdentifier: actionIdentifier, customData: customData)
9999
}
100100
}
101101

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2016 Spotify AB.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import Foundation
23+
import HubFramework
24+
25+
/// Action names used by `TodoListActionFactory`
26+
struct TodoListActionNames {
27+
/// The name of an action to display an alert to add a todo item
28+
static var add: String { return "add" }
29+
/// The name of an action that gets performed once a todo item has been added
30+
static var addCompleted: String { return "add-completed" }
31+
}
32+
33+
/// Action factory used by the "Todo list" feature
34+
class TodoListActionFactory: NSObject, HUBActionFactory {
35+
/// The namespace that this action factory is registered for with `HUBActionRegistry`
36+
static var namespace: String { return "namespace" }
37+
38+
func createAction(forName name: String) -> HUBAction? {
39+
if (name == TodoListActionNames.add) {
40+
return TodoListAddAction()
41+
}
42+
43+
return nil
44+
}
45+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) 2016 Spotify AB.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import Foundation
23+
import HubFramework
24+
25+
/// Action custom data keys used by `TodoListAddAction`
26+
struct TodoListAddActionCustomDataKeys {
27+
/// The title of the item to add to a todo list
28+
static var itemTitle: String { return "item" }
29+
}
30+
31+
/// Action that presents an alert to add a todo list item
32+
class TodoListAddAction: NSObject, HUBAsyncAction {
33+
weak var delegate: HUBAsyncActionDelegate?
34+
35+
func perform(with context: HUBActionContext) -> Bool {
36+
let alertController = UIAlertController(title: "Add an item", message: nil, preferredStyle: .alert)
37+
alertController.addTextField(configurationHandler: nil)
38+
39+
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
40+
alertController.dismiss(animated: true, completion: nil)
41+
self.delegate?.actionDidFinish(self, chainToActionWithIdentifier: nil, customData: nil)
42+
}
43+
44+
let doneAction = UIAlertAction(title: "Add", style: .default) { _ in
45+
let nextActionIdentifier = HUBIdentifier(namespace: TodoListActionFactory.namespace, name: TodoListActionNames.addCompleted)
46+
let nextActionCustomData = [TodoListAddActionCustomDataKeys.itemTitle: alertController.textFields!.first!.text]
47+
self.delegate?.actionDidFinish(self, chainToActionWithIdentifier: nextActionIdentifier, customData: nextActionCustomData)
48+
}
49+
50+
alertController.addAction(cancelAction)
51+
alertController.addAction(doneAction)
52+
53+
context.viewController.present(alertController, animated: true, completion: nil)
54+
55+
return true
56+
}
57+
}
58+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2016 Spotify AB.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import Foundation
23+
import HubFramework
24+
25+
/// Content operation used by the "Todo list" feature
26+
class TodoListContentOperation: NSObject, HUBContentOperationActionPerformer, HUBContentOperationActionObserver {
27+
weak var delegate: HUBContentOperationDelegate?
28+
weak var actionPerformer: HUBActionPerformer?
29+
30+
private var items = [String]()
31+
32+
func perform(forViewURI viewURI: URL, featureInfo: HUBFeatureInfo, connectivityState: HUBConnectivityState, viewModelBuilder: HUBViewModelBuilder, previousError: Error?) {
33+
viewModelBuilder.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(handleAddButton))
34+
35+
items.enumerated().forEach { index, item in
36+
let itemRowBuilder = viewModelBuilder.builderForBodyComponentModel(withIdentifier: "item-\(index)")
37+
itemRowBuilder.title = item
38+
}
39+
40+
delegate?.contentOperationDidFinish(self)
41+
}
42+
43+
func actionPerformed(with context: HUBActionContext, viewURI: URL, featureInfo: HUBFeatureInfo, connectivityState: HUBConnectivityState) {
44+
guard context.customActionIdentifier == HUBIdentifier(namespace: TodoListActionFactory.namespace, name: TodoListActionNames.addCompleted) else {
45+
return
46+
}
47+
48+
guard let itemTitle = context.customData?[TodoListAddActionCustomDataKeys.itemTitle] as? String else {
49+
return
50+
}
51+
52+
items.append(itemTitle)
53+
delegate?.contentOperationRequiresRescheduling(self)
54+
}
55+
56+
@objc private func handleAddButton() {
57+
let actionIdentifier = HUBIdentifier(namespace: TodoListActionFactory.namespace, name: TodoListActionNames.add)
58+
actionPerformer?.performAction(withIdentifier: actionIdentifier, customData: nil)
59+
}
60+
}

demo/sources/URL+ViewURIs.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ extension URL {
4141
static var reallyLongListViewURI: URL {
4242
return URL(viewURI: "reallylonglist")
4343
}
44+
45+
/// The view URI used for the "Todo list" feature
46+
static var todoListViewURI: URL {
47+
return URL(viewURI: "todoList")
48+
}
4449
}
4550

4651
private extension URL {

include/HubFramework/HUBAction.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
* under the License.
2020
*/
2121

22-
2322
#import <Foundation/Foundation.h>
2423

2524
@protocol HUBActionContext;

0 commit comments

Comments
 (0)