AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79301741
Accepted
Deepak Sharma
Deepak Sharma
Asked: 2024-12-23 05:05:07 +0800 CST2024-12-23 05:05:07 +0800 CST 2024-12-23 05:05:07 +0800 CST

Usando AsyncStream vs @Observable macro no SwiftUI

  • 772

Quero entender a utilidade de usar AsyncStreamquando o iOS 17 introduziu @Observablea macro onde podemos observar diretamente as mudanças no valor de qualquer variável no modelo (e o rastreamento de observação pode acontecer mesmo fora da visualização SwiftUI). Então, se eu estiver observando um fluxo contínuo de valores, como o progresso do download de um arquivo usando AsyncStreamuma visualização SwiftUI, o mesmo pode ser observado na mesma visualização SwiftUI usando onChange(of:initial)o progresso do download (armazenado como uma propriedade no objeto do modelo). Estou procurando benefícios, desvantagens e limitações de ambas as abordagens.

Especificamente, minha pergunta é com relação ao código de exemplo AVCam da Apple, onde eles observam alguns estados como segue. Isso é feito em CameraModelclasse que é anexada à visualização SwiftUI.

    // MARK: - Internal state observations

// Set up camera's state observations.
private func observeState() {
    Task {
        // Await new thumbnails that the media library generates when saving a file.
        for await thumbnail in mediaLibrary.thumbnails.compactMap({ $0 }) {
            self.thumbnail = thumbnail
        }
    }
    
    Task {
        // Await new capture activity values from the capture service.
        for await activity in await captureService.$captureActivity.values {
            if activity.willCapture {
                // Flash the screen to indicate capture is starting.
                flashScreen()
            } else {
                // Forward the activity to the UI.
                captureActivity = activity
            }
        }
    }
    
    Task {
        // Await updates to the capabilities that the capture service advertises.
        for await capabilities in await captureService.$captureCapabilities.values {
            isHDRVideoSupported = capabilities.isHDRSupported
            cameraState.isVideoHDRSupported = capabilities.isHDRSupported
        }
    }
    
    Task {
        // Await updates to a person's interaction with the Camera Control HUD.
        for await isShowingFullscreenControls in await captureService.$isShowingFullscreenControls.values {
            withAnimation {
                // Prefer showing a minimized UI when capture controls enter a fullscreen appearance.
                prefersMinimizedUI = isShowingFullscreenControls
            }
        }
    }
}

Se virmos a estrutura CaptureCapabilities, é uma estrutura pequena com dois membros Bool. Essas mudanças poderiam ter sido observadas diretamente por uma visualização SwiftUI. Gostaria de saber se há uma vantagem ou razão específica para usar AsyncStreamaqui e iterar continuamente sobre as mudanças em um loop for.

   /// A structure that represents the capture capabilities of `CaptureService` in
  /// its current configuration.
struct CaptureCapabilities {

let isLivePhotoCaptureSupported: Bool
let isHDRSupported: Bool

init(isLivePhotoCaptureSupported: Bool = false,
     isHDRSupported: Bool = false) {
    self.isLivePhotoCaptureSupported = isLivePhotoCaptureSupported
    self.isHDRSupported = isHDRSupported
}

  static let unknown = CaptureCapabilities()
}
  • 1 1 respostas
  • 115 Views

