我对技术探索与实践的看法:一个老iOSer的“折腾”手记

强悍_数据
2025-12-16 09:13
阅读 799

大家好,我是阿哲,在杭州摸爬滚打做了6年iOS开发。从Objective-C写到Swift 5,从Xcode 6熬到Xcode 15,见证了Swift从“玩具语言”一步步变成Apple生态的亲儿子。平时除了在公司搬砖(目前在一家靠近网易的中型互联网公司),我也喜欢参加GDevCon、SwiftGG meetup这些技术分享会,跟同行吹吹牛、踩踩坑。

今天想聊聊我对“技术探索”这件事的真实看法——不是那种高大上的方法论,而是我在实际项目里被逼着学新东西、踩坑、修锅、最后勉强上线的血泪史。

被逼上梁山:为什么一个iOS工程师要碰JavaScript?

去年双11前一个月,我们产品突然提了个需求:要在App内嵌一个动态活动页,支持实时配置、快速迭代,最好当天改完当天上线。产品经理原话是:“别再走发版流程了,太慢!”

我一听就头大。我们的主App是纯原生Swift写的,动态化方案一直没落地。领导一拍大腿:“要不试试Hybrid?用WebView加载H5页面,前端同事用JavaScript写逻辑,你们Native负责桥接就行。”

说实话,我心里一万只草泥马奔腾而过。我不是讨厌JavaScript——毕竟大学时也写过jQuery,但那都是十年前的事了。现在JS生态卷成啥样了?Webpack、Vite、React、Vue、TypeScript……我连npm install都怕装错依赖。

但Deadline不等人啊。双11流量高峰就在眼前,老板盯着数据看,测试同学已经在群里@我三次了:“Native这边联调环境好了吗?”

真实项目中的“缝合”实践

我们最终决定采用 WKWebView + JavaScriptBridge 的方案。核心思路很简单:Native提供能力接口(比如获取用户信息、调起支付),H5通过JS调用这些接口,实现业务闭环。

但现实远比想象骨感。

坑1:JS和Swift的数据类型对不上

前端同事传了个 { userId: 12345 },我用 evaluateJavaScript 拿到的是 Any?,强转成字典失败,直接crash。后来才发现,WKWebView返回的其实是 NSNumber,不是 Int

// 别这么写!会崩!
if let dict = result as? [String: Int] {
    let id = dict["userId"] // 💥
}

// 正确姿势:先转成基础类型
if let dict = result as? [String: Any],
   let userId = dict["userId"] as? NSNumber {
    let id = userId.intValue // ✅
}

坑2:JS回调时机混乱

前端调用 native.getUserInfo() 后,需要等Native异步返回结果。一开始我们用 completionHandler 直接回传,结果发现:如果JS连续调用多次,回调顺序会乱,甚至丢失。

后来参考了 WebViewJavascriptBridge 的思路,给每个调用加唯一ID,用字典缓存回调:

private var callbackMap: [String: (Any?) -> Void] = [:]

func handleJSMessage(_ message: [String: Any]) {
    guard let messageId = message["id"] as? String,
          let methodName = message["method"] as? String else { return }
    
    switch methodName {
    case "getUserInfo":
        fetchUserInfo { userInfo in
            self.sendResponse(to: messageId, data: userInfo)
        }
        // 先存起来,等异步完成再触发
        callbackMap[messageId] = { [weak self] data in
            self?.webView.evaluateJavaScript("window.handleResponse('\(messageId)', \(data ?? "null"))")
        }
    default:
        break
    }
}

前端那边也得配合:

// JS侧也要维护一个callback map
const callbacks = {};

function callNative(method, params) {
  const id = Date.now().toString();
  const message = { id, method, params };
  window.webkit.messageHandlers.nativeHandler.postMessage(message);
  
  return new Promise((resolve) => {
    callbacks[id] = resolve;
  });
}

// Native调用这个函数来触发回调
function handleResponse(id, data) {
  if (callbacks[id]) {
    callbacks[id](data);
    delete callbacks[id];
  }
}

这套机制跑通那天,我差点在工位上跳起来——终于不用每次改个文案都要等两周发版了!

技术选型:稳字当头,但不拒绝尝鲜

虽然我爱折腾新技术(私下用SwiftUI写了好几个Side Project),但在公司项目里,我始终信奉一个原则:能用稳定的,就别上实验性的

比如这次Hybrid方案,我们没选React Native或Flutter,就是因为:

  • 团队没有RN维护经验,学习成本高
  • 双11期间不敢赌稳定性
  • H5天然支持热更新,符合“快速迭代”需求

我把几个方案的对比拉了个表:

方案 开发效率 热更新 性能 团队熟悉度 风险
纯原生 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
React Native ⭐⭐⭐⭐ ⭐⭐⭐ 高(无经验)
WebView + JS ⭐⭐⭐ ⭐⭐ ⭐⭐⭐ 中(可控)
Flutter ⭐⭐⭐ 部分 ⭐⭐⭐⭐

最终选了折中方案。事实证明,虽然性能不如原生,但在活动页这种轻交互场景下,用户体验完全可接受。

心得:探索是为了更好地“稳”

现在回头看,这次被迫接触JavaScript的经历,反而让我对跨端协作有了更深理解。以前总觉得“前端=切图仔”,现在知道他们也在处理复杂的异步状态、内存泄漏、兼容性问题——只不过报错信息从 EXC_BAD_ACCESS 变成了 Cannot read property 'xxx' of undefined

更重要的是,我意识到:技术探索不是为了炫技,而是为了解决真实问题。如果你只是为了在简历上写“精通WebAssembly”,却连项目里的Crash率都没降下来,那纯粹是自嗨。

上周五晚上加班修复一个JS Bridge的内存泄漏时(又是闭包retain cycle!),我突然悟了:所谓“资深”,不是你会多少框架,而是你知道在什么场景下该用什么工具,并且能把坑填平。

写在最后

在杭州这座互联网重镇,阿里、网易的技术氛围确实浓厚。但我越来越觉得,比起追新,把现有技术用到极致,才是更稀缺的能力

下次如果你看到我在GDevCon上分享《如何用Swift优雅地调用JavaScript》,别笑——这背后可能是无数个深夜debug的辛酸。

技术探索的路上,我依然会折腾,但会带着镣铐跳舞:业务需求是锁链,线上稳定性是地板,而我的目标,是在这之间跳出最优解。

毕竟,代码能跑,才是硬道理。不是吗?😉

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