Gowhich

Durban's Blog

Map(swift 5.3)的使用

对集合类型中的每一个元素做一次处理,转换为新数组

数组系列

  • 案例1 - 遍历每个元素
1
2
3
4
5
6
let colors = ["red", "yellow", "green", "blue"]
let counts = colors.map { (color: String) -> Int in
return color.count
}

print(counts)

结果是 [3,6,5,4]

  • 案例2 - 更加简单的方法
1
2
let counts1 = colors.map { $0.count }
print(counts1)

结果也是 [3,6,5,4]

  • 案例3 - 转换为对象数组(请问下转换为对象数组干啥用)
1
2
3
4
5
6
7
8
9
10
11
12
class Color {
var name: String
init(name: String) {
self.name = name
}
}

let colorsObj = colors.map { return Color(name: $0) }

for obj in colorsObj {
print(obj.name)
}

结果是

1
2
3
4
red
yellow
green
blue

集合系列

1
2
3
let ColorsSet: Set = ["red", "yellow", "green", "blue"]
let colorsCount = ColorsSet.map { $0.count }
print(colorsCount)

结果是**[3, 6, 4, 5]**

字典系列

1
2
3
4
5
let dict = [2: "red", 4: "yellow", 6: "green", 8: "blue"]
let keys = dict.map { $0.key }
print(keys)
let values = dict.map { $0.value }
print(values)

结果分别是

[2, 8, 6, 4]

[“red”, “blue”, “green”, “yellow”]

WKWebView有一个内容交互控制器,该对象提供了通过JS向WKWebView发送消息的途径。需要设置MessageHandler,我把这个功能简称为MessageHandler。

首先了解下iOS开发中需要调用的方法

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

- (void)removeScriptMessageHandlerForName:(NSString *)name;

下面看下如何调用

添加MessageHandler

1
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"showAlert"];

移除MessageHandler

1
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"showAlert"];

JS调用

window.webkit.messageHandlers.<name>.postMessage(<messageBody>)

这个name就是设置MessageHandler的第二个参数

1
window.webkit.messageHandlers.getString.postMessage(null);

注意点

在JS中写起来简单,不用再用创建URL的方式那么麻烦了。

JS传递参数更方便。使用拦截URL的方式传递参数,只能把参数拼接在后面,如果遇到要传递的参数中有特殊字符,如&、=、?等,必须得转换,否则参数解析肯定会出错。

示例演示

html代码部分

四个按钮分别演示JS传NULL给WKWebView,然后WKWebView调用JS方法;JS传字符串、JS传数组、JS传字典改变导航背景。

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
<!DOCTYPE html>
<html lang="zh-cn">

<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function loadURL(url) {
var iFrame;
iFrame = document.createElement("iframe");
iFrame.setAttribute("src", url);
iFrame.setAttribute("style", "display:none;");
iFrame.setAttribute("height", "0px");
iFrame.setAttribute("width", "0px");
iFrame.setAttribute("frameborder", "0");
document.body.appendChild(iFrame);
// 发起请求后这个iFrame就没用了,所以把它从dom上移除掉
iFrame.parentNode.removeChild(iFrame);
iFrame = null;
}

function asyncAlert(content) {
setTimeout(function(){
alert(content);
},1);
}

function showAlert() {
window.webkit.messageHandlers.showAlert.postMessage(null);
}

function alertWithMessage(content) {
asyncAlert(content);
document.getElementById("returnValue").value = content;
}

function postString() {
window.webkit.messageHandlers.postString.
postMessage('r=10,g=170,b=250,a=0.5');
}

function postArray() {
window.webkit.messageHandlers.postArray.
postMessage([Math.floor(Math.random()*255),
Math.floor(Math.random()*255),
Math.floor(Math.random()*255),0.5]);
}

function postDictionary() {
window.webkit.messageHandlers.postDictionary.
postMessage({red: Math.floor(Math.random()*255),
green: Math.floor(Math.random()*255),
blue: Math.floor(Math.random()*255),
alpha: 0.5});
}