1 respostas

  • Voted
  1. Best Answer
    Rob
    2024-12-25T03:32:40+08:002024-12-25T03:32:40+08:00

    Algumas perguntas aqui:

    1. Para responder à pergunta no abstrato, AsyncSequence(do qual AsyncStreamé apenas um exemplo concreto) é um padrão mais geral. Quando implementado corretamente, suporta cancelamento, produz valores assincronamente ao longo do tempo, e onde necessário, e tem a noção de "terminar" quando a sequência é feita. Os padrões ObservableObject/ @Observablesão um padrão mais estreito (notavelmente, @Observableé ideal para SwiftUI, um pouco incômodo em UIKit/AppKit) para publicar alterações de algum estado de um objeto. Para seu exemplo específico (para informar SwiftUI sobre alterações de estado ao longo do tempo), tanto a observação quanto as sequências assíncronas podem realizar o trabalho; eu não perderia muito sono em relação a uma ou outra.

    2. Padrões relacionados à observação são bem naturais quando uma IU quer ser atualizada com base no estado de um objeto. É um pouco menos natural quando você quer que algum objeto (como este serviço de “câmera”) atualize seu estado com base em outros eventos (embora você possa fazer isso). Não vejo nenhuma falha inerente no padrão de sequência assíncrona empregado aqui. Mas você pode adotar qualquer um dos padrões aqui. Na verdade, eles estão apenas criando sequências assíncronas de publicadores usando values, então é um pouco uma mistura de ambos.

    3. Se você for usar sequências assíncronas, devemos observar que toda a concorrência não estruturada nisso observeStateé uma implementação questionável. Ela está iniciando quatro tarefas não estruturadas sem nenhuma maneira de cancelá-las.

      Claro, eles estão lançando isso do AVCamApp, então, neste caso, eles não estão pensando em parar o CameraModel, mas eu realmente desencorajaria esse padrão. Não se deve assar em comportamento não cancelável, especialmente em projetos de demonstração que os desenvolvedores podem estar inclinados a copiar e/ou incorporar em seus projetos.

      Geralmente, salvaríamos referências a essas tarefas e, então, forneceríamos uma função stop/ cancelpara cancelá-las. Ou, neste caso, melhor, perder a concorrência não estruturada, encapsular essas quatro em um grupo de tarefas:

      private func observeState() async {
          await withDiscardingTaskGroup { group in
              group.addTask { @MainActor [self] in
                  for await thumbnail in mediaLibrary.thumbnails.compactMap({ $0 }) {
                      …
                  }
              }
      
              group.addTask { @MainActor [self] in
                  for await activity in await captureService.$captureActivity.values {
                      …
                  }
              }
      
              group.addTask { @MainActor [self] in
                  for await capabilities in await captureService.$captureCapabilities.values {
                      …
                  }
              }
      
              group.addTask { @MainActor [self] in
                  for await isShowingFullscreenControls in await captureService.$isShowingFullscreenControls.values {
                      …
                  }
              }
          }
      }
      

      Então, mudaríamos start(que eu renomearia run) para fazer isso por último:

      func run() async {
          // Verify that the person authorizes the app to use device cameras and microphones.
          guard await captureService.isAuthorized else { … }
          do {
              await syncState()
      
              try await captureService.start(with: cameraState)
              status = .running
              await observeState() // do this last; when `run` is cancelled, this will automatically be cancelled, too
          } catch { … }
      }
      

      Então você Viewpoderia fazer algo como:

      var body: some View {
          CameraView(camera: camera)
              .task {
                  // Start the capture pipeline. If view is dismissed, asynchronous sequences will be cancelled.
                  await camera.run()
              }
      }
      

      Agora, novamente, o exemplo de código original estava fazendo isso no App, então esse problema não se manifestou lá. Mas o acima seria uma solução mais generalizada, funcionando tanto em an Appquanto em a View.

      O uso do código original de toda essa simultaneidade não estruturada e a falha em contemplar o cancelamento é um antipadrão. Queremos CameraModelpoder ser usados ​​de um Appou de um View. (Vou ignorar se CameraModelé um bom nome para esse objeto ou não; parece mais um “serviço” do que um simples “modelo”, na minha humilde opinião.)

    • 1

relate perguntas

  • Adicionar número de série para atividade de cópia ao blob

  • A fonte dinâmica do empacotador duplica artefatos

  • Selecione linhas por grupo com 1s consecutivos

  • Lista de chamada de API de gráfico subscritoSkus estados Privilégios insuficientes enquanto os privilégios são concedidos

  • Função para criar DFs separados com base no valor da coluna

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve