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

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 ͩͱΫϥογϡ͠ͳ͍