BartonTang的博客


  • 首页

  • 关于

  • 归档

  • 标签

Swift下使用Protobuf

发表于 2018-01-01 |

Swift下使用Protobuf

新年伊始,大家元旦快乐!

进入主题:Swift下使用Protobuf,属于扫盲贴,仅此记录过程。

对于protobuf不算太陌生但也不算非常6。之前玩cocos的时候C/S通信协议就是用的protobuf,因为是游戏对于消息的解析速度和大小要求还是比较高的,所以一般稍微大点的游戏都不会采用xml或者json这些可读性很强的序列化格式,之前有个项目组有的是amf3(不要问为啥,因为忒么人家是页游,当年AS3火的一塌糊涂),有个组是自己写二进制序列化和反序列化(默默滴说句:这种活吃力不讨好,看着他们天天对协议感觉好累)。

之后新开项目的时候我推荐大家使用protobuf,开始是在C++项目上使用,之后又在lua项目上使用,那时候用的是protobuf2.5,之后出了3.X的版本不过之后没有用到这一块所以也就不怎么了解,之后如果深入会继续记录下来学习之。

当然很感谢那些年一起的朋友们,大家新年好!

好,先装protobuf,下载链接地址:
https://github.com/google/protobuf/releases

选个版本来自己下载,下载完后解压之。进入到swift-protobuf文件夹下,非常C风格的make,依次执行如下命令:

1
2
3
4
./configure
make
make check
sudo make install

正常执行完后输入protoc –version
得到结果:libprotoc 3.5.1
我装的是3.5.1版本,so安装成功。

我们看下帮助
protoc –help
可以看到现在支持很多语言的导出:

1
2
3
4
5
6
7
8
9
--cpp_out=OUT_DIR Generate C++ header and source.
--csharp_out=OUT_DIR Generate C# source file.
--java_out=OUT_DIR Generate Java source file.
--javanano_out=OUT_DIR Generate Java Nano source file.
--js_out=OUT_DIR Generate JavaScript source.
--objc_out=OUT_DIR Generate Objective C header and source.
--php_out=OUT_DIR Generate PHP source file.
--python_out=OUT_DIR Generate Python source file.
--ruby_out=OUT_DIR Generate Ruby source file.

没有swift,但有Objective-C,but不打算通过构建bridge来互相调用,只想直接导出swift。Apple其实已经帮大家做了个很好的仓库:
https://github.com/apple/swift-protobuf

安装过程也非常简单。git clone仓库下来,cd到swift-protobuf仓库目录下执行

1
swift build -c release -Xswiftc -static-stdlib

成功之后,我们来随便写个proto文件。

1
2
3
4
5
6
7
8
9
syntax = "proto3";
package Im;
message helloworld
{
int32 id = 1; // ID
string str = 2; // str
int32 opt = 3; //optional field
}

OK,现在cd到proto文件所在文件夹,直接运行

1
protoc *.proto --swift_out=/Users/apple/Desktop

这样子在桌面就生成了一个msg.pb.swift文件,接下来xcode new a project,直接丢这个生成好的文件进工程,因为我使用的cocoapod方式来集成framwork,所以直接Podfile文件下加一句
pod ‘SwiftProtobuf’
更新完pod后直接上代码测试。

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
//
// ViewController.swift
// TestProtobuf
//
// Created by apple on 2018/1/1.
// Copyright © 2018年 apple. All rights reserved.
//
import UIKit
import SwiftProtobuf
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
testProtobuf()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func testProtobuf() -> Void {
var obj = Im_helloworld()
obj.id = 21
obj.opt = 12
obj.str = "dkdk"
do {
let protoData: Data = try obj.serializedData()
print("生成的二进制大小为:\(protoData.count) bytes")
let decodedInfo = try Im_helloworld(serializedData: protoData)
print("decodedInfo str = \(decodedInfo.str)")
print("decodedInfo opt = \(decodedInfo.opt)")
print("decodedInfo id = \(decodedInfo.id)")
} catch {
print(error)
}
}
}

获得如下输出:

生成的二进制大小为:10 bytes
decodedInfo str = dkdk
decodedInfo opt = 12
decodedInfo id = 21

坑:
对于直接丢framework到Xcode工程的童鞋,
记住千万不要直接拖到工程目录
记住千万不要直接拖到工程目录
记住千万不要直接拖到工程目录

因为你需要在General – Embedded Binaries中添加编译出的SwiftProtobuf.framework
如果,直接拖拽到工程中使用,会报错:
dyld: Library not loaded: @rpath/SwiftProtobuf.framework/SwiftProtobuf
Referenced from: /var/containers/Bundle/Application/**/ProductsName.app/ProductsName
Reason: image not found

OK,先简单这样。
之后看看golang的导出和简单使用,大概猜到我要干啥了吧~
gRPC,木有错~!!
新一年继续努力吧

参考链接:
链接:https://www.jianshu.com/p/751aa2b621d5
作者:y824165978

beego 获取request body内容

发表于 2017-10-12 |

beego 获取request body内容

beego 获取request body内容

最近自己用beego写了点东西,发现一些坑,主要是获取http的request body的内容。开始一直在controller里面直接使用GetString之类的方法,后来才知道这个并不是raw data。后来翻了下官网发现其实有文档记录的
获取 Request Body 里的内容

最后自己再封装下,上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func (this *BaseController) RequestBody() []byte {
return this.Ctx.Input.RequestBody
}
func (this *BaseController) decodeRawRequestBodyJson() map[string]interface{} {
var mm interface{}
requestBody := make(map[string]interface{})
json.Unmarshal(this.RequestBody(), &mm)
if mm != nil {
var m1 map[string]interface{}
m1 = mm.(map[string]interface{})
for k, v := range m1 {
requestBody[k] = v
}
}
return requestBody
}
func (this *BaseController) JsonData() map[string]interface{} {
return this.decodeRawRequestBodyJson()
}

这个是自己有个父类叫BaseController,全部controller都会继承它,之后在别的controller的地方使用的时候就可以如下:

1
2
3
4
requestBody := this.JsonData()
account := requestBody["account"].(string) //this.GetString("account")
password := requestBody["password"].(string) //this.GetString("password")

之前的简单Get方法就可以不要了~:-D

Api转换为小写丢回给Client

对于Go语言的大小写来作为是否为public标识的设计还是很不错的。但,人生总是充满着坑,作为一个良好的代码设计规范有些肯定是要统一遵守的。so,借用以前团队的规范,现在一般我定义的http的response的json结构如下:

1
2
3
4
5
6
7
{
rc: 0, // 返回的code
msg: "", // 返回的描述
data: { // 返回的数据,可为null
...
}
}

这样子应该是一个良好的设计。那么问题来了,beego里我们定义的Model类的变量肯定是大写开头的,因为你要在别的文件作调用(别聊getter和setter先),what the fuck,直接把某个对象设置为data就很蛋碎,你就会得到类似这样子的结构:

1
2
3
4
5
6
7
8
9
10
11
12
{
"rc": 1,
"msg": "登陆成功",
"data": {
"Id": 5,
"Name": "",
"RegisterTime": 1505806765,
"Vip": false,
"VipStartTime": 0,
"VipEndTime": 0
}
}

全大写这是在逗我呢,好诡异,查了下原来有个办法就是在struct定义的时候给个tag值就好了

1
2
3
4
5
6
7
8
9
10
type User struct {
Id int `orm:"pk;auto" json:"id"`
Name string `json:"name"`
RegisterTime int64 `json:"register_time"`
Phone string `json:"phone"`
Password string `json:"password"`
IsVip bool `json:"vip"`
VipStartTime int64 `json:"vip_start_time"`
VipEndTime int64 `json:"vip_end_time"`
}

这样子得到的response就是如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"rc": 1,
"msg": "登陆成功",
"data": {
"id": 5,
"name": "",
"register_time": 1505806765,
"password": "MjY4MTMyMmNjZjAwOTRiZGJjNTA2MjFjYzcwOTY0YzE1ZjE5ZjE0ZTU5OTY=",
"vip": false,
"vip_start_time": 0,
"vip_end_time": 0
}
}

nice,不错,看起来舒服多了~~~
O(∩_∩)O先记录到这里~

iOS App架构上的一些具体设计

发表于 2017-07-18 |

iOS App架构上的一些具体设计

非大神,只作个人记录用,大家将就着看看,欢迎指正和讨论。

忽略代码的组织形式,忽略构建工具例如:maven,cocoapods,gradle之类的东西,我觉得这些只是我们将代码集成起来构建工程的一个工具,或者换句话说是你打算把代码放哪儿,怎么放;这些内容先不在这个里面做讨论

大方向上用继承,小方向上用扩展

多年前一位老大的经验之谈吧,想想是很有道理的。先我们拉个文件夹叫Core,里面放上类似以下文件结构目录:

1
2
3
4
5
6
├── Base
│   └── BaseExtension
├── Extension
├── Log
├── Net
└── Util

大方向上用继承

Base里是一些类似对UIKit之类的继承,比如BaseViewController在这里面我们可以把一些常用的接口进行声明or定义。

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import Foundation
import UIKit
class BaseViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.addNotifiaction()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.removeNotifiaction()
}
override func viewDidLoad() {
super.viewDidLoad()
self.initData()
self.configUI()
}
func setNavigationBarTitle(_ title: String, titleColor: UIColor, titleFont: UIFont = UIFont.systemFont(ofSize: 17)) -> Void {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 40, height: 30))
label.text = title
label.font = titleFont
label.textColor = titleColor
self.navigationItem.titleView = label
}
/// 隐藏NavigationBar底部的阴影线
func hideNavigationBarShadowLine() -> Void {
let image = UIImage.createImageWithColor(color: UIColor.clear)
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
//消除阴影
self.navigationController?.navigationBar.shadowImage = image
}
/**
设置导航栏的颜色
- parameter color: 颜色
*/
func setNavigationBarColor(_ color: UIColor) {
self.navigationController?.navigationBar.backgroundColor = color
self.navigationController?.navigationBar.barTintColor = color
}
/**
添加返回上级viewcontroller的按键
*/
func addBackItemBtn() {
let leftBarItem = UIBarButtonItem(image: UIImage(named: "ico_back"), style: .plain, target: self, action: #selector(onClickBackItem))
self.navigationItem.leftBarButtonItem = leftBarItem
self.navigationItem.leftBarButtonItem?.tintColor = TextBlackColor//UIColor.gray
}
func onClickBackItem(sender: AnyObject) -> Void {
}
// 初始化数据
func initData() -> Void {
}
// 配置ui
func configUI() -> Void {
self.view.backgroundColor = WhiteColor
configNavigationBar()
}
// 初始化NavigationBar
func configNavigationBar() -> Void {
}
// 关闭viewcontroller界面
func close() -> Void {
}
// 增加Notifiaction监听
func addNotifiaction() -> Void {
}
// 移除Notifiaction监听
func removeNotifiaction() -> Void {
}
}

有人会说这些个空方法没啥意义,确实没啥太大的意义。它们只是改变以后你自己的代码讲一些UI写在哪里,将通知事件写在哪里;好比这个地方,我们的通知事件就已经包装在addNotifiaction和removeNotifiaction两个方法里了,包括正常情况下是在viewcontroller的viewWillAppear方法里去增加事件监听,在viewWillDisappear方法里去移除监听。

再比如configUI以后我就只需要将所有的ViewController继承BaseViewController,那么所有的UI初始化的东西都可以丢到configUI这个方法里去做,以后哪部分代码有问题都容易查找,并不会显得杂乱。

小方向上用扩展

再看另一个地方Base下有个BaseExtension文件夹,这个是干嘛的咧,所谓的小方向用扩展嘛,extension关键字是很有用的,会让你的代码更加的简洁而又不需要继承这么厚重。这里面就是一个简单的应用场景,我们在开发App的时候总有一些提示tips想展示给客户,这种tips一般有通知的形式的,我们可以稍作封装一下就不用每次都去写tips的字体,显示时间等一些代码了。懒,很重要~!