</script>
</head>

<body>
<input type="button" value="OC调用JS方法" onclick="showAlert()">
<input type="button" value="JS传字符串" onclick="postString()">
<input type="button" value="JS传数组" onclick="postArray()">
<input type="button" value="JS传字典" onclick="postDictionary()">
</body>

</html>

object-c代码部分

  • 加载WKWebView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)loadWebView {
// 偏好配置
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
config.preferences = [WKPreferences new];
config.preferences.minimumFontSize = 30.0f;
config.preferences.javaScriptCanOpenWindowsAutomatically = YES;

self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
self.webView.UIDelegate = self; // 设置交互代理
[self.view addSubview:self.webView];

// 加载HTML
NSString *path = [[NSBundle mainBundle] pathForResource:@"index2" ofType:@"html"];
NSString *htmlStr = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[self.webView loadHTMLString:htmlStr baseURL:nil];
}
  • 添加和移除处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"showAlert"];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"postString"];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"postArray"];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"postDictionary"];
}

- (void)dealloc {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"showAlert"];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"postString"];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"postArray"];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"postDictionary"];
}
  • 实现协议

我这里实现了两个协议 WKUIDelegate,WKScriptMessageHandler,WKUIDelegate是因为我在JS中弹出了alert。WKScriptMessageHandler是因为我们要处理JS调用OC方法的请求。

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
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:@"showAlert"]) {
[self alert];
}
else if ([message.name isEqualToString:@"postString"]) {
[self changeColorWithString:message.body];
}
else if ([message.name isEqualToString:@"postArray"]) {
[self changeColorWithArray:message.body];
}
else if ([message.name isEqualToString:@"postDictionary"]) {
[self changeColorWithDictionary:message.body];
}
}

- (void)alert {
// OC调用JS
NSString *jsStr = [NSString stringWithFormat:@"alertWithMessage('%@')", @"OC调用JS的方法"];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}

- (void)changeColorWithString:(NSString *)string {
NSArray *params =[string componentsSeparatedByString:@","];

NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
for (NSString *paramStr in params) {
NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];
if (dicArray.count > 1) {
NSString *decodeValue = [dicArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[tempDic setObject:decodeValue forKey:dicArray[0]];
}
}
CGFloat r = [[tempDic objectForKey:@"r"] floatValue];
CGFloat g = [[tempDic objectForKey:@"g"] floatValue];
CGFloat b = [[tempDic objectForKey:@"b"] floatValue];
CGFloat a = [[tempDic objectForKey:@"a"] floatValue];

self.navigationController.navigationBar.backgroundColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a];
}

- (void)changeColorWithArray:(NSArray *)array {
CGFloat r = [array[0] floatValue]/255.0;
CGFloat g = [array[1] floatValue]/255.0;
CGFloat b = [array[2] floatValue]/255.0;
CGFloat alpha = [array[3] floatValue];
self.navigationController.navigationBar.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:alpha];
}

- (void)changeColorWithDictionary:(NSDictionary *)dict {
CGFloat r = [dict[@"red"] floatValue]/255.0;
CGFloat g = [dict[@"green"] floatValue]/255.0;
CGFloat b = [dict[@"blue"] floatValue]/255.0;
CGFloat alpha = [dict[@"alpha"] floatValue];
self.navigationController.navigationBar.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:alpha];
}
  • WKWebView中使用弹窗
1
2
3
4
5
6
7
8
#pragma mark - WKUIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alertCrontroller = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
[alertCrontroller addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alertCrontroller animated:YES completion:nil];
}

文章参考:https://www.jianshu.com/p/160f529e16fa

State Property Wrappers 的用法

示例如下

1
2
3
4
5
6
struct MyView: View {
@State var myString: String = "Hello"
var body: some View {
OtherView(shareText: $myString)
}
}

Binding Property Wrappers 的用法

示例如下

1
2
3
4
5
6
7
struct OtherView: View {
@Binding var shareText: String

var body: some View {
Text(shareText)
}
}

