walker's code blog

coder, reader

iOS签名相关命令

用openssl查看CSR文件内容

openssl asn1parse -i -in CertificateSigningRequest.certSigningRequest

查看下发的证书的内容:

openssl x509 -inform der -in ios_development.cer -noout -text

可以使用如下命令查看一个mobileprovision:

security cms -D -i embedded.mobileprovision

ipa文件是一个zip包,可以使用如下命令解压:

/usr/bin/unzip -q xxx.ipa -d <destination>

用下面命令,列出系统中可用于签名的有效证书:

/usr/bin/security find-identity -v -p codesigning

使用如下命令对xxx.app目录签名,codesign程序会自动将其中的文件都签名,(Frameworks不会自动签):

/user/bin/codesign -fs "iPhone Developer: Your Cert Name (VDT388662Q)" --no-strict Payload/xxx.app

最后用下面命令校验签名是否合法:

/usr/bin/codesign -v xxx.app

使用zip命令重新打包成ipa包

/usr/bin/zip -qry destination source

来源

关于@synthesize

首先, @synthesize myLocalVar = _myLocalVar; 这句话是显式帮你省掉了一个 getter 方法和一个 setter 方法. 两个方法长什么样不赘述.

其次, 从某个版本的 Xcode 开始, 你连 @synthesize 这句话也不需要写了, 但是请注意, 这只是一个 IDE 的特性. 你不需要手动合成, 不代表 @synthesize 不作用了, 仅仅是让能少写这一句话, 而 Xcode 帮你补全了.

再次, @synthesize 仅仅是一个 clang 的 Objective-C 语言扩展 (autosynthesis of properties), 然后clang恰好是 Xcode 的默认编译器. 也就是说, 如果你换成了 gcc, 那么这个特性也就不复存在了.

基于上述, 如果你使用了自己的文本编辑器, 然后用自己用 clang 从命令行编译, @synthesize 这一句话是需要自己写的.

最后, 有如下例外

  1. 对于 readwrite 类型的属性, 你自行实现了 gettersetter
  2. 对于 readonly 类型的属性, 你自行实现了 getter 以上两种情况, 你一旦自行实现了对应的 gettersetter, 对于本文的myLocalVar例子, 你将发现 _myLocalVar没有了, 意味着你需要@synthesize一下.
  3. dynamicsynthesize是互斥的
  4. @protocol中声明的属性
  5. category中声明的属性
  6. 你覆盖(overridden)父类的属性时, 必须手动synthesize.

参考资料: 1, When should I use @synthesize explicitly? 2, @dynamic 与 @synthesize 关键词详解

重装mac系统后ssh异常

表现在两个方面: ssh 登录服务器, 和通过ssh 使用 git, 报的错都是Permissino denied (publickey)

git异常的解决

根据github文档, 我可以解决第二个问题, 第一个问题(ssh login)解决后再来更新.

使用git出现问题, 并不局限于 github, 我连 gitlab 也是一样. 其实根据文档一项项检测,我在ssh-add -l这一句发现了问题, 虽然我生成了密钥, 但是它的输出显示我没有私钥, 照如下解决即可:

Tip: On most systems the default private keys (~/.ssh/id_rsa

, ~/.ssh/id_dsa and ~/.ssh/identity) are automatically added to the SSH authentication agent. You shouldn't need to run ssh-add path/to/key unless you override the file name when you generate a key.

也就是说, 把你的id_rsa文件手动指定一下ssh_add 文件路径(不知道为什么会出这种鬼问题)

SSH自动登录异常的解决

这是补充的内容, 刚刚解决.

首先, 你登不上这是服务端的问题, 没有把你的公钥写到~/.ssh/authorized_keys里面去, 你想办法把它写进去(当你 ssh 不上去的时候, scp当然也不行)

写进去后, 仍然报错, 我们继续看日志: ssh [email protected] -vssh [email protected] -vvv (更详细的日志)

通过看日志, 你可以一步步看到问题:

...
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey
debug3: start over, passed a different list publickey
debug3: preferred publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Trying private key: .ssh/id_rsa
debug3: no such identity: .ssh/id_rsa: No such file or directory
debug2: we did not send a packet, disable method
debug1: No more authentication methods to try.
Permission denied (publickey).

注意截取的倒数第4行: 首先, 它昨天不是这么提示的, 它提示的是.ssh/id_rsd, 我到配置文件里把它改成了rsa, 因为我生成的是 rsa

配置文件的地址是: /etc/ssh/ssh_config, 改的节点名是IdentityFile

其次, 顺便看一下RSAAuthenticationPubkeyAuthentication这两项是不是 yes(如果是注释状态, 不要动, 默认是 yes)

