Tip-Review-Algorithm 第十期

「Tip」使用Provisional Authorization发送试用通知

学习至少一个技术技巧。主要是为了总结和归纳你在日常工作中所遇到的知识点。

Notification 权限申请后可以用来展示提醒、播放提示音或者展示红点。

一种常用的申请方式是在上下文中显式地请求授权。效果如图:
A screenshot showing the system prompting the user to allow or disallow the use of alerts, sounds, and badges when the app sends notifications.

写法如下:

1
2
3
4
5
6
7
8
9
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in

if let error = error {
// Handle the error here.
}

// Enable or disable features based on the authorization.
}

这样子 App 会在第一次发起授权请求时提示用户批准或者拒绝授权,确定好用户权限偏好后,后续不会再提示用户。这样用户在不熟悉 App 的情况下大概率会直接拒绝。

Apple 提供了一种临时授权的方式(provisional authorization)来允许 App 发出通知,然后用户可以看到后再评估是否需要关闭通知。效果如图:

A screenshot of a notification in the notification center, with buttons to keep or turn off the notification.

代码如下,就是在 options 中增加 .provisional 选项。它的逻辑在于,不会提示用户来确定授权,而是会自动授予权限,将通知俏咪咪地放在历史记录中,由用户决定是保留还是关掉。如果用户选择保留,还有两种选择,1. Prominent Notification,就是申请授权时的包含的所有权限都授予 App;2. Quiet Notification,就是只允许通知在通知中心的历史记录里,而不授予其他三类权限。

1
2
3
4
5
6
7
8
9
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge, .provisional]) { granted, error in

if let error = error {
// Handle the error here.
}

// Provisional authorization granted.
}

文档链接:Asking Permission to Use Notifications

「Review」7 DevOps steps you’re missing

阅读并点评至少一篇英文技术文章。主要是为了学习英文,如果你的英文不行,基本上无缘技术高手。

第75期 OverFlow Newsletter 推荐了这份免费电子书,The Seven DevOps Steps You are Missing,看了下 Logo 应该是 GitLab 的推广文。它介绍了 DevOps 团队可能会忽视的7个任务,以及 DevOps 平台是如何进行助力的。

忽视的原因:为了软件快速推出落地,一些“尽量能够实施的好东西”,比如额外测试、监控或者更紧凑的循环反馈机制,其优先级都让位于“快速完成”。DevOps就跟节食和锻炼一样,都不容易。由于一个软件的开发生命周期使用的平台越多,需要进行交接、沟通、人工干预等的成本就越高,以下列举了七个使用单一 DevOPS平台能够带来好处。

给故障管理提供可追溯性

故障管理是每个团队不可避免的,找到一个能够简化该流程的方法至关重要。通常情况下,监控工具与IDE、部署过程是分开存在的,告警是外部产生的,没法与常用工具联系起来,而且也没有一个地方可以找到所有信息。

DevOps 平台可以轻松地在故障管理工具中提取数据,接收警告通知,对警告和故障进行分流,是相关开发人员处于流转中,而无需离开平台。由于从发现问题到诊断和补救的事件非常紧迫,一个与故障管理解决方案同步的 DevOps 平台意味着更快的解决。

测试一切

大多数团队没有定期运行静态应用安全(SAST)、动态应用安全(DAST)、合规或依赖扫描,即使是运行扫描的团队,大多数时候扫描结果也不会在回到 IDE 中展示出来。为了解决测试的难题,从一个全面的 DevOps 平台开始,其将会使测试更容易放置在流程的早期,并且开发人员不必去索要测试结果,因为测试结果会在 merge request 之后展示出来。如果自动化测试被内置到 DevOps 平台中,它将会变得更容易;它越容易,可以覆盖和触发的测试就越多。

实行测试只是战斗的一半,一旦发现 Bug,就必须要进行修复。同上一节,统一的 DevOps 平台能够提供内置的可视性和告警,能简化流程并将错误的发现前置,使开发人员修复Bug的可能性提高,而不必等在运行的后期发现Bug然后以笨拙的方式进行处理。另外,在提交 PR 后发现错误,意味着其他开发者可以不在错误的代码上继续开发。

主流程性能监控

和测试一样,性能监控是开发过程中的另外一个步骤,但是其不经常发生,是因为这样的工具并没有被纳入开发过程。但是,了解代码的实际运行性能对开发人员来说是很有用的,但谁也不愿意中断自己的工作流程,去寻找另外一个工具并进行模拟来测试性能,太低效了。还有一种方式是等待 QA 验证,但这也算不上一种好的解决方案。

一个统一的 DevOps 平台可以让开发人员利用工具对新代码进行性能测试,包括浏览器测试、相关的负载测试和动态应用安全(DAST)等重要的步骤,特特别是在代码被快速部署上线的环境中。能够真正看到代码在现实世界中按照预期工作,是非常令人满意的。

有效的合作

未来职业生涯中最重要的技能:沟通和协作。

大家都知道沟通和协作的重要性,但是让事情变简单是一个挑战,尤其是远程协作或者跨组织协作时,距离或时间是一个巨大的障碍,但更大的挑战是确保每个人都能获得相同的信息,无论大家处于什么角色。在许多组织中,协作工具可能是分散的,并且与实际的工作脱离开来。

