Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Prev Previous commit
Next Next commit
use shortcut item parser instead of shortcut state manager
  • Loading branch information
hellohuanlin committed Nov 15, 2022
commit a0ff061af8c47b678f0e046a94187012ef92b267
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
E092A7ED28D10802005C7F67 /* MockMethodChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7EA28D10801005C7F67 /* MockMethodChannel.swift */; };
E092A7EE28D10802005C7F67 /* QuickActionsPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7EB28D10802005C7F67 /* QuickActionsPluginTests.swift */; };
E092A7EF28D10802005C7F67 /* MockShortcutStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7EC28D10802005C7F67 /* MockShortcutStateManager.swift */; };
E092A7F128D10890005C7F67 /* MockShortcutItemService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F028D10890005C7F67 /* MockShortcutItemService.swift */; };
E092A7F428D110B3005C7F67 /* DefaultShortcutStateManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F328D110B3005C7F67 /* DefaultShortcutStateManagerTests.swift */; };
E092A7F128D10890005C7F67 /* MockAppShortcutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F028D10890005C7F67 /* MockAppShortcutController.swift */; };
E092A7F428D110B3005C7F67 /* DefaultShortcutItemParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F328D110B3005C7F67 /* DefaultShortcutItemParserTests.swift */; };
E092A7F628D128EB005C7F67 /* RunnerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F528D128EB005C7F67 /* RunnerUITests.swift */; };
E0A075D529147FE200329BAE /* MockShortcutItemParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0A075D429147FE200329BAE /* MockShortcutItemParser.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -80,10 +80,10 @@
C35AD3650AB6BF850E016715 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
E092A7EA28D10801005C7F67 /* MockMethodChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockMethodChannel.swift; sourceTree = "<group>"; };
E092A7EB28D10802005C7F67 /* QuickActionsPluginTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickActionsPluginTests.swift; sourceTree = "<group>"; };
E092A7EC28D10802005C7F67 /* MockShortcutStateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockShortcutStateManager.swift; sourceTree = "<group>"; };
E092A7F028D10890005C7F67 /* MockShortcutItemService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockShortcutItemService.swift; sourceTree = "<group>"; };
E092A7F328D110B3005C7F67 /* DefaultShortcutStateManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultShortcutStateManagerTests.swift; sourceTree = "<group>"; };
E092A7F028D10890005C7F67 /* MockAppShortcutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAppShortcutController.swift; sourceTree = "<group>"; };
E092A7F328D110B3005C7F67 /* DefaultShortcutItemParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultShortcutItemParserTests.swift; sourceTree = "<group>"; };
E092A7F528D128EB005C7F67 /* RunnerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerUITests.swift; sourceTree = "<group>"; };
E0A075D429147FE200329BAE /* MockShortcutItemParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockShortcutItemParser.swift; sourceTree = "<group>"; };
F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -120,7 +120,7 @@
E092A7F228D10908005C7F67 /* Mocks */,
33E20B3626EFCDFC00A4A191 /* Info.plist */,
E092A7EB28D10802005C7F67 /* QuickActionsPluginTests.swift */,
E092A7F328D110B3005C7F67 /* DefaultShortcutStateManagerTests.swift */,
E092A7F328D110B3005C7F67 /* DefaultShortcutItemParserTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -216,8 +216,8 @@
isa = PBXGroup;
children = (
E092A7EA28D10801005C7F67 /* MockMethodChannel.swift */,
E092A7F028D10890005C7F67 /* MockShortcutItemService.swift */,
E092A7EC28D10802005C7F67 /* MockShortcutStateManager.swift */,
E092A7F028D10890005C7F67 /* MockAppShortcutController.swift */,
E0A075D429147FE200329BAE /* MockShortcutItemParser.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -436,9 +436,9 @@
files = (
E092A7EE28D10802005C7F67 /* QuickActionsPluginTests.swift in Sources */,
E092A7ED28D10802005C7F67 /* MockMethodChannel.swift in Sources */,
E092A7F128D10890005C7F67 /* MockShortcutItemService.swift in Sources */,
E092A7EF28D10802005C7F67 /* MockShortcutStateManager.swift in Sources */,
E092A7F428D110B3005C7F67 /* DefaultShortcutStateManagerTests.swift in Sources */,
E092A7F128D10890005C7F67 /* MockAppShortcutController.swift in Sources */,
E0A075D529147FE200329BAE /* MockShortcutItemParser.swift in Sources */,
E092A7F428D110B3005C7F67 /* DefaultShortcutItemParserTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import XCTest

@testable import quick_actions_ios

class ShortcutStateManagerTests: XCTestCase {
class DefaultShortcutItemParserTests: XCTestCase {

func testSetShortcutItems_shouldSetItem() {
func testparseShortcutItems() {
let rawItem = [
"type": "SearchTheThing",
"localizedTitle": "Search the thing",
Expand All @@ -23,16 +23,12 @@ class ShortcutStateManagerTests: XCTestCase {
icon: UIApplicationShortcutIcon(templateImageName: "search_the_thing.png"),
userInfo: nil)

let mockShortcutItemService = MockShortcutItemService()
let manager = DefaultShortcutStateManager(service: mockShortcutItemService)

manager.setShortcutItems([rawItem])

XCTAssertEqual(mockShortcutItemService.shortcutItems, [expectedItem])
let parser = DefaultShortcutItemParser()
XCTAssertEqual(parser.parseShortcutItems([rawItem]), [expectedItem])

}

func testSetShortcutItems_shouldSetItemWithoutIcon() {
func testparseShortcutItems_noIcon() {
let rawItem: [String: Any] = [
"type": "SearchTheThing",
"localizedTitle": "Search the thing",
Expand All @@ -46,11 +42,7 @@ class ShortcutStateManagerTests: XCTestCase {
icon: nil,
userInfo: nil)

let mockShortcutItemService = MockShortcutItemService()
let manager = DefaultShortcutStateManager(service: mockShortcutItemService)

manager.setShortcutItems([rawItem])

XCTAssertEqual(mockShortcutItemService.shortcutItems, [expectedItem])
let parser = DefaultShortcutItemParser()
XCTAssertEqual(parser.parseShortcutItems([rawItem]), [expectedItem])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

@testable import quick_actions_ios

final class MockShortcutItemService: ShortcutItemServicing {
final class MockAppShortcutController: AppShortcutControlling {
var shortcutItems: [UIApplicationShortcutItem]? = nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// MockShortcutItemParser.swift
// RunnerTests
//
// Created by Huan Lin on 11/3/22.
// Copyright © 2022 The Flutter Authors. All rights reserved.
//

import Foundation

@testable import quick_actions_ios

final class MockShortcutItemParser: ShortcutItemParser {

var parseShortcutItemsStub: ((_ items: [[String: Any]]) -> [UIApplicationShortcutItem])? = nil

func parseShortcutItems(_ items: [[String: Any]]) -> [UIApplicationShortcutItem] {
return parseShortcutItemsStub?(items) ?? []
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,57 +15,73 @@ class QuickActionsPluginTests: XCTestCase {
"localizedTitle": "Search the thing",
"icon": "search_the_thing.png",
]
let item = UIApplicationShortcutItem(
type: "SearchTheThing",
localizedTitle: "Search the thing",
localizedSubtitle: nil,
icon: UIApplicationShortcutIcon(templateImageName: "search_the_thing.png"),
userInfo: nil)

let call = FlutterMethodCall(methodName: "setShortcutItems", arguments: [rawItem])

let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let setShortcutItemsExpectation = expectation(description: "setShortcutItems must be called.")
mockShortcutStateManager.setShortcutItemsStub = { items in
let parseShortcutItemsExpectation = expectation(
description: "parseShortcutItems must be called.")
mockShortcutItemParser.parseShortcutItemsStub = { items in
XCTAssertEqual(items as? [[String: String]], [rawItem])
setShortcutItemsExpectation.fulfill()
parseShortcutItemsExpectation.fulfill()
return [item]
}

let resultExpectation = expectation(description: "result block must be called.")
plugin.handle(call) { result in
XCTAssertNil(result, "result block must be called with nil.")
resultExpectation.fulfill()
}

XCTAssertEqual(mockAppShortcutController.shortcutItems, [item], "Must set shortcut items.")
waitForExpectations(timeout: 1)
}

func testHandleMethodCall_clearShortcutItems() {
let call = FlutterMethodCall(methodName: "clearShortcutItems", arguments: nil)
let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let setShortcutItemsExpectation = expectation(description: "setShortcutItems must be called.")
mockShortcutStateManager.setShortcutItemsStub = { items in
XCTAssert(items.isEmpty)
setShortcutItemsExpectation.fulfill()
}
let plugin = QuickActionsPlugin(
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let resultExpectation = expectation(description: "result block must be called.")
plugin.handle(call) { result in
XCTAssertNil(result, "result block must be called with nil.")
resultExpectation.fulfill()
}

XCTAssertEqual(mockAppShortcutController.shortcutItems, [], "Must clear shortcut items.")
waitForExpectations(timeout: 1)
}

func testHandleMethodCall_getLaunchAction() {
let call = FlutterMethodCall(methodName: "getLaunchAction", arguments: nil)

let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let resultExpectation = expectation(description: "result block must be called.")
plugin.handle(call) { result in
Expand All @@ -80,9 +96,13 @@ class QuickActionsPluginTests: XCTestCase {
let call = FlutterMethodCall(methodName: "nonExist", arguments: nil)

let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let resultExpectation = expectation(description: "result block must be called.")

Expand All @@ -98,9 +118,13 @@ class QuickActionsPluginTests: XCTestCase {

func testApplicationPerformActionForShortcutItem() {
let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let item = UIApplicationShortcutItem(
type: "SearchTheThing",
Expand Down Expand Up @@ -128,9 +152,13 @@ class QuickActionsPluginTests: XCTestCase {

func testApplicationDidFinishLaunchingWithOptions_launchWithShortcut() {
let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let item = UIApplicationShortcutItem(
type: "SearchTheThing",
Expand All @@ -149,9 +177,13 @@ class QuickActionsPluginTests: XCTestCase {

func testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut() {
let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let launchResult = plugin.application(UIApplication.shared, didFinishLaunchingWithOptions: [:])
XCTAssert(
Expand All @@ -161,9 +193,13 @@ class QuickActionsPluginTests: XCTestCase {

func testApplicationDidBecomeActive_launchWithoutShortcut() {
let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

mockChannel.invokeMethodStub = { _, _ in
XCTFail("invokeMethod should not be called if launch without shortcut.")
Expand All @@ -186,9 +222,13 @@ class QuickActionsPluginTests: XCTestCase {
userInfo: nil)

let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let invokeMethodExpectation = expectation(description: "invokeMethod must be called.")
mockChannel.invokeMethodStub = { method, arguments in
Expand Down Expand Up @@ -217,9 +257,13 @@ class QuickActionsPluginTests: XCTestCase {
userInfo: nil)

let mockChannel = MockMethodChannel()
let mockShortcutStateManager = MockShortcutStateManager()
let mockAppShortcutController = MockAppShortcutController()
let mockShortcutItemParser = MockShortcutItemParser()

let plugin = QuickActionsPlugin(
channel: mockChannel, shortcutStateManager: mockShortcutStateManager)
channel: mockChannel,
appShortcutController: mockAppShortcutController,
shortcutItemParser: mockShortcutItemParser)

let invokeMethodExpectation = expectation(description: "invokeMethod must be called.")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

import UIKit

/// Services `UIApplicationShortcutItem`s.
protocol ShortcutItemServicing: AnyObject {
/// Controlls app's shortcut behavior.
protocol AppShortcutControlling: AnyObject {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need AnyObject?

Copy link
Contributor Author

@hellohuanlin hellohuanlin Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AnyObject scopes this protocol to reference types (classes). It prevents any value types (enums & structs) to conform to it.

Since this is injected dependency with no value semantics, we want to scope it to reference types. See the NOTE below:

class QuickActionsPlugin {
  // NOTE: If we remove `AnyObject`, we have to use `var` here. 
  private let controller: AppShortcutControlling 

  func foo() {
    controller.shortcutItems = [] // mutate here
  }
}


/// An array of shortcut items for home screen.
var shortcutItems: [UIApplicationShortcutItem]? { get set }
}

/// A default implementation of the `ShortcutItemServicing` protocol.
extension UIApplication: ShortcutItemServicing {}
/// A default implementation of the `AppShortcutControlling` protocol.
extension UIApplication: AppShortcutControlling {}
Loading