写点什么

算法:匹配有效的括号,Swift 5 中 UITest 从入门到精通, Swift 5 Viper Template,极客大学产品经理训练营 产品思维和产品意识, John 易筋 ARTS 打卡 Week 36

用户头像
John(易筋)
关注
发布于: 2021 年 01 月 24 日

1. Algorithm: 每周至少做一个 LeetCode 的算法题

笔者的文章:

算法:匹配有效的括号20. Valid Parentheses

LeetCode 全集请参考:LeetCode Github 大全


题目

20. Valid Parentheses

Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.


An input string is valid if:


Open brackets must be closed by the same type of brackets.

Open brackets must be closed in the correct order.

Example 1:


Input: s = "()"Output: true
复制代码


Example 2:


Input: s = "()[]{}"Output: true
复制代码


Example 3:


Input: s = "(]"Output: false
复制代码


Example 4:


Input: s = "([)]"Output: false
复制代码


Example 5:


Input: s = "{[]}"Output: true
复制代码


Constraints:


1 <= s.length <= 104s consists of parentheses only '()[]{}'.
复制代码


用栈 Stack 的解法

匹配左右括号,用栈解决,

  1. 左括号的时候入栈;

  2. 右括号的时候出栈,判断出栈的左括号 是否是同类型的,如果栈是空或者不匹配则返回 false;

  3. 结束以后,Stack 栈若是空则匹配 true。

class Solution {    public boolean isValid(String s) {        // check edge        if (s == null || s.length() == 0) {            return true;        }                Stack<Character> stack = new Stack<>();        Map<Character, Character> map = new HashMap<>();        map.put(')', '(');        map.put('}', '{');        map.put(']', '[');                char[] chars = s.toCharArray();        for(char c: chars) {            if (c == '(' || c == '{' || c == '[') {                stack.push(c);                continue;            }                        if (stack.isEmpty() || stack.pop() != map.get(c)) return false;        }                return stack.isEmpty();    }}
复制代码


2. Review: 阅读并点评至少一篇英文技术文章

笔者的文章:

翻译:您在Swift 5中的第一个UITest

1. 什么是自动 UITest?

当谈到测试时,我们通常会考虑单元测试。但是,在应用程序世界中还有另一种功能极其强大且有用的测试:UITests。

这些测试的目的是验证您的 UI 行为是否符合预期:按钮处于正确的状态,字符串的样式正确,导航在我们进行编码时发生。

测试的主体模拟了与 UI 交互的 use r,从而导致应用程序随着时间的推移而发展,并允许我们断言某些条件,我们希望在应用程序执行过程中满足要求。


2. 为什么要执行自动 UITest?

作为所有测试活动,编写适当的 UITests 需要时间。但是,这段时间将来将为我们节省很多麻烦,从长远来看,还将节省比其所消耗的时间更多的时间。


编写自动化的 UITests 有很多好处,特别是在应用变得非常大且可能难以维护的情况下。其中一些好处:


  • 无需测试旧功能。在敏捷的环境中,我们每两周创建一次新功能。但是,开发人员有些偏执:他们会要求 QA 朋友每次都测试整个应用程序,以避免性能下降。可以使用自动化的 UITest 自动浏览最常见的用例,从而保持开发人员和质量检查人员的头脑清醒。

  • 编码用例知识。UITests 允许我们查看事件序列发生时应用程序的行为。这对于跟踪常见用例并验证每次我们修改应用程序时它们不会崩溃都非常有用。

  • 测试 UI 性能。假设您的应用必须执行一些繁重的 UI 操作,例如滚动浏览一大堆图像,我们可以使用 UITests 来衡量执行这些操作所需的时间以及是否存在回归。

  • 检查本地化。假设您的应用程序已本地化,我们可以自动检查是否用省略号剪切了某些字符串。


这些只是自动 UITest 的一些优点。可能会有更多,但我认为这些是最突出的。当然也有缺点:

  • 他们比单元测试花费更多的时间。