最后调用下MyView,如下(建议Playground中运行)

1
MyView(myString: "Hello world")

测试服务器动态绑定多个域名

比如我现在有一个

1
api.domain.com

但是测试需要给不同分支代码绑定一个不同的域名方便访问

需求类似如下

1
2
3
api1.domain.com
api2.domain.com
api2.domain.com

可以像下面这样添加nginx配置

1
2
3
4
5
6
7
server {
listen 80;
server_name ~^api(?<which_domain_id>\d*)\.domain\.com$;
access_log logs/api.domain.com.access.log main;
error_log logs/api.domain.error.log;

}
1
2
3
4
5
6
server {
listen 443 ssl;
server_name ~^api(?<which_domain_id>\d*)\.domain\.com$;
access_log logs/api.domain.com.443.access.log main;
error_log logs/api.domain.443.error.log;
}

这样我们就可以拿到$which_domain_id变量,为后面的测试提供用途

比如upstream的配置

比如root的配置

比如我们配置三个upsteam

1
2
3
4
5
6
7
8
9
10
11
upsteam api1 {
// ...
}

upsteam api2 {
// ...
}

upstream api3 {
// ...
}

在用location做转发的时候,可以像下面这样使用了

1
2
3
4
5
6
7
8
9
location = /api/xxx/info {
proxy_pass http://api$which_domain_id;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
keepalive_timeout 0;
}

不过你如果用到了proxy_redirect的话,就不能proxy_redirect default这样的配置了,可以改为类似下面的配置

1
2
3
4
5
6
7
8
9
10
location = /api/xxx/info {
proxy_pass http://api$which_domain_id;
proxy_redirect /api/xxx/info /api/xxx/info;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
keepalive_timeout 0;
}

像上面如果是转发到同一个路由的话其实也可以不用加proxy_redirect

1
2
3
4
5
6
7
8
9
location = /api/xxx/info {
proxy_pass http://api$which_domain_id;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
keepalive_timeout 0;
}

在配置root的时候可以参考下面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80;
server_name ~^api(?<which_domain_id>\d*)\.domain\.com$;
root /var/www/api.domain.com/$which_domain_id/public/;
index index.html index.htm index.php;

error_log logs/api.domain.access.log main;
error_log logs/api.domain.error.log;

client_max_body_size 3M;

location / {
try_files $uri $uri/ /index.php?$args;
}

location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

最后声明这个which_domain_id只是我这里的需要可以用作id操作,你可以改为字符串参数

文章参考:http://nginx.org/en/docs/http/server\_names.html

这个提示意思是 AFHTTPSessionManager 中没有这个方法

原因:升级后导致,原来方法被废弃,应该换成新的方法,需要增加一些参数,具体看文档

我遇到的问题解决了 是因为我的AFNetworking版本是4.0

所以见上面的方法替换为下面的方法

1
POST:parameters:headers:progress:success:failure:

类似的

GET
DELETE
PUT

都需要加对应的headers参数

问题:error: Unable to load contents of file list ......

原因是:升级了一下 cocoapods的版本后,由于版本不同导致

重新安装或者升级一下就好

1
gem install cocoapods --pre

然后把工程里面的 Pod 文件夹和 Podfile.lock 文件删掉,然后 cd 到项目根目录,然后重新运行一下 pod install 命令,重新编译即可。

TypeScript Challenge - 实现一个通用MyReadonly2<T, K>,它带有两种类型的参数T和K

题目


实现一个通用MyReadonly2<T, K>,它带有两种类型的参数TK

K指定应设置为Readonly的属性集(如果T)。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly<T>一样。

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Todo {
title: string
description: string
completed: boolean
}

const todo: MyReadonly2<Todo, 'title' | 'description'> = {
title: "Hey",
description: "foobar",
completed: false,
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK

测试用例


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Alike, Expect } from '@type-challenges/utils'

type cases = [
Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>,
Expect<Alike<MyReadonly2<Todo1, 'title' | 'description'>, Expected>>,
]

