起因:那条线什么都说明不了
去年冬天做完一次 citywalk,打开手机看轨迹——就一条线。
看了两秒关掉了。那条线能说明「我去过」,仅此而已。我走了哪些新街道?绕进去的没名字小巷算不算探索?穿过老居民区,到底覆盖了多少地方?没有答案。现有的 App 给的只有步数和卡路里,好像出门的目的是减肥而不是探索。
于是我开始做 雁过留痕:一个把 GPS 轨迹换算成面积(km²)的 App。不是轨迹线,是你真正踩过的土地面积。以 25 米为精度的网格,每个格子踩到才染色,踩过永久累积。
这篇记录 iOS 版开发过程中踩的几个坑,都是重写之后才想明白的事。
网格算法:逻辑三天,返工三次
把 GPS 轨迹转成面积,思路上不复杂:用 25 米网格切地图,记录轨迹经过的格子,统计数量乘以单格面积。
第一版就这么做的。跑起来发现:用户站在原地不动,面积还在涨。
GPS 在后台会漂移,静止状态坐标抖动半径能到十几米,平白多染了一堆格子。加了平滑滤波之后漂移压住了,但 CPU 开销上去了,后台跑一小时电量掉得很难看。用户哪怕喜欢这功能,手机烫成暖宝宝也会卸载。
删掉重写。第三个方案换了思路:后台只管采集 GPS 点,不做任何实时计算。把连续轨迹切成一段一段的 TraceSegment,每段至少需要 8 个有效点才算有效轨迹,不足的直接丢弃。用户走完回来,再批量处理网格覆盖、统计面积。
核心判断逻辑大概是这样:
func isValidSegment(_ points: [CLLocation]) -> Bool {guard points.count >= 8 else { return false }let distance = zip(points, points.dropFirst()).map { $0.distance(from: $1) }.reduce(0, +)return distance > 10.0 // 过滤掉原地抖动产生的「伪轨迹」
}
这个 8 是跑出来的,不是拍脑袋定的。试过 5、10、12,在步行上班、公园散步、商场逛街几个场景里来回测:5 太松,噪声进来一堆;12 又会把十分钟内的短途全过滤掉——用户出去买个早餐发现没有记录,体验很差。8 是能平衡两端的数字。
坐标系:国内有个绕不开的坑
做「省份解锁」功能时踩了个坑——国内手机 GPS 用的是 GCJ-02 偏移坐标系,跟标准 WGS-84 有几百米的位移。不做适配的话省份边界判断就乱了。
实测的时候我站在长安街,App 判断我在朝阳区。坐标没出北京,人已经「跨区」了。这要是上线,用户肯定以为遇到了灵异事件。
花了几天把坐标转换适配进去,这是做给国内用户用的东西应该有的基本功。
还有个细节:省份解锁的面积阈值必须做成动态的。面积越小的省阈值越低,不然上海、北京这种小省市会比新疆难解锁——住了十年的上海用户,反而比在新疆路过一次更难点亮,这逻辑说不通。
Widget 的一个小细节
Widget 和主 App 数据用 App Groups 共享本地缓存,标准方案没什么问题。但有个边界情况:用户走完回来,主 App 还在批量算面积,Widget 这时读到的是旧数据,进度比实际少——用户会以为 App 有 bug。
我加了一个写入时间戳:Widget 如果发现缓存超过 15 分钟没更新,显示「正在同步」的提示状态,而不是直接展示一个旧数字。这个细节没人会注意到,除非它不在的时候。
现在的状态
上线到现在,下载量还不到 200。不确定是触达方式的问题,还是「探索面积」这个概念本身还没找到对的人群。
成就系统做了四条赛道:探索轨迹、坚持打卡、华夏版图、环球足迹。做完之后有点可惜——用户量太小,稀有徽章基本没人能点亮。精心摆了一桌,宾客还没到。
我在想,「你的城市你探索了几 %」这种数字是不是天然有攀比欲?加个朋友之间的面积排行,能不能让更多人愿意分享截图?还是说这套玩法本来就是小众需求,不会破圈?不知道,先做着。
下一篇打算写 iOS 后台 GPS 保活,比这篇惨多了。iOS 有个机制会在你不注意的时候把后台进程干掉,我为了让 App 活着研究了一套「骗」系统的方案,那段才是真正让我抓狂的部分。
感兴趣的可以先下载体验:App Store - 雁过留痕