  • 鉴于 UI 可以经常更改,因此它们往往更加不稳定。您的 UITests 有可能在 6 个月内过时。


尽管存在这些缺点,但我认为这些测试确实有帮助,特别是对于某些部分不太可能更改的大型应用程序。如果您必须由一个只能同时关注一个应用程序的小型团队管理多个应用程序,它们也很有用。


3. 如何编写 UITests?

搭建环境

UITests 已集成到 Xcode 中,我们的 IDE 为我们完成了大部分工作。第一步是为我们的应用添加一个新的 UITest 目标。

  1. 点击 File > New > Target

  2. 选择 UI Testing Bundle

  3. 单击 Next 并在表单中填写名称

  4. 点击 Finish。


Xcode 将使用第一个空 UITest 文件创建新目标。该文件具有以下结构(请注意 Xcode 提供了多少注释,以帮助我们进行首次尝试)。


import XCTest
class UITestAppUITests: XCTestCase {
override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. }
override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. }
func testExample() throws { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch()
// Use recording to get started writing UI tests. // Use XCTAssert and related functions to verify your tests produce the correct results. }
func testLaunchPerformance() throws { if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { // This measures how long it takes to launch your application. measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { XCUIApplication().launch() } } }}
复制代码

让我们专注于该 testExample()方法。一切都由一个 XCUIApplication()对象管理。该对象是我们应用程序的抽象。我们可以 launch()并且 terminate()它可以让我们查询 UI 中的所有元素。

4. 记录我们的第一个测试

是的,你没看错!我们不会立即编写测试:Xcode 提供了一个非常不错且有用的工具,允许我们与应用程序进行交互,而 Xcode 会将我们的交互转换为代码。多么酷啊?!

要开始此操作,我们只需要将光标置于第testExample()24 行下方的empty方法中,然后按一下record(红色按钮是 record 按钮。), Xcode 底部的小按钮即可。

当 record 按下按钮时,Xcode 将启动我们的应用程序并捕获所有交互,为我们编写代码。

对于本文,我准备了一个非常简单的应用程序(您可以在此处找到代码),该应用程序view具有 3 个按钮:一个privacy按钮,一个tos(服务条款)按钮和一个继续按钮。Continue除非用户同时点击privacytos按钮,否则该按钮将被禁用。一旦两个按钮都被选中,该Continue按钮将变为启用状态,并且用户HomeView在点击该Continue按钮时会导航到蓝色。

屏幕 A。未选择按钮,继续禁用;屏幕 B。选择了服务条款,继续禁用;屏幕 C,两个按钮均被选中,继续启用;屏幕 D,蓝色 HomeView。


让我们做一个快速实验并开始记录测试。在此用例中,我们要:

  1. 轻按 Tap to accept TOS

  2. 轻按 Tap to Accept Privacy Policy

  3. 确认已Continue启用

  4. 点按Continue,即可看到该应用导航至蓝色view

  5. 停止录音。

此过程的输出是以下代码:


func testExample() throws {  // UI tests must launch the application that they test.  let app = XCUIApplication()  app.launch()
// Use recording to get started writing UI tests. // Use XCTAssert and related functions to verify your tests produce the correct results. // XCODE STARTS WRITING HERE:👇 let app = XCUIApplication() app.staticTexts["Tap to accept TOS"].tap() app.staticTexts["Tap to Accept Privacy Policy"].tap() app.staticTexts["Continue"].tap()
}
复制代码

注意,Xcode 再次写入 letapp = XCUIApplication()行。我们必须将其删除。


现在通过按来运行测试 cmd+U,会导致 Xcode 为我们运行该应用程序并再次执行我们刚执行的操作。测试结果是成功的,主要是因为我们没有编写任何断言。

5. 写断言

每当我们必须测试一段代码时,我们就必须编写断言。断言通常是布尔检查,可让我们声明一些条件,这些条件必须有效才能使测试成功。


通过利用 XCTest 框架,在 UITests 中编写断言与在标准单元测试中编写断言完全相同。


让我们将断言添加到我们的代码中。


let tosButton = app.staticTexts["Tap to accept TOS"]let privacyButton = app.staticTexts["Tap to Accept Privacy Policy"]let continueButton = app.staticTexts["Continue"]
XCTAssertFalse(tosButton.isSelected)XCTAssertFalse(privacyButton.isSelected)XCTAssertFalse(continueButton.isEnabled)
tosButton.tap()
XCTAssertTrue(tosButton.isSelected)XCTAssertFalse(privacyButton.isSelected)XCTAssertFalse(continueButton.isEnabled)
privacyButton.tap()
XCTAssertTrue(tosButton.isSelected)XCTAssertTrue(privacyButton.isSelected)XCTAssertTrue(continueButton.isEnabled)
continueButton.tap()
复制代码

首先,我们将三个按钮分解为单独的变量。这样,我们可以查询它们的状态。然后我们写断言。它们非常简单:只需检查按钮的状态即可,因为我们知道它在执行期间会发生变化。


运行测试,看看..他们失败了!😨


那怎么可能 好吧,让我们分析错误和代码。


第一个问题是continueButton.isEnabled自测试开始以来该属性返回 true这是因为我们将其作为staticText而不是作为进行访问button。静态文本没有启用或禁用的概念,因此该isEnabled属性始终返回true。首先执行更改:将所有staticTexts更改为buttons


让我们再次运行测试,……另一个失败!第二组断言不再找到tosButton!不用担心,这里有一个合理的解释。当我们点击按钮时,按钮的标题会变化,tosButton怎样?我们用来访问button字典的内容是什么?在tosButton.title当按钮被窃听,我们正在使用的原标题查询按钮词典性质的变化。


幸运的是,我们可以使用来克服此问题accessibilityIdentifier。无障碍技术使iPhone可以向用户描述屏幕上的内容。UITests利用相同的技术来索引视图中的元素。因此,我们只需要向类中的setup()方法添加第 7 至 10 行即可LegalView


private func setup() {  // ... other view elements ...  self.addSubview(tosButton)  self.addSubview(privacyButton)  self.addSubview(continueButton)     // Add these lines to let the XCUITest access the buttons with identifiers  self.tosButton.accessibilityIdentifier = "legal_view.tos_button"  self.privacyButton.accessibilityIdentifier = "legal_view.privacy_button"  self.continueButton.accessibilityIdentifier = "legal_view.continue_button"    // ...}
复制代码


现在,我们已经 id 为每个按钮分配了一个,我们可以使用它来检索适当的 UI 元素。


您可能要问的最后一个问题是,我们如何确定应用正确导航到正确的视图?好的,通过结合accessibilityIdentifiersXCUIApplication.otherElements属性,我们可以编写一个简单的断言来验证一切是否如我们所愿。实际上,我们可以:

  1. "home_view”可访问性标识符添加到蓝色视图。

  2. 导航后,检查是否存在带有该标识符的元素。

现在,我们可以按cmd+U运行代码,并查看每个断言都通过了。具有正确断言的最终代码如下所示:


func testExample() throws {    // UI tests must launch the application that they test.    let app = XCUIApplication()    app.launch()
// Use recording to get started writing UI tests. // Use XCTAssert and related functions to verify your tests produce the correct results. let tosButton = app.buttons["legal_view.tos_button"] let privacyButton = app.buttons["legal_view.privacy_button"] let continueButton = app.buttons["legal_view.continue_button"]
XCTAssertFalse(tosButton.isSelected) XCTAssertFalse(privacyButton.isSelected) XCTAssertFalse(continueButton.isEnabled)
tosButton.tap()
XCTAssertTrue(tosButton.isSelected) XCTAssertFalse(privacyButton.isSelected) XCTAssertFalse(continueButton.isEnabled)
privacyButton.tap()
XCTAssertTrue(tosButton.isSelected) XCTAssertTrue(privacyButton.isSelected) XCTAssertTrue(continueButton.isEnabled)
continueButton.tap()
XCTAssertTrue(app.otherElements["home_view"].exists)}
复制代码

我还要添加许多其他内容,但下周将保留它们。本文已经充满了我认为我们应该休息一下的概念和想法。


您将在网上找到的代码稍微先进一些:请记住,测试与您的业务代码同等重要,因此您需要加以注意。这就是为什么我准备的代码井井有条,并排除了检查,并对状态更改逻辑和导航进行了两种不同的测试。


参考

https://uxdesign.cc/your-first-uitest-in-swift-847bc5595c26


3. Tips: 学习至少一个技术技巧

笔者的文章:

Swift 5用Template自动创建VIPER架构代码

说明

创建 VIPER 架构,XCode 手动创建比较麻烦,而且容易出错。很自然可以想到用自动化的方式创建,可以用 Template 进行创建。

1. 如何安装

  1. 克隆存储库 https://github.com/zafarivaev/VIPER-Template

  2. 导航到 Xcode Templates 文件夹:~/Library/Developer/Xcode/Templates/。如果“Templates”文件夹不存在,请创建它

  3. 将 VIPER Module.xctemplate 复制并粘贴到 Templates 文件夹中


2. 使用

  1. 开启 Xcode

  2. File -> New -> File 要么 ⌘ N

  3. 向下滚动,直到看到 VIPER Module 模板并选择它。

  4. 为您的模块设置一个名称。例子:Home, Auth, SignIn



3. 生成代码如下

3.1 AuthContract.swift


import Foundation

// MARK: View Output (Presenter -> View)protocol PresenterToViewAuthProtocol { }

// MARK: View Input (View -> Presenter)protocol ViewToPresenterAuthProtocol { var view: PresenterToViewAuthProtocol? { get set } var interactor: PresenterToInteractorAuthProtocol? { get set } var router: PresenterToRouterAuthProtocol? { get set }}

// MARK: Interactor Input (Presenter -> Interactor)protocol PresenterToInteractorAuthProtocol { var presenter: InteractorToPresenterAuthProtocol? { get set }}

// MARK: Interactor Output (Interactor -> Presenter)protocol InteractorToPresenterAuthProtocol { }

// MARK: Router Input (Presenter -> Router)protocol PresenterToRouterAuthProtocol { }
复制代码


3.2 AuthViewController.swift


import UIKit
class AuthViewController: UIViewController {
// MARK: - Properties var presenter: ViewToPresenterAuthProtocol?
// MARK: - Lifecycle Methods override func viewDidLoad() { super.viewDidLoad() } }
extension AuthViewController: PresenterToViewAuthProtocol{ // TODO: Implement View Output Methods}InteractorAuthInteractor.swift
import Foundation
class AuthInteractor: PresenterToInteractorAuthProtocol {
// MARK: Properties var presenter: InteractorToPresenterAuthProtocol?}
复制代码


3.3 AuthPresenter.swift


import Foundation
class AuthPresenter: ViewToPresenterAuthProtocol {
// MARK: Properties var view: PresenterToViewAuthProtocol? var interactor: PresenterToInteractorAuthProtocol? var router: PresenterToRouterAuthProtocol?}
extension AuthPresenter: InteractorToPresenterAuthProtocol { }
复制代码


3.4 AuthRouter.swift


import Foundationimport UIKit
class AuthRouter: PresenterToRouterAuthProtocol {
// MARK: Static methods static func createModule() -> UIViewController { let viewController = AuthViewController() let presenter: ViewToPresenterQuotesProtocol & InteractorToPresenterQuotesProtocol = AuthPresenter() viewController.presenter = presenter viewController.presenter?.router = AuthRouter() viewController.presenter?.view = viewController viewController.presenter?.interactor = AuthInteractor() viewController.presenter?.interactor?.presenter = presenter return viewController } }
复制代码


参考

https://github.com/zafarivaev/VIPER-Template


Create Xcode Templates

https://medium.com/overapp-ios/create-xcode-templates-c968d4b43f7b


4. Share: 分享一篇有观点和思考的技术文章

笔者的文章:

极客大学产品经理训练营 产品思维和产品意识(上) 第3课总结

说明

讲师:邱岳(二爷)


分享提纲

  • 什么是产品思维?

  • 从用户体验讲起


1. 产品思维是什么

  • 叙事和思考框架,而不是事情本身

  • 广阔的叙事范围,而不是单点

  • 从使用(消费)的角度感受,从设计(生产)的角度思考


1.1 产品分析:微信视频号

弱化微信 ID 的概念,一个微信个人号,可以建立多个微信视频号(相当于弱联系,但是推荐又需要精准推送)。为了解决微信的隐私问题,通过视屏号又允许用户公开分享视频内容。新建视频号的时候,会请求用户重新填写视频号的信息,签名,头像等等。


1.2 产品分析:动物世界(赵忠祥老师设计的场景)

猎豹趴在草丛中等待羚羊,会有两种场景:

  1. 猎豹妈妈好几天没有逮到羚羊,猎豹崽崽嗷嗷待哺;观众就会期待能抓到羚羊;

  2. 小羚羊刚出生,特别可爱;观众就会期待羚羊能够逃脱。


所以产品思维,会带领用户有不同的价值倾向。


《了不起的盖茨比》的作者斐济吉拉德说,检验一流智力的头脑的标准是:能在脑子中同时接收两种截然相反的观点,并且能够并行不悖。


1.3 产品分析:提款机

初级产品经理:想到的是提款机的界面,功能性的描述,比如界面,输入密码,如何取款。

资深产品经理:想到的是提款机的上下游,如何运转起来,比如提款机能存多少钱,运钞车如何把钱存到提款机,怎么开卡,卡片如何其作用等,用户存取款只是其中的一部分。


1.4 产品思维是下意识反应


  • 工程师思维:用什么方法,解决谁的,什么问题。

  • 产品经理思维: 解决谁的,什么问题,用什么方法。


看看谁

  1. 喝水的人:考虑到易拿起放下,喝水方便,不烫手等;

  2. 欣赏杯子的人:比如花瓶,艺术价值,观赏价值等。

  3. 运送杯子的人:考虑到不能易碎,容纳体积要小。(宜家)


产品思维:找到所有的利益相关方,找出有可能出现的问题,再想办法如何更好的解决问题。最终排序解决问题的优先级。


1.5 产品分析:外面平台

站在用户的角度本能就会想到买外面的行为,还要考虑到外面小哥的问题,比如安全的问题;商家的问题,还有餐饮店家炒菜的问题,等等。


1.6 解决方法伪装成需求

【插播】产品经理职业尊严第一条:不要直接伪装成解决方案的需求转发给工程师。

在这个页面上加个按钮,增加个订单排序功能,做弹窗。-- 这都是解决方案,不是需求。


案例分析:

迪扬,我内容没准备好,帮我把周三的直播时间调整到周五。-- 这是解决方案,不是需求。

  1. 谁?背后还藏着哪些相关人会收到波及?

利益相关方除了我,还助教,班主任,同学等等。

  1. 内容没准备好?

遇到了什么问题?PPT 没准备好码?场景是什么?

  1. 把周三的直播时间调整到周五。

方案合理吗?细节是什么?有其它替代方案吗?是不是真需求?


1.7 下雨天打车

二爷在下雨天打车,打电话给司机告诉他如何左转,右转,经过各种复杂操作,在哪个位置接他。司机直接打住二爷,你是不是不想淋雨。二爷说,是的。司机说,那我直接去地库接你不就好了么? -- 司机的本质工作可能是产品经理。


【插播】远古人类,最开始玩的游戏就是画人的脸。比如插座的两个孔,就是人委屈的脸。


谁是比较重要的角色,下节课会具体解析。这节课先讲用户。


2. 用户体验

  • 用户体验:使用产品或服务的情感和态度:包括一个人对系统方面的看法,如实用性、易用性和效率;对产品和系统的认知和思考模型;动态:随着使用和情境变化而发生变化。变现为人与系统的交互和感受。

  • 用户体验设计工具:角色模型/焦点小组/用户体验地图/关键人物地图/问题卡片


对产品和系统的认知和思考模型:比如夏天很热,开空调以后,用户会把空调调到很低比如 16°。其实空调调到 16°,不是空调吹出来的温度。


工具:https://servicedesigntools.org


2.1 用户体验的交易模型

  • 用户体验的交易模型:用户对系统的输入,交易,系统对用户的输出。

星巴克排队的方式,是横着拍的,从蛋糕排到咖啡机。比较放松,展示产品。空调里面都有咖啡的味道,味觉氛围。吧台的高度是调过的,用户跟工作人员是平等的。


3. 设计

  • 逻辑 -- Logic 音译过来的

  • 系统 -- System 音译过来的


设计 -- Design 音译是【点赞】,所以不是音译过来的。

重点在于计。


3.1 建造 --> 营造

make things --> make things happen

诸葛亮草船借箭。造箭是 make things,但是局势和条件所限;借箭是 make things happen。


每一次互动,都是交易。

SOP: Standard Operation Procedure 标准操作流程


交易产生,就是双方都觉得物有所值。




3.2 泡沫与感受

  1. 是不是一个谎言?

  2. 倘若坦诚相告,感情会不会破裂


用户价值 = 新体验 - 旧体验 - 替换成本

成本:

  • 显性成本:钱\时间\精力 -- 全力降低

  • 隐形成本:替换\信任\机会\发现... -- 全面分析,找到机会和路径


收益:

  • 泡沫价值:操纵\欺骗... -- 小心嵌入

  • 感受价值:用户感受 -- 场景化思考、细致打磨

  • 核心价值:用户核心需求 -- 挖掘/聚焦/确认



3.3 案例分析

  1. 浮动的元宝,目前验证是比较吸引人的交互方式;

  2. 内购列表,实际上是网页做成的样子;

  3. 浮动框的关闭按钮从右上角放到下面,把广告点击率提高了 50%以上。


CPC:cost per click 点击计费。


3.4 案例购票火车票



增加势差:

携程、飞猪等加速抢票实际上是泡沫价值,因为第三方买火车票也是调用 12306 的接口来购票的,根本就没有加速的概念。


3.5 怎么区分泡沫与感受

  1. 是不是一个谎言?

  2. 倘若坦诚相告,感情会不会破裂?


3.6 互联网如何做有竞争力的产品

  1. 全力降低:显性成本(钱\ 时间 \ 精力 ...)

  2. 全面分析找到机会和路径:隐形成本(替换成本\ 信任成本 \ 机会 \ 发现...)

  3. 小心陷入:泡沫价值(操纵\ 欺骗...)

  4. 场景化思考细致打磨:感受价值(用户感受)

  5. 挖掘/聚焦/确认:核心价值(用户核心需求)



总结

  • 多把精力放在没有泡沫的成本和收益上。

  • 捣鬼有术也有效,但有限,以此能成大事者,古来无有。


  • 《用户体验要素》 -- 特别推荐,把用户分层成 5 层

  • 《设计中的视觉思维》-- 活动的东西更容易引起用户的反应

  • 《Web 信息架构》-- 让用户知道自己在哪?让用户知道自己去哪?

  • 《设计心理学》-- 第一本写得贼好

  • 《交互设计沉思录》-- 写的比较深入

  • 《无界面交互》

  • 《点石成金》 -- Don't make me think

  • 《女性的起源》


关键词:UX 服务设计 信息架构 交互设计 视觉思维


发布于: 2021 年 01 月 24 日阅读数: 37
用户头像

John(易筋)

关注

问渠那得清如许?为有源头活水来 2018.07.17 加入

工作10+年,架构师,曾经阿里巴巴资深无线开发,汇丰银行架构师/专家。擅长架构、算法、数据结构、设计模式、iOS、Java Spring Boot。易筋为阿里巴巴花名。

评论

发布
暂无评论
算法:匹配有效的括号,Swift 5中UITest从入门到精通, Swift 5 Viper Template,极客大学产品经理训练营 产品思维和产品意识, John 易筋 ARTS 打卡 Week 36