这样, 服务端有你的公钥, 本地配置了 IdentityFile 路径, 就可以登录了, 但我一直没成功的原因在于, 我在原配置文件改的, 它是路径是.ssh/id_rsa,.ssh/id_rsd, 我单纯把 rsd 的去掉, 却没发现它的路径是错的, 直到看了日志提示这个文件不存在, 才想起把格式改对.

我碰到的情况不知道是不是个例, 比如同一个文件

UserKnownHostsFile ~/.ssh/known_hosts,~/.ssh/known_hosts2

这一行, 路径格式却是正确的, 匪夷所思.

总之, 你碰到Permission denied (public key)这个问题, 就结合 git 和 server 这两情况, 看是没有 identity, 还是 id_rsa路径配错了. (前提是公钥必须已经写到服务器上去了)

备份Nginx设置php的方法

版本nginx/1.12.2

  location ~ \.php$ {
    fastcgi_pass   unix:/run/php/php7.0-fpm.sock;
    fastcgi_param  SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    include        fastcgi_params;
   }

我机器上的fastcgi_param值不对, 改成上图成功, 备份下

让Objective-C也有map功能

map一个数组是大部分高级语言都有的, OC 没有, 有几个方案让它实现, 我优选出三个:

原生实现

其实就是valueForKeyPath的活用:

NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"];
NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}.<NSUnarchiveFromDataTransformerName>.albumCoverImageData"];

category

这个大家肯定早想到过了, 你没有, 我给你扩展出来一个:

定义:

@interface NSArray (Map)

- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block;

@end

@implementation NSArray (Map)

- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block {
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [result addObject:block(obj, idx)];
    }];
    return result;o
}

@end

使用:

NSArray *people = @[
                     @{ @"name": @"Bob", @"city": @"Boston" },
                     @{ @"name": @"Rob", @"city": @"Cambridge" },
                     @{ @"name": @"Robert", @"city": @"Somerville" }
                  ];
// per the original question
NSArray *names = [people mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
    return obj[@"name"];
}];
// (Bob, Rob, Robert)

// you can do just about anything in a block
NSArray *fancyNames = [people mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
    return [NSString stringWithFormat:@"%@ of %@", obj[@"name"], obj[@"city"]];
}];
// (Bob of Boston, Rob of Cambridge, Robert of Somerville)

三方库

是的, 一般简单功能能自己实现就自己实现, xcode 项目还是不能像 nodejs项目一样, 哪怕有的包里也只有一句话, 我也要从用第三方的...ddddd

js 的世界里, underscore用来处理数组可算神器, 自然, 我也挑中了同样名字的 OC 库: Underscore

NSArray *tweets = Underscore.array(results)
    // Let's make sure that we only operate on NSDictionaries, you never
    // know with these APIs ;-)
    .filter(Underscore.isDictionary)
    // Remove all tweets that are in English
    .reject(^BOOL (NSDictionary *tweet) {
        return [tweet[@"iso_language_code"] isEqualToString:@"en"];
    })
    // Create a simple string representation for every tweet
    .map(^NSString *(NSDictionary *tweet) {
        NSString *name = tweet[@"from_user_name"];
        NSString *text = tweet[@"text"];

        return [NSString stringWithFormat:@"%@: %@", name, text];
    })
    .unwrap;

当然, 所有方案来源于StackOverflow 上的答案, 一些其它方案, 其它库(如BlocksKit), 都可以试试, 也挺简洁的

让你fork下来的项目与源项目保持同步

原文在此, 建议阅读, 我把关键步骤抽出来了, 方便概览

(也就是add remote upstream, fetch upstream, rebase, 再push)

Step 1: Forking a repo

git clone https://github.com/nitin-test/blog-example-fork.git
git remote add upstream https://github.com/nitstorm/blog-example.git
git remote
git remote show origin
git remote show upstream

Step 2: Making changes and submitting Pull Requests

git checkout -b word-addition
git commit -am "Adds the word memory"
git push origin word-addition
# 网页端发起merge到master

Step 3: Keeping the forked repo synced with the main repo

# 确保你是在master分支
git checkout master
git fetch upstream
git rebase upstream/master
git push origin master

Windows下用AHK来映射Mac常用快捷键

最近用Windows做了一段主力机, 别的问题倒没啥, Windows下有啥干不了的呢, 哪怕鼠标滚轮方向相反, 大量快捷键不同等问题都能在非常短的适应后就不会对自己造成什么困找, 问题是我还得在Windows和Mac环境切来切去, 这样频繁地按错快捷键再纠正也挺烦人的, 决定一劳永逸地解决这个问题, 经过反复搜索和比较, 还是一个以前用Windows的时候就一直敬而远之的软件成了我的首选:AutoHotKey(AHK).

