[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FGNT68JDVHF86NFBMAXQ6B":3,"skill-same:01M6FGNT68JDVHF86NFBMAXQ6B":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":49,"ownerHtmlUrl":50,"ownerBlogUrl":6,"ownerDescription":51,"updatedAt":52,"install":53,"resources":66},"01M6FGNT68JDVHF86NFBMAXQ6B","mobile-offline-support",null,"Offline-first mobile apps with local storage, sync queues, conflict resolution. Use for offline functionality, data sync, connectivity handling, or encountering sync conflicts, queue management, storage limits, network transition errors.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fsecondsky\u002Fclaude-skills\u002Fmobile-offline-support\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"mobile","mobile offline support","support","offline","conflicts","storage","errors","with","apps","connectivity","resolution","conflict","local","sync","conflict resolution","data sync","use","first","data","network","2026-11-10 10:12:30",false,"---\nname: mobile-offline-support\ndescription: Offline-first mobile apps with local storage, sync queues, conflict resolution. Use for offline functionality, data sync, connectivity handling, or encountering sync conflicts, queue management, storage limits, network transition errors.\nlicense: MIT\n---\n\n# Mobile Offline Support\n\nBuild offline-first mobile applications with local storage and synchronization.\n\n## React Native Implementation\n\n```javascript\nimport AsyncStorage from '@react-native-async-storage\u002Fasync-storage';\nimport NetInfo from '@react-native-community\u002Fnetinfo';\n\nclass OfflineManager {\n  constructor() {\n    this.syncQueue = [];\n    this.isOnline = true;\n    \u002F\u002F Maximum items in sync queue before discarding oldest\n    this.MAX_SYNC_QUEUE_LENGTH = 1000;\n\n    NetInfo.addEventListener(state => {\n      this.isOnline = state.isConnected;\n      if (this.isOnline) this.processQueue();\n    });\n  }\n\n  \u002F**\n   * Fetch data from server.\n   * TODO: Replace with actual API endpoint implementation.\n   *\u002F\n  async fetchFromServer(key) {\n    try {\n      \u002F\u002F Example implementation - replace with your API\n      const response = await fetch(`${API_BASE_URL}\u002Fdata\u002F${key}`);\n      if (!response.ok) {\n        throw new Error(`Server returned ${response.status}`);\n      }\n      return await response.json();\n    } catch (error) {\n      console.error('fetchFromServer failed:', error);\n      throw new Error(`Failed to fetch ${key}: ${error.message}`);\n    }\n  }\n\n  \u002F**\n   * Sync data to server.\n   * TODO: Replace with actual API endpoint implementation.\n   *\u002F\n  async syncToServer(key, data) {\n    try {\n      \u002F\u002F Example implementation - replace with your API\n      const response = await fetch(`${API_BASE_URL}\u002Fdata\u002F${key}`, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\u002Fjson' },\n        body: JSON.stringify(data)\n      });\n      if (!response.ok) {\n        throw new Error(`Server returned ${response.status}`);\n      }\n      return await response.json();\n    } catch (error) {\n      console.error('syncToServer failed:', error);\n      throw new Error(`Failed to sync ${key}: ${error.message}`);\n    }\n  }\n\n  async getData(key) {\n    const cached = await AsyncStorage.getItem(key);\n    if (cached) return JSON.parse(cached);\n\n    if (this.isOnline) {\n      const data = await this.fetchFromServer(key);\n      await AsyncStorage.setItem(key, JSON.stringify(data));\n      return data;\n    }\n\n    return null;\n  }\n\n  async saveData(key, data) {\n    await AsyncStorage.setItem(key, JSON.stringify(data));\n\n    if (this.isOnline) {\n      await this.syncToServer(key, data);\n    } else {\n      \u002F\u002F Add to queue\n      this.syncQueue.push({ key, data, timestamp: Date.now() });\n\n      \u002F\u002F Enforce queue bounds - discard oldest if exceeded\n      while (this.syncQueue.length > this.MAX_SYNC_QUEUE_LENGTH) {\n        const discarded = this.syncQueue.shift();\n        console.warn(`Sync queue full - discarded oldest item: ${discarded.key}`);\n      }\n\n      \u002F\u002F Persist trimmed queue\n      await AsyncStorage.setItem('syncQueue', JSON.stringify(this.syncQueue));\n    }\n  }\n\n  async processQueue() {\n    const failedItems = [];\n    for (const item of this.syncQueue) {\n      try {\n        await this.syncToServer(item.key, item.data);\n      } catch (err) {\n        console.error('Sync failed:', err);\n        failedItems.push(item);\n      }\n    }\n    this.syncQueue = failedItems;\n    if (failedItems.length === 0) {\n      await AsyncStorage.removeItem('syncQueue');\n    } else {\n      await AsyncStorage.setItem('syncQueue', JSON.stringify(failedItems));\n    }\n  }\n}\n```\n\n## Conflict Resolution\n\n```javascript\nfunction resolveConflict(local, server) {\n  \u002F\u002F Last-write-wins\n  if (local.updatedAt > server.updatedAt) return local;\n  return server;\n\n  \u002F\u002F Or merge changes\n  \u002F\u002F return { ...server, ...local };\n}\n```\n\n## UI Indicators\n\n```jsx\nfunction OfflineIndicator() {\n  const [isOnline, setIsOnline] = useState(true);\n\n  useEffect(() => {\n    return NetInfo.addEventListener(state => {\n      setIsOnline(state.isConnected);\n    });\n  }, []);\n\n  if (isOnline) return null;\n\n  return (\n    \u003CView style={styles.banner}>\n      \u003CText>You're offline. Changes will sync when connected.\u003C\u002FText>\n    \u003C\u002FView>\n  );\n}\n```\n\n## Best Practices\n\n- Cache frequently accessed data locally\n- Queue actions for later sync\n- Show clear offline indicators\n- Handle sync conflicts gracefully\n- Compress stored data\n- Test offline scenarios thoroughly\n\n## Native Implementations\n\nSee [references\u002Fnative-implementations.md](references\u002Fnative-implementations.md) for:\n- iOS Core Data with sync manager\n- Android Room database with WorkManager sync\n\n## Avoid\n\n- Assuming connectivity\n- Losing data on sync failures\n- Unbounded queue growth\n- Syncing sensitive data insecurely\n","# Mobile Offline Support\n\nBuild offline-first mobile applications with local storage and synchronization.\n\n## React Native Implementation\n\n```javascript\nimport AsyncStorage from '@react-native-async-storage\u002Fasync-storage';\nimport NetInfo from '@react-native-community\u002Fnetinfo';\n\nclass OfflineManager {\n  constructor() {\n    this.syncQueue = [];\n    this.isOnline = true;\n    \u002F\u002F Maximum items in sync queue before discarding oldest\n    this.MAX_SYNC_QUEUE_LENGTH = 1000;\n\n    NetInfo.addEventListener(state => {\n      this.isOnline = state.isConnected;\n      if (this.isOnline) this.processQueue();\n    });\n  }\n\n  \u002F**\n   * Fetch data from server.\n   * TODO: Replace with actual API endpoint implementation.\n   *\u002F\n  async fetchFromServer(key) {\n    try {\n      \u002F\u002F Example implementation - replace with your API\n      const response = await fetch(`${API_BASE_URL}\u002Fdata\u002F${key}`);\n      if (!response.ok) {\n        throw new Error(`Server returned ${response.status}`);\n      }\n      return await response.json();\n    } catch (error) {\n      console.error('fetchFromServer failed:', error);\n      throw new Error(`Failed to fetch ${key}: ${error.message}`);\n    }\n  }\n\n  \u002F**\n   * Sync data to server.\n   * TODO: Replace with actual API endpoint implementation.\n   *\u002F\n  async syncToServer(key, data) {\n    try {\n      \u002F\u002F Example implementation - replace with your API\n      const response = await fetch(`${API_BASE_URL}\u002Fdata\u002F${key}`, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\u002Fjson' },\n        body: JSON.stringify(data)\n      });\n      if (!response.ok) {\n        throw new Error(`Server returned ${response.status}`);\n      }\n      return await response.json();\n    } catch (error) {\n      console.error('syncToServer failed:', error);\n      throw new Error(`Failed to sync ${key}: ${error.message}`);\n    }\n  }\n\n  async getData(key) {\n    const cached = await AsyncStorage.getItem(key);\n    if (cached) return JSON.parse(cached);\n\n    if (this.isOnline) {\n      const data = await this.fetchFromServer(key);\n      await AsyncStorage.setItem(key, JSON.stringify(data));\n      return data;\n    }\n\n    return null;\n  }\n\n  async saveData(key, data) {\n    await AsyncStorage.setItem(key, JSON.stringify(data));\n\n    if (this.isOnline) {\n      await this.syncToServer(key, data);\n    } else {\n      \u002F\u002F Add to queue\n      this.syncQueue.push({ key, data, timestamp: Date.now() });\n\n      \u002F\u002F Enforce queue bounds - discard oldest if exceeded\n      while (this.syncQueue.length > this.MAX_SYNC_QUEUE_LENGTH) {\n        const discarded = this.syncQueue.shift();\n        console.warn(`Sync queue full - discarded oldest item: ${discarded.key}`);\n      }\n\n      \u002F\u002F Persist trimmed queue\n      await AsyncStorage.setItem('syncQueue', JSON.stringify(this.syncQueue));\n    }\n  }\n\n  async processQueue() {\n    const failedItems = [];\n    for (const item of this.syncQueue) {\n      try {\n        await this.syncToServer(item.key, item.data);\n      } catch (err) {\n        console.error('Sync failed:', err);\n        failedItems.push(item);\n      }\n    }\n    this.syncQueue = failedItems;\n    if (failedItems.length === 0) {\n      await AsyncStorage.removeItem('syncQueue');\n    } else {\n      await AsyncStorage.setItem('syncQueue', JSON.stringify(failedItems));\n    }\n  }\n}\n```\n\n## Conflict Resolution\n\n```javascript\nfunction resolveConflict(local, server) {\n  \u002F\u002F Last-write-wins\n  if (local.updatedAt > server.updatedAt) return local;\n  return server;\n\n  \u002F\u002F Or merge changes\n  \u002F\u002F return { ...server, ...local };\n}\n```\n\n## UI Indicators\n\n```jsx\nfunction OfflineIndicator() {\n  const [isOnline, setIsOnline] = useState(true);\n\n  useEffect(() => {\n    return NetInfo.addEventListener(state => {\n      setIsOnline(state.isConnected);\n    });\n  }, []);\n\n  if (isOnline) return null;\n\n  return (\n    \u003CView style={styles.banner}>\n      \u003CText>You're offline. Changes will sync when connected.\u003C\u002FText>\n    \u003C\u002FView>\n  );\n}\n```\n\n## Best Practices\n\n- Cache frequently accessed data locally\n- Queue actions for later sync\n- Show clear offline indicators\n- Handle sync conflicts gracefully\n- Compress stored data\n- Test offline scenarios thoroughly\n\n## Native Implementations\n\nSee [references\u002Fnative-implementations.md](references\u002Fnative-implementations.md) for:\n- iOS Core Data with sync manager\n- Android Room database with WorkManager sync\n\n## Avoid\n\n- Assuming connectivity\n- Losing data on sync failures\n- Unbounded queue growth\n- Syncing sensitive data insecurely\n",{"name":5,"license":15,"description":7},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fsecondsky\u002Fclaude-skills\u002Fmobile-offline-support",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill mobile-offline-support",{"tool":59,"command":60},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill mobile-offline-support",{"tool":62,"command":63},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill mobile-offline-support","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fsecondsky\u002Fclaude-skills\u002Fmobile-offline-support\u002FSKILL.md",true,[67],{"path":68,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":71,"sourceHtmlUrl":72},"references\u002Fnative-implementations.md","REFERENCE","参考文档",8082,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fsecondsky\u002Fclaude-skills\u002Fmobile-offline-support\u002Freferences\u002Fnative-implementations.md",[74,88,98,106,112,119],{"skillNo":75,"name":76,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":84,"publishedAt":39},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[79],{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[85,86,87],"model","model hierarchy","model hierarchy skill",{"skillNo":89,"name":90,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":94,"publishedAt":39},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[93],{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},[95,96,97],"MCP","zoom","zoom mcp",{"skillNo":99,"name":100,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":103,"publishedAt":39},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[104,105,27,96],"sdk","zoom apps sdk",{"skillNo":107,"name":108,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":111,"publishedAt":39},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[108],{"skillNo":113,"name":114,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":118,"publishedAt":39},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[117],{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},[114],{"skillNo":120,"name":121,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":124,"publishedAt":39},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[121]]