1
2
3
4
5
6
7
8
9
10
11
import Foundation
import Toast_Swift
extension BaseViewController {
func showToast(msg: String, completion: ((_ didTap: Bool) -> Void)?) -> Void {
var style = ToastStyle()
style.cornerRadius = AppCornerRadius
style.messageFont = UIFont.regularFont(size: 15)
self.view.makeToast(msg, duration: 1.3, position: .center, title: nil, image: nil, style: style, completion: completion)
}
}

这样子,在一个ViewController里需要展示通用的tips的时候只需要一句话

1
2
3
self.showToast(msg: msg, completion: { (didTap: Bool) in
self.close()
})

OK~这样子project代码的整洁度包括可读性都会有一个不错的提升,当然有童鞋会喷,你的ViewController里怎么啥都有耦合严重,额,确实需要改改,but先看这些部分吧~~相信这部分总有一些简单而实际的启发。先这样子吧 O(∩_∩)O哈哈~

自定义CocoaPods库制作

发表于 2017-01-18 |

自定义CocoaPods库制作

CocoaPods的安装这里就不赘述了,各位童鞋自行google解决,记得是google而不是百度。

准备一个git project

在自己的git server上先创建一个project。git server可以大家各自选择,github or 自己的git服务器都可以。我选择之前自己用Gogs搭建的一个git server。关于如何搭建自定义git server,请点击
记录Gogs搭建
我这里就把它命名为CoreKit,里面依赖了网络框架库Alamofire, Pod文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
project 'CoreKit.xcodeproj'
# Uncomment this line to define a global platform for your project
platform :ios, '8.0'
target 'CoreKit' do
# Comment this line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for CoreKit
pod 'Alamofire', '3.4.1'
target 'CoreKitTests' do
inherit! :search_paths
# Pods for testing
end
target 'CoreKitUITests' do
inherit! :search_paths
# Pods for testing
end
end

这个地方非常简单。
执行pod install,并且增加一些很简单的测试代码。最终工程目录如下所示:

这里就是个单例,然后public了个test的方法,并且print形参。

编写podspec文件

命令行执行下面的命令来创建podspec文件:

1
pod spec create CoreKit

然后你就能在对应的目录下看到相应的文件了,如下图所示:

使用Sublime text打开CoreKit.podspec文件,

不要用文本编辑器
不要用文本编辑器
不要用文本编辑器

重要的事情说三遍,6666~!

至于podspec里面的参数说明相信我解释不如大家自行谷歌,我直接贴源码吧

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#
# Be sure to run `pod spec lint CoreKit.podspec' to ensure this is a
# valid spec and to remove all comments including this before submitting the spec.
#
# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#
Pod::Spec.new do |s|
# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# These will help people to find your library, and whilst it
# can feel like a chore to fill in it's definitely to your advantage. The
# summary should be tweet-length, and the description more in depth.
#
s.name = "CoreKit"
s.version = "0.0.1"
s.summary = "CoreKit,测试玩玩先"
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = "对项目的具体描述信息,可以比较随意,写一些装逼的话就好了"
s.homepage = "随意,但是我直接用的是项目的git仓库地址"
# s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
# ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Licensing your code is important. See http://choosealicense.com for more info.
# CocoaPods will detect a license file if there is a named LICENSE*
# Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
#
# s.license = "MIT"
s.license = { :type => "MIT", :file => "LICENSE" }
# ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the authors of the library, with email addresses. Email addresses
# of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
# accepts just a name if you'd rather not provide an email address.
#
# Specify a social_media_url where others can refer to, for example a twitter
# profile URL.
#
s.author = { "bartonTang" => "你的邮箱地址" }
# Or just: s.author = "bartonTang"
# s.authors = { "bartonTang" => "472439901@qq.com" }
# s.social_media_url = "http://twitter.com/bartonTang"
# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If this Pod runs only on iOS or OS X, then specify the platform and
# the deployment target. You can optionally include the target after the platform.
#
s.platform = :ios, "8.0"
# s.platform = :ios, "5.0"
# When using multiple platforms
# s.ios.deployment_target = "5.0"
# s.osx.deployment_target = "10.7"
# s.watchos.deployment_target = "2.0"
# s.tvos.deployment_target = "9.0"
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the location from where the source should be retrieved.
# Supports git, hg, bzr, svn and HTTP.
#
s.source = { :git => "你的git仓库地址", :tag => "#{s.version}" }
# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# CocoaPods is smart about how it includes source code. For source files
# giving a folder will include any swift, h, m, mm, c & cpp files.
# For header files it will include any header in the folder.
# Not including the public_header_files will make all headers public.
#
s.source_files = "CoreKit/Source/*.{h, m}" #, "CoreKit/**/*.{h, m, swift}"
# s.exclude_files = "Classes/Exclude"
s.public_header_files = "CoreKit/Source/*.{h, m}"
# ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script. Anything else will be cleaned.
# You can preserve files from being cleaned, please don't preserve
# non-essential files like tests, examples and documentation.
#
# s.resource = "icon.png"
# s.resources = "CoreKit/**/*.{png,xib,storyboard}"
# s.preserve_paths = "FilesToSave", "MoreFilesToSave"
# ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Link your library with frameworks, or libraries. Libraries do not include
# the lib prefix of their name.
#
# s.framework = "Foundation"
# s.frameworks = "SomeFramework", "AnotherFramework"
# s.library = "iconv"
# s.libraries = "iconv", "xml2"
# ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If your library depends on compiler flags you can set them in the xcconfig hash
# where they will only apply to your library. If you depend on other Podspecs
# you can include multiple dependencies to ensure it works.
s.requires_arc = true
# s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
s.dependency 'Alamofire', '3.4.1'
end