interface Todo1 {
title: string
description?: string
completed: boolean
}

interface Expected {
readonly title: string
readonly description?: string
completed: boolean
}

答案


1
2
3
4
5
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [P in K]: T[P]
} & {
[P in keyof T]: T[P]
}

蒙圈了

新手入门,了解Nest.js的Modules

在Nest.js中,一个带有@Module()装饰器的类,即是一个Module

每个Application都至少有一个Module,root module

同时Nest.js强烈建议使用一个Module作为一个有效的方式来管理你的组件

下面看下如何创建一个Module

创建一个module

创建module的命令如下

1
nest g module cats

运行后结果如下

1
2
3
$ nest g module cats
CREATE src/cats/cats.module.ts (81 bytes)
UPDATE src/app.module.ts (603 bytes)

创建后默认的代码如下

1
2
3
4
import { Module } from '@nestjs/common';

@Module({})
export class CatsModule {}

使用module

下面修改下cats.module.ts,修改后的结果如下

1
2
3
4
5
6
7
8
9
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}

同时需要修改下app.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AccountController } from './account/account.controller';
import { DogsController } from './dogs/dogs.controller';
import { CatsModule } from './cats/cats.module';

@Module({
imports: [CatsModule],
controllers: [AppController, AccountController, DogsController],
providers: [AppService],
})
export class AppModule {}

跟没有使用cats.module.ts的时候对比下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsController } from './cats/cats.controller';
import { AccountController } from './account/account.controller';
import { DogsController } from './dogs/dogs.controller';
import { CatsService } from './cats/cats.service';
import { CatsModule } from './cats/cats.module';

@Module({
imports: [CatsModule],
controllers: [AppController, CatsController, AccountController, DogsController],
providers: [AppService, CatsService],
})
export class AppModule {}

可以发现少了对应的CatsController,CatsService

从这个方面可以看出 Nest.js的代码体现了SOLID原则,对代码的维护是很有帮助的。

laravel升级记录,laravel from 5.2 upgrade 5.3

升级之前我们要知道的第一个点,在5.3中被移除的方法如下,详情点击这里

  • 1 - Illuminate\Contracts\Bus\SelfHandling contract. Can be removed from jobs.
  • 2 - The lists method on the Collection, query builder and Eloquent query builder objects has been renamed to pluck. The method signature remains the same.
  • 3 - Implicit controller routes using Route::controller have been deprecated. Please use explicit route registration in your routes file. This will likely be extracted into a package.
  • 4 - The get, post, and other route helper functions have been removed. You may use the Route facade instead.
  • 5 - The database session driver from 5.1 has been renamed to legacy-database and will be removed. Consult notes on the “database session driver” above for more information.
  • 6 - The Str::randomBytes function has been deprecated in favor of the random_bytes native PHP function.
  • 7 - The Str::equals function has been deprecated in favor of the hash_equals native PHP function.
  • 8 - Illuminate\View\Expression has been deprecated in favor of Illuminate\Support\HtmlString.
  • The WincacheStore cache driver has been removed.

如果是在国内的话,记得更新镜像源,请参考这里

然后修改composer.json中

1
"laravel/framework": "5.2.*",

改为

1
"laravel/framework": "5.3.*",

然后执行

1
composer update --verbose

或者

1
composer update --verbose --ignore-platform-reqs

如果遇到依赖问题,比如我这里遇到了laravelcollective/html

类似requires...satisfiable...的提示

只需要将这个库暂时移除,然后在执行上面的命令

顺利的话,就安装成功了

然后在安装回刚刚移除的库,比如laravelcollective/html,这个时候可能会遇到,如果使用原来老的版本的话,会提示类似不兼容现在laravel5.3这个版本,需要针对性的安装对应的版本,如果不带版本号的话会安装最新版本,那个时候,依赖的项目会很多,composer会自动升级对应的依赖版本,建议不要这么做

最后安装完,参考[这里],修改对应的代码就可以了。

0%