You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS端React Native NFC会话自动关闭:如何保持会话持续活跃?

问题:iOS下react-native-nfc-manager会话自动关闭,需保持持续活跃

在iOS平台使用react-native-nfc-manager读取NFC标签时,每次发现标签均可正常读取,但约1分钟后NFC会话会自动关闭,应用停止读取标签,需手动重启会话才能恢复。需求是按需保持会话持续活跃。

现有代码

import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
import React, { useEffect, useState } from "react";
import NfcManager, {
  Ndef,
  NfcAdapter,
  NfcEvents,
  NfcTech,
} from "react-native-nfc-manager";
import AppButton from "@/components/AppButton";
import Animated, { SlideInLeft } from "react-native-reanimated";

NfcManager.start({
  onSessionClosedIOS: () => {
    console.log("ios session closed");
  },
})
  .then((result) => {
    console.log("start OK", result);
  })
  .catch((error) => {
    console.warn("device does not support nfc!");
  });

const Item = ({ title }) => {
  return (
    <Animated.View
      entering={SlideInLeft.duration(300)}
      style={{
        backgroundColor: "lightgreen",
        marginVertical: 4,
        padding: 12,
        borderRadius: 2,
        borderLeftWidth: 4,
      }}
    >
      <Animated.Text
        entering={SlideInLeft.duration(100)}
        style={{ fontSize: 16 }}
      >
        {title}
      </Animated.Text>
    </Animated.View>
  );
};

const Page = () => {
  const [tags, setTags] = useState([]);
  const [nfcState, setNfcState] = useState(null);
  const [nfcSupport, setNfcSupport] = useState(null);
  console.log(tags);

  const processNfcTag = (tag) => {
    if (tag.ndefMessage) {
      const ndefMessage = tag.ndefMessage[0]; // Assuming only one NDEF record

      const text = Ndef.text.decodePayload(ndefMessage.payload);
      // Use the decoded text
      console.log("Decoded Text:", text);
      setTags((pre) => {
        return [...pre, { id: pre.length, tag: text }];
      });
    } else {
    }
  };

  useEffect(() => {
    NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag) => {
      console.log("tag found", tag.ndefMessage);
      processNfcTag(tag);
    });

    return () => {
      NfcManager.setEventListener(NfcEvents.DiscoverTag, null);
    };
  }, []);

  useEffect(() => {
    const checkNfcState = async () => {
      const _nfcSupport = await NfcManager.isSupported();
      setNfcSupport(_nfcSupport);
      const _nfcState = await NfcManager.isEnabled();
      setNfcState(_nfcState);
      console.log(_nfcState, "nfcState");
      console.log(_nfcSupport, "nfcSupport");
    };
    checkNfcState();
  }, []);

  const readTag = async () => {
    try {
      const isActive = await NfcManager.isSessionAvailableIOS();
      console.log(isActive, "is session available");
      if (isActive) {
        return;
      }
      await NfcManager.registerTagEvent({
        invalidateAfterFirstRead: false,
        isReaderModeEnabled: true,
        readerModeFlags:
          NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
      });
    } catch (error) {
      console.log(error);
    }
  };

  if (nfcSupport === null || nfcState === null) {
    return;
  } else if (!nfcSupport) {
    return (
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <Text> Your device does not support NFC</Text>
      </View>
    );
  } else if (!nfcState) {
    return (
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <Text> NFC is off</Text>
      </View>
    );
  }

  return (
    <View style={{ padding: 16, gap: 12 }}>
      <AppButton
        style={{
          backgroundColor: "orange",
          alignSelf: "center",
          width: 100,
        }}
        title="Start"
        textColor="#111"
        onPress={readTag}
      />
      <FlatList
        data={tags}
        keyExtractor={(item) => item.id + "_key"}
        renderItem={({ item }) => <Item title={item?.tag} />}
      />
    </View>
  );
};

export default Page;

const styles = StyleSheet.create({});

解决方案

1. 利用iOS会话关闭事件自动重启会话

iOS系统默认NFC会话超时时间为60秒,超时后会触发onSessionClosedIOS回调。我们可以在这个回调里重新启动NFC会话,实现自动续期。

2. 优化会话注册参数

在registerTagEvent中设置sessionTimeoutIOS为0(表示无超时限制),同时保留invalidateAfterFirstRead: false确保读取多个标签后会话不关闭。

修改后的代码示例

import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
import React, { useEffect, useState } from "react";
import NfcManager, {
  Ndef,
  NfcAdapter,
  NfcEvents,
  NfcTech,
} from "react-native-nfc-manager";
import AppButton from "@/components/AppButton";
import Animated, { SlideInLeft } from "react-native-reanimated";

// 定义启动NFC会话的通用函数
const startNfcSession = async () => {
  try {
    const isActive = await NfcManager.isSessionAvailableIOS();
    if (isActive) return;
    
    await NfcManager.registerTagEvent({
      invalidateAfterFirstRead: false,
      isReaderModeEnabled: true,
      readerModeFlags: NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
      sessionTimeoutIOS: 0, // 设置为0表示无超时限制
    });
    console.log("NFC会话已启动");
  } catch (error) {
    console.error("启动NFC会话失败:", error);
  }
};

NfcManager.start({
  onSessionClosedIOS: async () => {
    console.log("iOS NFC会话已关闭,自动重启...");
    await startNfcSession(); // 会话关闭后自动重启
  },
})
  .then((result) => {
    console.log("NFC初始化成功", result);
  })
  .catch((error) => {
    console.warn("设备不支持NFC!");
  });

const Item = ({ title }) => {
  return (
    <Animated.View
      entering={SlideInLeft.duration(300)}
      style={{
        backgroundColor: "lightgreen",
        marginVertical: 4,
        padding: 12,
        borderRadius: 2,
        borderLeftWidth: 4,
      }}
    >
      <Animated.Text
        entering={SlideInLeft.duration(100)}
        style={{ fontSize: 16 }}
      >
        {title}
      </Animated.Text>
    </Animated.View>
  );
};

const Page = () => {
  const [tags, setTags] = useState([]);
  const [nfcState, setNfcState] = useState(null);
  const [nfcSupport, setNfcSupport] = useState(null);

  const processNfcTag = (tag) => {
    if (tag.ndefMessage) {
      const ndefMessage = tag.ndefMessage[0];
      const text = Ndef.text.decodePayload(ndefMessage.payload);
      console.log("解码文本:", text);
      setTags(prev => [...prev, { id: prev.length, tag: text }]);
    }
  };

  useEffect(() => {
    const tagListener = NfcManager.setEventListener(NfcEvents.DiscoverTag, processNfcTag);

    return () => {
      tagListener.remove();
      NfcManager.unregisterTagEvent(); // 组件卸载时清理会话
    };
  }, []);

  useEffect(() => {
    const checkNfcState = async () => {
      const _nfcSupport = await NfcManager.isSupported();
      setNfcSupport(_nfcSupport);
      const _nfcState = await NfcManager.isEnabled();
      setNfcState(_nfcState);
    };
    checkNfcState();
  }, []);

  const readTag = async () => {
    await startNfcSession();
  };

  if (nfcSupport === null || nfcState === null) {
    return null;
  } else if (!nfcSupport) {
    return (
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <Text>您的设备不支持NFC</Text>
      </View>
    );
  } else if (!nfcState) {
    return (
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <Text>NFC已关闭</Text>
      </View>
    );
  }

  return (
    <View style={{ padding: 16, gap: 12 }}>
      <AppButton
        style={{
          backgroundColor: "orange",
          alignSelf: "center",
          width: 100,
        }}
        title="启动"
        textColor="#111"
        onPress={readTag}
      />
      <FlatList
        data={tags}
        keyExtractor={(item) => `${item.id}_key`}
        renderItem={({ item }) => <Item title={item?.tag} />}
      />
    </View>
  );
};

export default Page;

const styles = StyleSheet.create({});

关键修改点

  • 提取通用的startNfcSession函数,避免代码重复
  • 在onSessionClosedIOS回调中自动调用startNfcSession实现会话重启
  • 设置sessionTimeoutIOS: 0取消iOS默认的60秒超时限制
  • 组件卸载时添加NfcManager.unregisterTagEvent()清理资源,避免内存泄漏

内容的提问来源于stack exchange,提问作者Irfan Deva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:39:54