以前不想用它就是因为要写脚本, 没有大量的需求的情况下我并不想去通读一遍那并不算短的文档, 这次不同了, 需要定制一堆快捷键, 感觉这个付出是值得的, 一番文档和实验之后, 初步得到如下脚本: (特别惊喜的是, 之前要改变鼠标滚轮的方向要改注册表, 这样你每次换了个usb口插鼠标还得再改一次, 而在ahk下都是一句话的事...是不是有点佩服这个才百来k的软件了?)

;----------Application

; hyper
^`::send #4

;----------Mac Key Remap

; nature scroll
;WheelUp::send {WheelDown}
;WheelDown::send {WheelUp}

; cmd+c,v,x
#c::send ^c
#v::send ^v
#x::send ^x
; 用win+shift+x来替换原来的win+x
#+x::send #x

; clipboard history
; 用win+shift+v来替换原来的win+v(剪贴板历史记录)
#+v::send #v

; save
#s::send ^s

; undo, redo
#z::send ^z
#+z::send ^+z

; selection
#+Left::send +{Home}
#+Right::send +{End}
#+Up::send +{Up}
#+Down::send +{Down}

; skip word using alt+Arrow
!Left::send ^{Left}
!Right::send ^{Right}

; switch win+tab & alt+tab
releaseAltKey() {
  if GetKeyState("LWin") {
    sleep 100
    releaseAltKey()
  }
  else
    send {alt up}
}
#Tab::
send {alt down}{Tab}
releaseAltKey()
return

!Tab::send #{Tab}

; switch ctrl+w & cmd+w
#w::send ^{w}
; 用win+shift+w替换win+w
#+w::send #{w}

; new tab
#t::send ^t
#+t::send ^+t

; search
#f::send ^f

; cmd+a
#a::send ^a

; cmd+n
#n::send ^n

我这里不是教程, 就不解释每一行的原理了, 就我个人而言最最最常用的复制粘贴切换程序等等都映射了, 唯一不完美的地方就在于把热键对换我没研究透(比如ctrl+x 和 win+x对换), 有三处热键对换, 但只有一处成功(alt+tab 和 win+tab互换), 其它两处只能把其中一个热键换成别的, 而不能互相替换, 在官方论坛发贴目前还没人答复我.

多说一句, 最前面的Application部分我贴代码的时候没删, 也是为了介绍一种打开程序的快捷方法的思路, 一般人会用脚本检测程序是否打开, 比如邮件应用, 如果打开, 则将窗口激活, 如果没有, 则打开程序, 如果当然已是激活状态, 则把它最小化. 而我发现如果一个程序在底部状态栏里的话, 用win+数字键激活它也可以达到上述三个目的, 比如我的hyper应用排在第四个, 那么我只要反复按win+4就能在上述三种状态间切换, 比写代码完成检测, 打开, 激活, 最小化等要来得简单的多.

期间还有一个收获, mac下多桌面的切换非常简单, ctrl+左右方向键即可, 或者用四根手势轻扫触摸板, 而windows下则点taskview图标, 或者用win+tab激活, 然后再用鼠标选择(总的来说是 Win + Ctrl + D / F4 / ← / → 这些组合), 怎样都没有mac的方案来得优雅(就像mac下没有一个窗口管理程序有windows自带的优雅一样^_^), 发现有人写了一个脚本检测鼠标位置, 如果到了最右侧或最左侧, 就直接把下一个桌面拉出来, 原本没打算分享只是自己用用, 因此出处没保存, 抱歉, 不过类似的脚本网上很多, 还有很多很精细的, 这一个已经算很简单粗暴了, 可以抱着学习的目的了解下, 毕竟这个脚本是以毫秒级别在时刻监视鼠标位置, 这是没必要的性能消耗吧:

;----------------屏幕热区
;这个是设置鼠标坐标的相对位置本例是相对雨整个桌面
CoordMode, Mouse ,Screen

#Persistent
;这个设置了获取鼠标信息的频率数值越小边缘热区越灵敏
SetTimer, WatchCursor, 300
return

WatchCursor:
GetKeyState, state, LButton 
MouseGetPos, xpos, ypos, id, control 
;若要重设边缘热区的范围请把下一行的 ; 号去掉就会在鼠标位置显示鼠标的坐标根据坐标修改以下数值
;ToolTip,x:%xpos% y:%ypos% state:%state%
if(state = "U" ){
    ;y方向的范围
    if(ypos > 150 and ypos < 1000){
        ;x方向的范围
        if(xpos = 2879){
            sleep 650
            Send ^#{Right}
            MouseMove, 2870, ypos
        }else if(xpos = 0){
            sleep 650
            Send ^#{Left}
            MouseMove, 20, ypos
        }
    ;显示所有虚拟桌面的热区
    }else if(xpos = 0 and ypos = 0){
        Send #{Tab}
        MouseMove, 10, 10
    }
}
return

注释很明确了, 自己读, 鼠标检测的热区是代码写死的, 解除显示鼠标位置的注释, 然后你把代码改成适合你的屏幕分辨率的数值即可

开发apple-pay碰到的问题总结

本来想简单总结一下Apple Pay 开发过程中的几个问题, 结果被这篇文章全碰上了, 干脆全文转载, 作者对相关资源整理得比较详细, 比较有参考价值, 建议阅读, 我做个概述.

总的来说, 我们做过 APNs 推送的话, 申请 商户ID 并关联到 apple id, 申请证书, 生成provisioning profile等步骤都差不多

然后我真机调试有两个地方没通过, 下文也总结了, 我拎出来单独说一下:

1, Payment request is invalid: check your entitlements. Connection to remote alert view service failed

原因:

粗心, 把merchant id写错了. 之所以要把粗心的事也列出来, 是因为, 我出问题是粗心, 但是因为集成苹果支付的过程中, 是需要在配置界面的Capabilities里面用下拉列表选择一个merchant id, 以及代码里还要写一次的, 如果你有多个merchant id, 或者开发过程中切换过, 下拉列表值和代码里手写的值要记得同步, 没有同步, 一样会得上上面的错误

2, 进不到didAuthorizePayment方法.

原因:

payrequest.merchantCapabilities = PKMerchantCapability3DS|PKMerchantCapabilityEMV. 看到了吧, 后面的 EMV 是必须要加的 大部分碰到同样问题的同学估计都是看 WWDC 的视频, 里面的小哥说3DS 是必须的, 显然在咱们大天朝, EMV 也是必须的.

百度地图坐标转换

首先, 我们了解一下为什么要坐标转换

国际经纬度坐标标准为WGS-84,国内必须至少使用国测局制定的GCJ-02,对地理位置进行首次加密。百度坐标在此基础上,进行了BD-09二次加密措施,更加保护了个人隐私。百度对外接口的坐标系并不是GPS采集的真实经纬度,需要通过坐标转换接口进行转换。

其次, 我们在网上搜到有通过 http://api.map.baidu.com/ag/coord/convert?from=[0/2]&to=4&x=纬度&y=经度 这种URL来进行转换的, 其中0代表WGS-84即标准GPS设备返回的坐标, 2代表国测局的标准, 显然4就是百度地图认的坐标了. 但我遍搜网络, 这个接口是没有文档的(但是确实可用). 因为会碰到跨域的问题(CORS), 所以就用了jQuery中script标签跨域的方式来执行, 结果这个脚本也被百度”回收”了, 当然, 网上还是有痕迹的, 我把它保存了一下, 见此gist. 我没在这上面花太多时间, 并没有测它支不支持批量转换(看网友写了一个transMore, 是必须批量送入, 但底层还是一次次分别请求, 不合我意).

而事实上, 百度已经提供了文档化的坐标转换接口, 并且原生就支持批量请求. 显然, 我们应该用文档化的方法, 只是这种用法需要申请成为一个开发者, 添加一个应用. 这不是小事么, 于是我小包装了一下, 见此gist.

仅仅有一点小要求, 就是传入的坐标, 键名分别是lon(经度)和lat和(纬度), 并且可以传入一个(传入一个, 返回也是一个), 或者一组

转换一个坐标:

BMap.Convertor.translate({lon:lon, lat:lat}, 1, 5, bdkey, function (point, status, message) {
    if(status) return show(message || "转换坐标出错:"+status, true);
    var curmarker = new BMap.Marker(point);
    baidumap.addOverlay(curmarker);
});

一组坐标:

BMap.Convertor.translate([{lon:lon, lat:lat},...], 1, 5, bdkey, function (points, status, message) {
    if(status) return show(message || "转换坐标出错:"+status, true);
    // 遍历points
});

树莓派利用Privoxy,Shadowsocks,Kcptun做http代理排坑记录

我用树莓派做翻墙网关, 透明网关, 通通绕到坑里出不来了, 方案很多, 思路是树莓派翻, 智能dns, 智能国内外分流, 外加让树莓派成为局域网的网关以便让局域网用户无感翻墙.
黑科技没玩转, 于是我整理思绪, 做一个最简单的局域网http代理服务器吧. 任何网络设备/应用都能找到设http代理的地方, 这样最后只要让树莓派能应用gfwlist的规则.

我们来看一下最终的拓扑:

几点说明:

  1. 如果只是Shadowsocks翻墙, 那么这一步做完就收工了. 最简步骤
  2. 如果需要转成http代理, 你就再多包一层代理工具, Prixovy, Polipo等, 用关键词搜, 比, 自己定. Polipo配置要简单很多
  3. 对, 不考虑做成网关就是这么简单, 但是我还想用kcptun给提提速, 所以多插一脚
  4. 不需要更多了, 再多就又有新坑了.

开始排坑

shadowsocks-libev

我使用的是一键安装脚本, 它会给你默认启动成ss-server, 显然, 你需要的是ss-local, 如果从名字可以看出它们的差别, 当然还有ss-redir, 你要搭建透明代理的话用着得, 还有ss-tunnel, 转发DNS.

lsof -i:8530看看跑8530端口的是哪个程序

我讲解一下怎么把ss-server改成ss-local.

  1. 它是以ss-server -c <configFILE> -f <pidFILE>启动的, 写在了/etc/init.d/shadowsocks 里面, 你把所有ss-server改为ss-local, 然后顺便把-f参数删掉
  2. 改完后, 保存: update-rc.d -f shadowsocks defaults
  3. 重启: /etc/init.d/shadowsocks restart

如果需要连kcptun, 那么这里是第二个需要注意的地方, 即你的服务器地址本应为shadowsocks服务器地址及端口, 这里要改成127.0.0.1:<kcptun端口>

shadowsocks就上述两个需要注意的地方. 如果你使用了python版, go版的, 或是手工安装的libev版, 那么改ss-server这个坑你就碰不到了

kcptun

下载Linux版的kcptun包, 选择名字里含有clientarmv7的文件即可, 这里无其它坑, 选对文件是关键, 配置文件参数与服务端一致即可.

网上有些文章说一定要保持server文件和client文件是同一天的, 我实测没必要. 我的server端都部署了一年了, 今天用的最新的client, 没什么问题

Privoxy

这里坑比较多

  1. 按网上普通的教程, 设置自己的监听端口(默认8118)和shadowsocks的(127.0.0.1:1080), 没问题
  2. 做完上一步, 我用电脑设置树莓派的8118端口为代理服务器, 居然所有流量已经(划重点)被转发了, 而理论上这时是需要你自己添加规则(.action文件)的,否则就是直连.
  3. 找了很多资料, 没人碰到跟我一样的情况, 所以我是绕了一圈, 做了一个不转发所有流量的规则, 然后再在后面跟上我的gfw的规则, 才会选择性地转发. 奇怪
  4. 最后就是网上找一个能转gfwlist规则的方案应用到action里就好, 比如这个

分享一个action文件编写的良好实践, 应用别名:

{{alias}}
direct   = +forward-override{forward .}
socks    = +forward-override{forward-socks5 localhost:8080 .}
httproxy = +forward-override{forward localhost:8000 .}

{direct}
.google.com
.googleusercontent.com
.mozilla.com
【我就是在这里设了一个*.*, direct了所有的流量】

{socks}
.youtube.com
.ytimg.com

{httproxy}
.twitter.com
.blogspot.com
feedproxy.google.com

#总结

  1. 树莓派对外暴露Privoxy8118端口, 转发至shadowsocks1080端口
  2. shadowsocks转发至kcptun1087端口
  3. kcptun clientkcptun server29900通讯
  4. kcptun servershadowsocks server8530通讯

等于把拓扑图口述了一遍~~~我也是想清了这个才最终思路清晰地做完所有事的

最后, 不管是privoxy还是shadowsocks还是kcptun, 都是需要加入自启动的, 你可以选择在/etc/rc.local里面依次写入启动脚本, 也可以在/etc/init.d/里面添加对应的脚本文件

本文不是教程, 是排坑指南. 相应的安装, 配置, 加启动, 可以搜既有教程, 如果碰到跟我一样的问题, 希望帮助到了你.

#题外话 我的目的其实是给我的Apple TV第3代翻墙, 结果发现它居然不能直接设置http代理, 得用Apple Configurator 2来设置, 并推到设备上. 这里有两个选择

  1. 设置全局代理(需要设备Supervised)
  2. 设置某个WiFi热点的代理

我没去研究什么是Supervised了, 而且也希望代理好切换, 于是选择了第二种方案, 即换了wifi后就没代理了(跟在iPhone上设置一样)

TODO:

udp2raw, and udp2raw on mac