统一的 DevOps 平台将所有的利益相关者带到了同一张桌子上。无论是产品、安全、人力资源还是法律。如果每个人都使用同一个平台,就很容易进行评论、分享,并对软件开发过程的每个部分由真正的可视性。

使用统一的平台还有一个好处,就是摩擦和误解可以缓解,因为围绕单一平台的合作意味着每个人都需要学习各自的行话,这将改善沟通和前进的结果。

合规变得更容易

追踪法规和条例在一些行业是强制性的,在其他一些行业则是最佳做法。但是合规性在大多数组织中是一种令人头痛的问题。即使是大公司也仍然在使用电子表格、电子邮件和云文件来监督合作工作。不得不在所有这些不同的地方寻找数据,然后将其与第三方工具或服务联系在一起,头疼。

此外,许多组织没有一个单一的合规性仓库;有些组织单独增加了治理、风险和合规性(Governance, Risk, Compliance, GRC)工具,而这些工具也没有雨DevOps 整合。事实上,开发和运营都将与合规有关的任务都视为“妨碍”应用快速发布,当然这种想法没有任何帮助。

统一的 DevOps 平台可以改变这种状况,如果大家都是在一个单一平台上,就很容易定义用户的角色和权限,从而建立起证书、安全扫描和规则批准的流程。因为应用的安全性检测本身就是在平台上构建的,所以自动满足信息安全的要求是一件很简单的事情。一些行业有非常具体的合规性要求,在单一的 DevOps 平台上工作,可以很容易的通过 Issue 和 PR 来进行跟踪协作、监管链以及重写(Override),没有必要用单独的工具来管理这个过程。

扭转技术债务

每个团队都在于技术债务做斗争,首先要证明解决它的时间是合理的。 DevOps 可以帮忙解决这个问题,因为非开发人员(包括产品和其他业务方)可以有机会接触到开发方的数据。

当业务方想知道为什么团队需要花时间在技术债务上时,看一下数字就会发现 25% 的部署是失败的,平均恢复时间是6小时 … 换句话说,花时间来偿还技术债务是有价值的。 DevOps 平台可以作为一种方式来宣传这个过程,给业务方提供更多的背景内容。

利用受控部署的优势

团队都希望有更多的自动化,有更多的控制,有能力不住尽可能多或者尽可能少的代码。 DevOps 平台可以改变这种情况,只需要一个工具,就可以很容易的将所有分散的协同部署部分集中到一个地方,从多个审批到核准、授权以及确保正确的人在正确的任务集上工作。这样子,Dev 和 Ops 对流程有了前所未有的控制。

得嘞,看完才知道真的是推广文。但是说的有道理么,确实有道理,工作之后发现协作机制上有很多不合理的地方,跟这篇文章也算呼应了。

「Algorithm」[打开转盘锁]

每周至少做一个 LeetCode 的算法题。主要是为了编程训练和学习。如果不训练你看再多的算法书,你依然不会做算法题。

题目:

你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为 '0''0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。

锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。

列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。

字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。

思路:

BFS。

BFS框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 计算从起点start到终点target的最短距离
int BFS(Node start, Node target) {
Queue<Node> q; // 核心数据结构
Set<Node> visited; // 避免走回头路

q.offer(start); // 将起点加入队列
visited.add(start);
int step = 0; // 记录扩散的步数

while (q not empty) {
int sz = q.size();
// 将当前队列中的所有节点向四周扩散
for (int i = 0; i < sz; i++) {
// 判断是否到达终点
if (cur is target) {
return step;
}

// 将cur的邻节点加入队列
for (Node x: cur.adj()) {
if (x not in visited) {
q.offer(x);
visited.add(x);
}
}
}
step++;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
* @lc app=leetcode.cn id=752 lang=cpp
*
* [752] 打开转盘锁
*/

// @lc code=start
class Solution {
public:
// 单向BFS
int openLock(vector<string>& deadends, string target) {
unordered_set<string> dead;
unordered_set<string> visited;
for (string s : deadends) {
dead.insert(s);
}

queue<string> queue;
int step = 0;
queue.push("0000");
visited.insert("0000");

while (queue.size()) {
int sz = queue.size();
for (int i = 0; i < sz; i++) {
string cur = queue.front();
queue.pop();
if (dead.count(cur)) {
continue;
}
if (cur == target)
{
return step;
}

for (int i = 0; i < 4; i++)
{
string up = plusOne(cur, i);
if (!visited.count(up))
{
queue.push(up);
visited.insert(up);
}
string down = minusOne(cur, i);
if (!visited.count(down))
{
queue.push(down);
visited.insert(down);
}
}
}
step++;
}
return -1;
}

string plusOne(string s, int n) {
if (s[n] == '9')
{
s[n] = '0';
} else {
s[n] += 1;
}
return s;
}

string minusOne(string s, int n) {
if (s[n] == '0')
{
s[n] = '9';
} else {
s[n] -= 1;
}
return s;
}
};
// @lc code=end