Amon's Blog

猛猛如玉

致妻

乙未年三月六日作

冬末朽木离别寒,

夏初嫩草换新颜。

五月和风起池浪,

桃花丛里嬉KeRan。

  • 旧文新发,原作于 2015 年 3 月 6 日 • 深圳 。

  • 2015 年 2 月 24 日(冬)别离妻女返深,预计 2015 年 5 月 1 日(夏)再回信阳。

  • “KeRan”:小女。

8月份的时候有个需求,需要将 iOS 项目持久化构建,方便将打包上传安装程序的工作从开发人员手中脱离出来(关爱猿类、解救开发)。

需求来了,就简单分析下,实现这种需求需要以下两步:

  1. 自动编译打包,而不再是在 Xcode 中编译打包(xcodebuild, [xctool](https://github.com/facebook/xctool))
  2. 将 ipa 包上传到服务器或者 Fir.im / Pgyer 这种托管网站供下载

除了自己写脚本实现外,也有一些成熟的方案,如 Jenkins

先放出最终 OK 的的方案:

1. 使用 [Jenkins](http://jenkins-ci.org/) 从 Git 上拉取代码
2. 使用 [Fir.im CLI](http://club.fir.im/topic/54f3d26074c4c85e73e4ac14) 提供的打包程序进行打包和上传

说干就干,

  • 安装 Jenkins(具体过程不赘述,网上一票)
  • 打开http://localhost:8080/启动 Jenkins
  • 安装Xcode integration,GIT plugin,Post-Build Script Plug-in,GitHub plugin四个插件(使用 Fir.im 的 CLI 可不需要安装 Xcode integration
  • 新建一个项目,录入基本信息,Git 的地址与分支
  • 增加构建步骤 -> Execute shell:

    fir b 项目路径 -w -S Scheme名称 -p -T Fir.im API Token

  • 保存构建即可

当然这只是百般折腾后的得出的正确路线,因为项目是集成了 Cocoapods,所以遇到不少网上没有的坑。

最开始我在构建步骤里面添加了 Xcode 相关的信息,结果每次构建都会一直卡死在某个进度上。多番折腾下,仍然未果,因为项目进度与考虑到无 OS X 服务器可以用来构建,无奈放弃。

最近比较闲,再个 OS X 服务器也摆那有一阵了,手痒便折腾了一下。

这次依然遇到那个卡死的问题,我尝试了只用 Xcode 构建步骤,也尝试只用 Fir.im CLI,均有各种错误。后来尝试先脱离 Jenkins,直接本地编译。遇到以下几个坑:

我的命令是 fir build_ipa ${WORKSPACE} -S MyApp -o ${WORKSPACE}/build -w -C Release -t MyApp -n myapp

  • 还是卡住,尝试去掉 workspace 路径和 Target,变成fir build_ipa -S MyApp -o ${WORKSPACE}/build -w -C Release -n myapp
  • 运行后不会卡住,但是报了error: couldn't create directory /build: Permission denied. Failed to create temporary outfile '/build/MyApp.app.dSYM.tmp%%%%%%.dwarf': No such file or directory错误,已经好很多了。继续简化步骤,索性直接前往项目目录下,然后使用fir b -w -S MyApp,结果居然 OK 了。
  • 继续在 Jenkins 中折腾,删除 Xcode 的构建步骤,然后在 Execute Shell 中使用fir b 项目路径 -w -S Sheme名称 -p -T token
  • 报 Cocospods Lock File 错误,可以手动pod install一次,也可以将 Lock File 同步,这样就可以避免这个问题。

最终解决!回望整个过程是折腾的, 不过有几点可以记住:

  • 项目 clone 到本地后,先在 Xcode 中编译运行试试,有问题及时解决
  • 在命令行里面试试编译、打包,看是否正常
  • 上面都 OK 再在 Jenkins 这种集成平台中集成

全文完。

2015的夏天走的晚,直到现在夏意仍然未尽,门外有的树叶还显得碧绿,野外田地里油菜和小麦刚长出翠青的苗,屋旁的菜园里的青菜更是盎然生机。

信阳的秋天早就来了,到现在也尽显秋意。杨树叶落得落,黄的黄,在泥土地面上盖住一层层,光秃秃的树枝指向雾蒙蒙的天空。柳树条则光条条的仿佛一根根老妇人枯瘦的手指,即使无风也总是颤颤巍巍着。

冬天来的也早,这次回来的几天,也算是过了几天冬。不刮寒风不下冷雨单是靠着几度的温度把人冻的厉害。走在路上,唯恐不能盖住身上每一寸肌肤。戴上帽子仍觉得冷空气从面颊两侧灌入脑后,直感觉像走在诺森德冰天冻地、漫天飞雪的大陆上,戴着阿尔萨斯的寒冰王冠。

在这冷空气遍布每一个空间的秋天,忽然听到爆竹的响声。在这不年不月的时候,一般乡下是安静的出奇的。能听到的也就是偶有的不明狗叫与妇人的呼喊声。而这时候的爆竹声,无疑是哪个村子的红白喜事。

爸随口问我:杨X村有你姐同学罢?叫什么。

我大概一想,似乎有叫杨X与杨Y的。答:杨X?

爸说:他老头死了,杨元Q。

我还没细问,但见母亲大人那凌厉的眼神。大概不让讨论这不吉利的丧事。此话题便提前结束。

待一个人时,脑里想起这个杨X/Y父亲的离世。杨X的可能性大些,一是他单是我姐的同学,并不同时是我的同学。如果是,我爸会说你同学杨XX的父亲,而不绕路说我姐的。二是杨Y的父亲似乎不叫杨元Q,经过我的仔细回忆应该是叫杨元P。三是我已经幻想了杨X丧失父亲的悲痛模样,实在转换不了把悲伤的杨X变成哀痛的杨Y。这大概是思想的偷懒,或者谓之自欺欺人。

可是不管怎么说,杨X/的父亲是死了的。之前很想知到底是X还是Y,可是后面明白XY都不重要,重要的是一个意气风发的年轻人,在生命最好的时光,突然丧失了父亲。而这个年轻人,恰好是我姐的同学,同时是一个地方的,所以听到了他家的葬礼爆竹声。重要的是一个老骥伏枥的中年人,一辈子闯南走北,把儿女养大,正准备歇下来享受儿孙绕膝之乐,突然的就去了。所以我刻意的不去想是X还是Y,害怕得到它的答案。

杨XY是学习很好的实力派,那时与我这种天赋派成为对比。我们觉得他们是愚不可及,用生命在学习。他们觉得我们是投机取巧,不可长久。但彼此都对对方有敬畏之心。

而他的父亲,我未曾谋面。他可能是一个结实的庄稼汉,也可能是在外谋生的一个老板。年轻的时候挑水库,挖水塘,掘水坝。后来种田地,犁田耙田,撒种子,插秧,割谷,挑草头,堆草垛,打谷。忙碌的身影春夏秋冬不断的闪现在田野上。流出的汗水洒遍每一条小路上滴进他家的土地里。后来慢慢的村子里时兴出外打工。有去北京收废品的,有去南方进厂的,有去江浙拉货的,也有去大西北磨板子的。他也步入了这一洪流,在外地打工。一年只有农忙时和过年才会扛着大包背着小包拎着两包拖着一化肥袋,满载着回家的期盼挤上洪水般的人流火车。包里有给孩子买的新衣服,玩具,捡来的新奇玩意,外面老板不要的给他的旧衣服和旧玩具,买的吃的,外面见过的家里没见过的水果等。塞的满满当当,鼓鼓囊囊。回家后,看到年幼的孩子,消瘦的妻子。大半年在外的劳累,都付诸咧嘴一笑。1990,1995,2000,2005,2010,2015…二十几年转瞬即逝,曾经呆的城市天翻地覆,刚来落脚的工棚也早已建起了高楼大厦。老家也变化很大,家家盖起了两层小洋楼,买了洋车代步,村子前都修起了水泥路。而那个年幼的孩子也已变成大姑娘小伙子,就像昨天的自己一样。就连当初梦寐以求才娶到的青春靓丽的妻子,也体态渐渐臃肿,乌黑的长发渐渐被岁月染上点白色,水灵灵的皮肤如今呈黄黑色。

2015,人生大概第十个五年。这一年,是五十年来再普通不过的一年,相比十岁时的在贫苦中作乐,二十岁的在清苦中劳累,三十岁拖着家里家外中奋力直前,四十岁子女初长成、家业稳健,五十岁实在是平淡无聊的一年。这前后,儿子工作几年,事业初成,娶妻生子,一切都显得顺其自然。老婆给儿子照看着孙子,自己也想着回到老家了,等把这两年的账清点完了就打道回府,开个小百货店或者卖个水果,没事就去村子后面转转,看看当年挖的水库、修的路,栽过的树。看看20岁时的自己的那个世界,与50岁自己的这个世界,进行着激烈的时空交错,而迸发出的火花,都是这几十年的记忆碎片的光与影。

这一年的11月,夏秋冬三个季节联奏着准备送完最后的2015。在这个人们都不想外出只想守着老婆孩子热炕头的时节,他突然走了,没有一句叮咛嘱咐,突然潇洒的去了。杨X/Y从遥远的城市接来家里的电话,像往常一样:

喂 妈

X/Y啊,你快回来,你爸……

我爸怎么了

你爸走了……

……

买票,坐车,离开这个他熟知的世界,这个他还有个遥远的爹的世界,这个丝毫感受不到悲伤的爹,去往另一个世界,阔别已久的旧世界,那个一直有爹的世界。像从热被窝里掉入了冰窟窿里,两个世界。

回到家,这个秋天,真冷。大概再不会有这样冷的秋天罢。熟悉的老房子前泛起了久违的热闹,忙碌的身影穿梭于屋前屋后,他想着父亲已经撒手西去,再看着眼前这幅景象,觉得很荒诞,他嘴角咧了起来,他想笑,亦想哭,哭哭笑笑。到家后他急寻着母亲,和父亲,见到母亲后便被带着去入棺的父亲。深红色的棺木里静静的躺着他熟悉的父亲,身上花绿的寿衣像参加一场盛大的节日。

阴雨连绵几天,浑浑噩噩的几天,事情总算办妥。终于要返程,回到那个乍离的世界,这一去一回,父亲也没有了罢。再去的世界,和几天前的世界,是否是一个世界。或许那个世界,根本感受不到他的一丝悲伤。被窝已不是被窝,冰窟窿还是冰窟窿。临别前,看着从未如此瘦弱的母亲,他心如刀割血直流,恨欲从此烂家里,任它雨打风吹去。

临行前,杨X/Y最后一次去后院田地上的坟前,冷风刺骨。遍布丘壑的田野有的光秃秃,有的种上了小麦和油菜,有的只是野草,有的还是上次的播收后未经翻土处理的田地。新坟孤伶伶的躺在那里,听着秋风歌,看着雾蒙蒙的天空和暗黄的田野、零星的村落,淋着秋雨,吹着秋风。

他走了,带着身上残留的秋意。远处的坟边的田野上,枯黄的叶草凋落着,而泥土下面的草根,正等待着明年的春天。他想着,笑了。

接手了他的项目后,

心里总会感觉怪怪的,

因为他前阵子过世了,

因为过劳。


现在我每天都会很忙,

有时候,

读到他写的代码,

思考他的逻辑。

思考一个死去人的逻辑,

揣摩一个死去人的想法,

这种感觉真的很奇怪,

就像深夜在深山老林的山路缓行:幽冷而又孤独。

就像触摸一个死人的身体:冰凉屏气。


每当这个时候,

脑子里不自觉的想起他,

他和我以前一起上下班,

一起喝酒,

一起讨论项目问题,

每写一会代码,

都要想起他。

甚至想起他临死前的情形,

虽然我并没有见识过,

可是它却像真实发生的一样:


在一个深夜,

这夜天很黑,

无星无月,

风呜呜吹。

他一个人挑灯在公司奋战,

大楼里早已人去楼空,

走廊里的灯也黑不见物。

同事们早已下班,

他只打开他头上那个灯,

昏暗的灯正好只照亮了他周围的一块。


他在写代码,

是的,

此时无关这个世界,

他脑中只有项目的模块还有哪些没完成,

他手下只有 26 个字母 10 个数字和符号,

他心里只有赶紧弄最晚 2 点要回家不然明天没精神继续 coding 了。


键盘嚓嚓,

他觉得有点口渴,

时间是 9:41,

他脑子终于下达命令让自己拿水杯喝水,

时间是 10:24。


键盘嚓嚓,

他手机来了推送信息,

时间是 10:30,

他拿起手机看了一眼,

是他定的睡眠闹钟提醒。

时间是 10:38。

他规定自己 10:30 要睡觉,

尽管他从来没有遵守过,

但他根本不打算删掉这个闹钟或者调整时间,

因为他相信总有一天他会严格遵守它。


键盘嚓嚓,

他突然想起了隔壁住的女孩,

头发很长,

笑起来很美。

时间是 10:47。

他继续写着,

这个模块快写完了,

这段代码要不要优化一下,

优化吧,明显看起来不优雅,

优化个啥啊,时间不多了得赶紧写完回家。

优雅?像她那样?

又想起了隔壁那个优雅的女孩。

前天早上出门的时候正好碰见她出门,

她看了他一眼,

持续 0.4 秒。

想到这里,

他的嘴角弯了。

时间是 10:54。


再定神时,

他责备自己不该写代码总跑神。*(Stinger: 我定神再写时,已是 17 个月后的 2015 年 12 月) *

沉气写完一段逻辑后,

他边打上分号边低头扫了一眼显示器右下角,

时间是 10:58。


整理一下思绪,

他吁了一口气,

竟感觉有些饿了。

是的,他夜晚在楼下快餐厅匆匆吃完便上来工作了,

连打的紫菜汤都未来及喝上几口。

他本想坐在窗前趁着吃饭时看看夜晚的城市,

他得有半个月没有好好吃饭了,

没有好好的看看白天的城市,

看看夜晚的城市,

看看行走的人群。

仔细想想上一次好好吃顿饭是上个月,也可能是上上个月时去同学家里蹭饭。

毕业时一起十来人说好要在这个城市当主人,

如今回老家、换城市只剩下他和另外两个同学。

自己勉强留下来,

却也战战兢兢地呆着。


同学的一家子在一起很热闹,

有家的气氛。

家,他的家,

在一千两百公里外。

两站路外的那个一室一厅,

是这个城市属于他的地方之一。

另一个之一是现在头顶灯光刚好照的位置。

尽管认真说来这些并不属于他。


操,他小声的骂自己。

又跑神了。

他不总是这样容易跑神。

白天上班时,办公室熙熙攘攘,

他总是专注的写代码,

即使偶尔跑神也很快被同事们的交谈声和办公室此起彼伏的手机通知声和键盘声打断。

今天这么安静,

他倒是很容易走神了。


键盘嚓嚓,

他迫不及待的想给这个模块收尾了。

这个模块写了三四天了,

像这样的模块,

还有几个。

他喜欢这种有事做的感觉,

这种被需要的感觉。

他喜欢开发一些新功能,

像打一场硬仗。

搭好架构、定义好接口,

就像排兵布阵、运筹帷幄,

具体的实现时就像硬弓硬马、真刀真枪,

策马平川、气吞三军,

完成一个模块犹如征服一块领地,踏平一座山。

他喜欢这种编程像打仗、血战沙场、戎马生涯的感觉,

每每兴致所至,无不有一种提刀上马的冲动。

如果生活也只有铁与血这么简单就好了。

一股热血涌上胸腔,

他不由得由倚在椅子上挣扎着坐正,

时间是 11:19,外面风声正劲。


*未完待续...*
  • 老文新发,原文于 2014 年 7 月 24 看到一帖《 假如一个码农突然出意外去世了,你们觉得接手他写过的代码是什么心情?》后有感而发。
  • 2015 年 12 月 16 日 续作。

说白了,人生就是一场马拉松。
起跑的时候你跑的快了,爽了,舒服了。
跑了一会摔了一跤,被别人赶上了,然后你就开始自怨自艾,觉得这个比赛就完了,人生就没有希望了。
在你不断的痛苦与抱怨时,比赛也在一点点的进行。别人都在努力跑,而你还停留在抱怨之前的时候摔那一跤。你还在落后,你还在抱怨,觉得自己之所以落后是输在摔那一跤上。
你失误了从前,又忽略了现在,更放弃了未来。这场比赛,你还想玩么?

我们不能用微观的眼光去看这种事情,应该站在宏观的角度上。现在苦点累点,等这一段时间过去了,它们都将成为记忆中走过的一条湿漉漉的小路。虽然走时泥泞不堪,当你走完再回头看时,尽是阳光灿烂。

李:你这么能说,为什么不去做销售。
余:不去。
李:为啥?
余:我去了,那些销售人员就要失业了
李:[挖鼻孔]
余:我想把这个世界上最聪明的脑袋留在工地,建造出一栋栋摩天大楼。
李:你装逼。
余:装逼只是我的一个副业。
李:主业是啥?
余:搬砖工。
李:去屎。
余:不要把我想的多么高大上,其实我只是路边一个小小搬砖工。
李:其实我一直认为搬砖是一个高端的职业。
余:搬砖没有高低贵贱,搬砖只是搬砖。
李:工资来的快拿的高。
余:搬砖不为工资,搬砖只求内心的平静。
李:累岔气了,还能平静。
余:越累越平静。
余:不管累还是舒坦,不管搬砖还是指点江山,永远追寻内心的平静,这才是人生真正的奥义。
李:我曾经认为X是我精神世界的挚友,后来遇到你,我发现我错了。X只是一个能和我一起挖臭缠子的人,唉。
余:红玫瑰与白玫瑰嘛。

本文译自Breaking down iOS 9 Universal Links, 侵立删

在几周前的 WWDC 上, Apple 为 iOS 9 宣布了一个所谓通用链接的深层链接特性, 地址为 无缝链接到您的 App.虽然它不是一个必须实现的功能, 但还是需要引起一些注意.

在网上有太多让人迷惑和错误的信息, 然后这次 WWDC 本身也没有去描述细节.幸运的是, 在 HOKO 我们在我们的智能链接上添加了这一特性, 所以我们可以无缝的引导用户到 APP 上.

什么是通用链接?

很显然 Apple 正在大力推动 APP 开发者在深层链接上有更好的体验.所有的消息都围绕着深层链接技术.与此同时, Apple 推出通用链接:一种能够方便的通过传统 HTTP 链接来启动 APP, 使用相同的网址打开网站和 APP.

通过唯一的网址, 就可以链接一个特定的视图到你的 APP 里面, 不需要特别的 schema .试想一下 Twitter 使用了通用链接, 那么你每在 twitter.com 点击一个链接, 你的 iOS 设备都会在 Twitter 里面自动打开这个页面, 而不是当你没有安装时转到普通的网页.这个用户体验是顺畅的, 最重要的是用户不会失去上下文(跳到 APP 后 Safari 上不再留下空标签).

准备使用通用链接

实现通用链接不难, 但首先必须遵守一些先决条件.如下:

  • 有一个注册的域名
  • 通过 SSL 访问域名
  • 支持上传一个 JSON 文件到你的域名
  • 至少 iOS 9 beta 2 版本 (下载) , —— 这很重要, 因为如果是之前的测试版本你需要做额外的操作.
  • 至少 Xcode 7 beta 2 (下载)

如果你都有了, 那就来按照下面3个步骤来做吧.

1.添加域名到 Capabilities

首先, 你必须在 Xcode 的 capabilities 里 添加你的 APP 域名, 必须用 applinks: 前置它:还添加一些你可能拥有的子域和扩展(www.domain.com, news.domain.com 等等).

img

—— 用 applinks: 前缀添加所有域名, 同时不要忘了包含所有可能需要的子域名

这将使你的 APP 从你的域名请求一个特殊的 JSON 文件 apple-app-site-association.** 当你第一次启动 APP ** 它会从 https://domain.com/apple-app-site-association 下载这个文件.跳到下个步骤来了解如何构建这个文件.

2.上传 apple-app-site-association 文件

该文件必须存在且为了安全原因可使用 SSL 通过 GET 请求访问到.你可以打开一个文本编辑器然后写一个这样的简单 JSON 格式:

1
2
3
4
5
6
7
8
9
10
{
"applinks": {
"apps": [],
"details": {
"TBEJCS6FFP.com.domain.App": {
"paths":[ "*" ]
}
}
}
}

根据 paths 键设定一个允许的路径列表(你想你的 APP 作出反应的路径), 或只是一个星号如果你想打开 APP 而不管路径是什么.

你可能想知道 TBEJCS6FFP.com.domain.App 从何而来, 基本上, 它是加入了你的团队标识的 bundle id.你可以从你的 苹果开发账号页面 获取你的团队标识:
img

—— 这个页面有你的团队标识, 你可以拷贝粘贴到 apple-app-site-association 文件

Bundle id 可以在项目的 target —— General 中找到:
img
—— 检查 General 标签并拷贝粘贴 bundle id 到 apple-app-site-association 文件

最后, 上传这个文件到你的域名根目录.如果你打开 https://domain.com/apple-app-site-association 可以看到你的文件, 那么你就可以继续下一步了.

3.在 APP 里处理通用链接

为了在 APP 里支持通用链接, 你需要在 AppDelegate 里实现 application(_:continueUserActivity:restorationHandler:). 尽管这种方法可以用于许多不同的目的(比如 Handoff搜索 API), 我们将只关注如何处理接收到的通用链接.

如果提供的 userActivityNSUserActivityTypeBrowsingWeb 类型, 那么意味着它已经由通用链接 API 代理.这样的话, 它保证用户打开的 URL 将有一个非空的 webpageURL 属性.依据前面的例子, 这将是 http://domain.com/path/to/thezoo http://domain.com/path/to/thezoo 体现.

为了确保你的 APP 可以翻译 URL 成实际的内容, 你需要做下面几步:

  • 使用 NSURLComponents 简单解析webpageURL 到 host(如domain.com), 路径组成同理(如["/"], "path", "to", "thezoo").
  • 确保能识别 host.
  • 尝试将 pathComponents 匹配到 APP 的已知内容里.
  • 验证该内容实际上可以被呈现.
  • 呈现内容给用户.

如果上述步骤有任何一个失败, Apple 建议你的 APP 应该在 Safari 上打开 webpageURL 以显示错误.

总结如图:

img

—— 上图为通用链接的工作流程提供一个摘要

通用链接的缺点

通用链接对于开发人员来说是一个好主意, 但也有一些缺点可能会导致不受青睐:

通用链接只适用于 iOS 9 +

配置 APP 支持通用链接意味着只有运行 iOS 9 的用户才能享用这一技术优势. 而之前版本的用户将不能在点击网页链接时打开 APP. 相反的, 他们将返回到浏览器和网页, 就像之前正常的网页链接一样.

然后, HOKO为 iOS 5 及更高的用户提供了移动深层链接. 因此, 你的移动深层链接将可以运行在几乎所有的 iOS 设备上, 不管它们是不是 iOS 9.

通用链接总是会返回到先前创建的网页

如果你想退回主页或与 APP 完全不不相关的网站?实现这一目标需要一些额外的工作, 要配置一个网页可以指引用户到你的目标页面. 此外, 如果你没有一个网站的话, 这将会是一个不可能的解决方案.

你可以使用 HOKO 智能链接和他们自适应的返回来轻松的解决这个问题. 对于创建的每个智能链接, 你可以在每个平台上选择如果没有安装 APP 会发生什么. 另外你可以设置返回到你的网站, iTunes 商店页面或其它外部网站.

使用通用链接, 开发者必须部署一个网站来关联 APP

这对于小型开发者来说可能是个坏消息, 他们可能买不起或维护一个网站, 但是仍然想通过网页链接进入他们的 APP.

HOKO 可以解决这个问题因为它来充当开发者的网站, 每个 APP 托管在不同的子域. 因此, 开发者只需创建智能链接, 并发布其网址, 然后每次都会无缝的打开相应的 APP.

APP 与网站之间的联系是由创建和托管在开发者网站上的配置文件搞定的

使用 HOKO, 你可以跳过这些繁的配置, 因为我们使它可以开箱即用. 此外, 我们的服务器运行在有行业顶级标准的安全性和性能上, 为每个设备提供安全和快速的方式.

如果你想了解更多关于 HOKO 的信息, 可以给我们发邮件或直接注册.

(或直接检查我们关于Google’s App Links的详细指南)

ReadMe

为了方便第三方开发者快速简单的集成国内三大社交平台(微信,QQ,微博),故造此轮子。

目前支持微信(微信好友,微信朋友圈),QQ,QQ空间,新浪微博)

关于此模块的Bug反馈、建议、口水等等,请大家狠拍并提交到Github上,我会尽快解决。

该模块会继续完善优化,争取为大家提供一个更快速、更简单、更规范、扩展性更好的集成模块。

===========
图片

特点:

  • 快速
  • 简单
  • 易集成
  • 支持网页/SSO
  • 扩展性好
  • 可定制

===

更新 Log

* 2015-09-29 修复网页微博分享时内容没有传入的 Bug
* 2015-10-29 适配 iOS 9 下分享
* 2015-11-04 新增三大平台分享后的回调
* 2016-01-11 新增适配 iOS 9 SSO 的 `LSApplicationQueriesSchemes`配置说明

集成步骤

1. 下载微信、QQ、微博的社交sdk并导入至项目中

  • 可在本页下方参考资料中取得

2. 导入它们所需的系统framework与库

程序 —— Targets —— Build Phases —— Link Binary With Libraries

Security.framework, SystemConfiguration.framework, CoreGraphics.framework, CoreTelephony.framework, QuartzCore.framework, ImageIO.framework, CoreText.framework, UIKit.framework, Foundation.framework, libsqlite3.dylib, libc++.dylib, libz.dylib, libstdc++.dylib, libiconv.dylib

  • (具体可参考本页底部参考资料链接)

3. 设置编译选项

程序 —— Targets —— Build Settings —— Linking —— Other Linker Flag 添加 -ObjC

4. 设置URL Schema

  1. 程序 —— Targets —— Info —— URL Types

分别添加微信,QQ,微博

tencentopenapi : tencent222222

mqqapi : QQ0605C97A

weibo : wb2045436852

weixin : wxd930ea5d5a258f4f

  • 以上均为测试app key,具体可以去对应的开放平台注册

  • mqqapi 是 tencent 的 app key 转十六进制,不足八位在前面补 0 的结果


  1. 在 Info.plist 中添加LSApplicationQueriesSchemes项,
    分别添加社交平台的几个白名单:
    mqq
    mqqopensdkapiV2
    mqqapi
    weibosdk2.5
    weibosdk
    weibo
    weixin
    wechat

具体列表请至获取iOS 9 应用跳转适配

5. 导入XMShareView至项目中

XMShareView结构介绍:
名称 解释
CommonMarco.h 通用宏文件,包含APP Key等宏
ExtView/ 扩展View,VerticalUIButton为图片文件垂直对齐按钮
Resource/ 图片等资源,包含微信、QQ、微博图标
ShareUtil/ 分享工具类
XMShareView.h 分享显示视图

6. 在AppDelegate.m中注册

  1. 导入文件头
1
2
3
#import "WXApi.h"
#import <TencentOpenAPI/TencentOAuth.h>
#import "WeiboSDK.h"
  1. application -> didFinishLaunchingWithOptions 方法中注册
1
2
3
[WXApi registerApp:APP_KEY_WEIXIN];
[WeiboSDK enableDebugMode:YES];
[WeiboSDK registerApp:APP_KEY_WEIBO];

7. 在ViewController.m 中引用

  • 导入文件头

    1
    #import "XMShareView.h"
  • 添加一个属性

1
@property (nonatomic, strong) XMShareView *shareView;
  • 在点击分享的方法添加如下代码:
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
if(!self.shareView){

self.shareView = [[XMShareView alloc] initWithFrame:self.view.bounds];

self.shareView.alpha = 0.0;

self.shareView.shareTitle = NSLocalizedString(@"分享标题", nil);

self.shareView.shareText = NSLocalizedString(@"分享内容", nil);

self.shareView.shareUrl = @"http://xumeng.github.com";

[self.view addSubview:self.shareView];

[UIView animateWithDuration:1 animations:^{
self.shareView.alpha = 1.0;
}];


}else{
[UIView animateWithDuration:1 animations:^{
self.shareView.alpha = 1.0;
}];

}

8. 处理 URL 跳转

AppDelegate 类中实现两个处理 URL 跳转的方法。

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
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
if ([[url absoluteString] hasPrefix:@"tencent"]) {

// return [TencentOAuth HandleOpenURL:url];
return [QQApiInterface handleOpenURL:url delegate:self];

}else if([[url absoluteString] hasPrefix:@"wb"]) {

return [WeiboSDK handleOpenURL:url delegate:self];

}else if([[url absoluteString] hasPrefix:@"wx"]) {

// 处理微信回调需要在具体的 ViewController 中处理。
ViewController *vc = (ViewController *)self.window.rootViewController;
return [WXApi handleOpenURL:url delegate:vc];

}

return NO;
}

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
if ([[url absoluteString] hasPrefix:@"tencent"]) {

return [TencentOAuth HandleOpenURL:url];

}else if([[url absoluteString] hasPrefix:@"wb"]) {

return [WeiboSDK handleOpenURL:url delegate:self];

}else{

ViewController *vc = (ViewController *)self.window.rootViewController;
return [WXApi handleOpenURL:url delegate:vc];

}
}

9. 处理分享后的回调

特别提醒:(下方方法体内是示例代码,具体业务逻辑请具体实现)

  • 微信分享:在具体的调用 ViewController 类里面实现微信 WXApiDelegate 协议,然后实现以下方法:
1
2
3
4
5
6
7
8
9
10
- (void)onResp:(BaseResp *)resp
{
NSString *message;
if(resp.errCode == 0) {
message = @"分享成功";
}else{
message = @"分享失败";
}
showAlert(message);
}
  • QQ 分享:在 AppDelegate 类中实现 QQApiInterfaceDelegate 协议,然后实现以下方法:
1
2
3
4
5
6
7
8
9
10
- (void)onResp:(QQBaseResp *)resp
{
NSString *message;
if([resp.result integerValue] == 0) {
message = @"分享成功";
}else{
message = @"分享失败";
}
showAlert(message);
}
  • 微博分享:在 AppDelegate 类中实现 WeiboSDKDelegate 协议,然后实现以下方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)didReceiveWeiboResponse:(WBBaseResponse *)response
{
NSString *message;
switch (response.statusCode) {
case WeiboSDKResponseStatusCodeSuccess:
message = @"分享成功";
break;
case WeiboSDKResponseStatusCodeUserCancel:
message = @"取消分享";
break;
case WeiboSDKResponseStatusCodeSentFail:
message = @"分享失败";
break;
default:
message = @"分享失败";
break;
}
showAlert(message);
}

参考资料

QQ iOS SDK 环境搭建

新浪微博 iOS SDK 环境搭建

微信 iOS SDK 环境搭建

iOS 9 适配 SSO

0%