OK,上面这里因为涉及到一些个人信息神马的,有些我换成了描述,具体有:

1
2
3
s.homepage = "随意,但是我直接用的是项目的git仓库地址"
s.author = { "bartonTang" => "你的邮箱地址" }
s.source = { :git => "你的git仓库地址", :tag => "#{s.version}" }

上面这些东西大家换成和自己对应的东西就好了。
下面说几个坑:

  1. CocoaPods是根据tag来做管理的,那么那个tag标签就很重要,一定要记得。
  2. s.license 不可缺省,而且最好指定license文件。
  3. s.source_files源文件的路径,是相对podspec文件而言的。
  4. s.frameworks需要用到的frameworks,不用再加.frameworks后缀。

写完podspec文件后对podspec文件进行检查,命令行在当前目录下执行

1
pod lib lint

如果有错的话好好修改下,一般不会有太大问题。成功后会出现类似:
xxx passed validation.

记得提交好你的工程到git server并且记得tag值要和podspec文件里的一致。

在Test工程中引入自定义库

创建一个TestPodsRef工程,并且引入自定义pod库

pod ‘CoreKit’, :git => ‘你的pod库所在的git server地址’

执行pod install。

最终应该都是可以测试成功调用方法的,如果不行,请注意刚才提到的那些坑。
最终运行结果如下图:

OK,回头有空再补充在github上那种,当然这种其实对项目私密保护性更好吧,不过就不开源啦~~~666

App微服务化的第一步就此迈出,吹逼ing~

记录Gogs搭建

发表于 2016-12-23 |

记录Gogs搭建

git很好用,毋庸置疑。一直想在自己的server搭建个git服务器,gitlab很不错,但是一看配置,有点懵逼,一看好长好长的配置步骤,尤其是对我这种linux掌握不多的孩纸来说无疑略困难。直到无聊逛oschina的时候看到Gogs。还是用Go撸的,不错~于是自己搞来试试,过来妥妥滴。
Gogs官网

这里面有很详细的文档,首先在阿里云上

1
2
3
4
wget https://dl.gogs.io/gogs_v0.9.97_linux_amd64.zip
sudo unzip gogs_v0.9.97_linux_amd64.zip
cd gogs_v0.9.97_linux_amd64
./gogs web

据说这样子就大功告成~~!!当然有坑,
1、你需要自己去mysql里创建一个gogs的表
2、你需要一个git用户,默认的gogs是在git用户去启动的
3、在上面那堆命令执行的过程中要注意权限的问题

12…4
Barton

Barton

18 日志
3 标签
GitHub
© 2016 — 2018 Barton