Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOSDC2025 みてねiOSアプリにおける バックグラウンドアップロード継続の挑戦

Avatar for HikaruSato HikaruSato
September 20, 2025

iOSDC2025 みてねiOSアプリにおける バックグラウンドアップロード継続の挑戦

Avatar for HikaruSato

HikaruSato

September 20, 2025
Tweet

More Decks by HikaruSato

Other Decks in Technology

Transcript

  1. ©MIXI 9 • ը૾ɾಈըͷΞοϓϩʔυதʹόοΫάϥ΢ϯυભҠ͢ Δͱ30ඵͰΞοϓϩʔυ͕தஅ͞Εͯ͠·͏ ◦ iOSͷ beginBackgroundTask ͷ੍ݶ ◦

    ͦͷͨΊΞοϓϩʔυதʹΞϓϦΛ։͖ଓ͚͍ͯΔඞཁ͕͋Δ ◦ ΈͯͶͰ͸Ұ౓ʹଟ͘ͷը૾ɾಈըΛΞοϓϩʔυ͢Δ܏޲͕͋ ΔͨΊɺ͜ͷ੍ݶͰΞοϓϩʔυ͕தஅ͞Ε΍͍͢ ՝୊
  2. ©MIXI 11 BGProcessingTaskͷ࢓༷͕ΈͯͶͱ߹Θͳ͔ͬͨ • 1ճͷ࣮ߦ͕࣌ؒ5෼ఔ౓ • ௕͍ಈըͷΞοϓϩʔυ͕ऴΘΒͳ͍ • ୺຤͕ΞΠυϧঢ়ଶ •

    Ξοϓϩʔυ׬ྃޙͷ௨஌͕ਂ໷ʹඈΜͰ͠·͏ݒ೦ × ղܾҊ1 BGProcessingTask https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask
  3. ©MIXI 12 • Widget ExtensionͷҰछͰ͋ΔͨΊΞϓϦଆͷॲཧΛܧଓ͢Δ͜ ͱ͸Ͱ͖ͳ͔ͬͨ × ղܾҊ2 Live Activity

    https://developer.apple.com/jp/design/human-interface-guidelines/live-activities
  4. ©MIXI 18 ࢀߟࢿྉ • uakihir0͞ΜͷGithubͷެ։ϥΠϒϥϦ ◦ https://github.com/uakihir0/UIPiPView • ϛϥςΟϒ͞Μͷ഑৴ίϝϯτόʔͷTech Blog

    ◦ https://tech.mirrativ.stream/entry/2021/11/26/114002 • iOSDC 2022 ͷTsuzukihashi͞ΜͷτʔΫ ◦ https://speakerdeck.com/tsuzuki817/dong-hua-dakeziyanai-ios- 15nopikutiyainpikutiyawoshi-tutehao-kinauiwobiao-shi-saseyou
  5. ©MIXI 22 ࣮૷ࣗମ͕೉͍͠ • UIViewΛϏσΦϑϨʔϜͷΦϒδΣΫτ(AVSampleBufferDisplayLayerɺ CMSampleBuffer)ʹม׵͢Δ࣮૷ • PiPΛ։࢝Ͱ͖ΔΑ͏ʹ͢ΔͨΊͷ৚݅ ◦ Capability

    -> BackgroundModes -> Audio, AirPlay, and Picture in Picture ◦ AVAudioSession#setCategory(.playback) ͕ඞཁ • PiP͕։࢝͞Εͳͯ͘΋Τϥʔ͕ฦͬͯདྷͳ͍৔߹͕͋Δ • ࣮૷ৄࡉ͸αϯϓϧίʔυࢀর ◦ https://github.com/HikaruSato/AutoPictureInPictureUIViewExample UIViewΛPiPͰදࣔ͢ΔͨΊͷ࣮૷
  6. ©MIXI 25 •AVPictureInPictureController#canStartPictureInPictureAutomaticallyFromI nline = true ʢόοΫάϥ΢ϯυભҠ࣌ʹࣗಈͰPiPΛ։࢝ʣ͕ಈ࡞͢Δ৚ ͕݅Θ͔Βͳͯ͘ࢼߦࡨޡͨ͠ ◦ ৚݅

    ▪ දࣔର৅ͷAVSampleBufferDisplayLayer͕ඳը͞Ε͍ͯΔ ▪ όοΫάϥ΢ϯυભҠલʹAVPictureInPictureController ͕ॳظԽ͞Εͯ ͍Δ ▪ AVPictureInPictureSampleBufferPlaybackDelegateͷ pictureInPictureControllerIsPlaybackPaused(_:) ໭Γ஋͕ false όοΫάϥ΢ϯυભҠ࣌ʹࣗಈͰPiPΛ։࢝͢Δ
  7. ©MIXI 26 extension XXX: AVPictureInPictureSampleBufferPlaybackDelegate { public func pictureInPictureController(_ pictureInPictureController:

    AVPictureInPictureController, setPlaying playing: Bool) {} public func pictureInPictureControllerTimeRangeForPlayback(_ pictureInPictureController: AVPictureInPictureController) -> CMTimeRange { .init( start: .indefinite, // 30೔෼ʹηοτ duration: .init(seconds: 60 * 60 * 24 * 30, preferredTimescale: .init(1)) ) } public func pictureInPictureControllerIsPlaybackPaused(_ pictureInPictureController: AVPictureInPictureController) -> Bool { false // ˒canStartPictureInPictureAutomaticallyFromInline = true ʢόοΫάϥ΢ϯυભҠ࣌ʹࣗಈͰPiP͕։࢝͢ΔʣͷͨΊʹfalseʹ͢Δ } public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, didTransitionToRenderSize newRenderSize: CMVideoDimensions) {} public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime) async {} } AVPictureInPictureSampleBufferPlaybackDelegateͷίʔυ
  8. ©MIXI 36 • ػೳΛ׶͑ͯઆ໌ͯ͠AppleͷϨϏϡʔʹग़͢͜ͱͰٸʹReject ͞Εͳ͍Α͏ʹରࡦΛͨ͠ ◦ PiP։࢝͸Ϣʔβʔͷૢ࡞Ͱ։࢝͢Δඞཁ͕͋ΓɺϓϩάϥϜతʹPiP։࢝͢Δͷ͸ AppleͷϨ ϏϡʔͰReject͞ΕΔɺͱυΩϡϝϯτʹهࡌ͕͋Δ ▪

    https://developer.apple.com/documentation/avkit/adopting-picture-in-picture-in-a-custom-player#Handle-User-Initiated- Requests ◦ Ϣʔβʔͷૢ࡞͕൐͏ը૾ɾಈըͷΞοϓϩʔυૢ࡞ޙόοΫάϥ΢ϯυભҠͰPiPΛ։͍࢝ͯ͠ Δ͕ͦΕ͕Appleͱͯ͠OKͳͷ͔Λ೦ͷҝɺ֬ೝ͢Δ͜ͱʹͨ͠ • ϨϏϡʔ͸໰୊ͳ͘௨ͬͨ AppleͷϨϏϡʔରࡦ
  9. ©MIXI 37 ֬ೝํ๏ App Store Connect → App Review ʹؔ͢Δ৘ใ

    ʹPiPͷΞοϓϩʔυػೳʹͭ ͍ͯಈը෇͖Ͱઆ໌ͯ͠ਃ੥ͨ͠ͱ͜Ζɺಛʹ໰୊ͳ͘৹͕ࠪ௨ͬͨ
  10. ©MIXI 58 BGTaskScheduler.shared.register(forTaskWithIdentifier: taskIdentifier, using: nil) { task in //

    ϝΠϯҎ֎ͷεϨουͰಈ࡞͢Δ let task = task as! BGContinuedProcessingTask task.expirationHandler = { /* γεςϜʹΑΓλεΫ͕Ωϟϯηϧ͞Εͨ࣌ͷॲཧ */ } task.progress.totalUnitCount = 100 // ׬ྃ஋ task.progress.completedUnitCount = 0 // ਐḿͷ஋ for i in 1...100 { task.progress.completedUnitCount = Int64(i) // ਐḿΛߋ৽ task.updateTitle("Processing", subtitle: "xxx%") } task.updateTitle("Completed", subtitle: "") task.setTaskCompleted(success: isCompleted) } https://developer.apple.com/documentation/backgroundtasks/performing-long-running-tasks-on-ios-and-ipados όοΫάϥ΢ϯυλεΫొ࿥ͷίʔυ
  11. ©MIXI 59 do { let request = BGContinuedProcessingTaskRequest(identifier: taskIdentifier, title:

    "Start", subtitle: "") // .queue ͸λεΫϦΫΤετ͸Ωϡʔʹ௥Ճ͞ΕɺޙͰ࣮ߦ͞ΕΔ͜ͱ͕͋Δ(default) // .fail ͸λεΫϦΫΤετΛ͙͢ʹ࣮ߦͰ͖ͳ͍৔߹ʹΤϥʔʹͳΔ request.strategy = .queue if BGTaskScheduler.supportedResources.contains(.gpu) { request.requiredResources = .gpu // GPUΛར༻͍ͨ͠৔߹ʹηοτɻ } try BGTaskScheduler.shared.submit(request) } catch { print("Failed to submit request: \(error)") } https://developer.apple.com/documentation/backgroundtasks/performing-long-running-tasks-on-ios-and-ipados όοΫάϥ΢ϯυλεΫͷ࣮ߦґཔ
  12. ©MIXI 62 ݕূͯ͠ΈͯΘ͔ͬͨ͜ͱ • completedUnitCount(ਐḿͷ஋) ͕Ұఆ࣌ؒͷ͏ͪʹมԽ͕ͳ͍ ͱϓϩηε͕ऴྃ͞ΕΔ ◦ ࢭ·Δ࣌ؒ͸ෆఆ ◦

    ਐḿ͕Θ͔Βͳͯ͘΋ॲཧΛܧଓ͢ΔͨΊʹcompletedUnitCountΛ૿΍͢ ͳͲͷ޻෉͕ඞཁͳ৔߹͕͋Γͦ͏
  13. ©MIXI 64 ݕূͯ͠ΈͯΘ͔ͬͨ͜ͱ • Swift Language Version ͕ Swift 6

    ͩͱ BGTaskScheduler.shared.submit()ͰΫϥογϡ͢Δ ◦ Xcode26 RC࣌఺Ͱ࠶ݱɻAppleʹ͸όάϨϙʔτࡁΈ ◦ Swift 5 ͩͱΫϥογϡ͠ͳ͍