Swift does not know at what Actor is a Task?

I am learning async/await and Task. So i learned that Task basically inherit actor.
Imagine I have a model:

class SomeModel: ObservableObject {
   @Published var downloads: [Int] = []

   func doSome() async throw {
      // MAKE URL
      downloads.append(1)

      try await /// MAKE REQUEST
      downloads.append(data) // after request
    
   }
}

And I have a simple View

struct DownloadView: View {

  @EnvironmentObject var model: SomeModel


  var body: some View {
    List {
      // USE model.downloads 
    }
    .task {
        do {
          try await model.doSome()
        } catch {}
    }
}

I get an error that I am updating UI from background thread. Thats ok.
Then i add

func doSome() async throw {
      // MAKE URL
      downloads.append(1)

      try await /// MAKE REQUEST
      await MainActor.run {
            downloads.append(data)
       } // after request
}

And I have still Error. System does not know that first append is Always main actor? Or this is a security from dummies if some on pus some suspended code before first append?

How I understand hierarchical await is needed for System know what tree of sub jobs need to suspend. But a suspension point is after first append, so it could not pass on different actor.

Code for reproduce:
Model

struct DownloadFile: Identifiable {
    var id: String { return name }
    let name: String
    let size: Int
    let date: Date
    
    static let mockFiles = [DownloadFile(name: "File1", size: 100, date: Date()),
                            DownloadFile(name: "File2", size: 100, date: Date()),
                            DownloadFile(name: "File3", size: 100, date: Date()),
                            DownloadFile(name: "File4", size: 100, date: Date()),
                            DownloadFile(name: "File5", size: 100, date: Date()),
                            DownloadFile(name: "FIle6", size: 100, date: Date())]
    
    static let empty = DownloadFile(name: "", size: 0, date: Date())
}

FirstView

struct ContentView: View {
    @State var files: [DownloadFile] = []
    let model: ViewModel
    
    @State var selected = DownloadFile.empty {
      didSet {
        isDisplayingDownload = true
      }
    }
    
    @State var isDisplayingDownload = false
    
    var body: some View {
      NavigationStack {
        VStack {
          // The list of files available for download.
          List {
            Section(content: {
              if files.isEmpty {
                ProgressView().padding()
              }
              ForEach(files) { file in
                Button(action: {
                  selected = file
                }, label: {
                    Text(file.name)
                })
              }
            })
          }
          .listStyle(.insetGrouped)
        }
        .task {
            try? await Task.sleep(for: .seconds(1))
            files = DownloadFile.mockFiles
        }
        .navigationDestination(isPresented: $isDisplayingDownload) {
          DownloadView(file: selected).environmentObject(model)
        }
      }
    }
}

Second view

struct DownloadView: View {
    let file: DownloadFile
    @EnvironmentObject var model: ViewModel
    @State var result: String = ""
    var body: some View {
        VStack {
            Text(file.name)
            if model.downloads.isEmpty {
                Button {
                    Task {
                        result = try await model.download(file: file)
                    }
                } label: {
                    Text("-- Download --")
                }
            }
            Text(result)
        }
    }
}

viewModel

class ViewModel: ObservableObject {
    @Published var downloads: [String] = []
    
    func download(file: DownloadFile) async throws -> String {
        downloads.append(file.name)

        try await Task.sleep(for: .seconds(2))
        return "Download Finished"
    }
}

12

System does not know that first append is Always main actor?

How should the “system” (or more precisely the compiler) know this at this point?
There is no magic in Swift Concurrency that would recognize that if you access a property in one code location from a specific actor, all accesses to that property should be protected with that actor.

In your case, SomeModel is not bound to a specific actor, nor is the doSome function.
The task modifier creates a new top level task and if you don’t bind it to a specific actor you have the described issue.

Child tasks inherit the actor of the top-level task to which they belong, but for top-level tasks you must specifically specify an actor.

So what you actually should do is bind your view model to the main actor:

@MainActor
final class SomeModel: ObservableObject {
    // ...
}

This means that access to all properties is protected by the MainActor and any attempt to access a property from another actor must take place as an async call. This is then enforced by the compiler and makes it clear that a special protected call must take place at this point.
Roughly speaking, it is as if you have to wait for a lock at this point, with the difference that the calling thread is not blocked as a result.

Or, if you are sure of what you are doing, just bind the downloads property and maybe the doSome method to the MainActor and make sure that all other accesses to SomeModel are protected accordingly:

final class SomeModel: ObservableObject {
    @MainActor @Published private(set) var downloads: [Int] = []

    // ...

    @MainActor
    func doSome() async throw {
        // ...
    }
}

At first glance, your implementation listed below has fairly clear concurrency issues:

func doSome() async throw {
      // MAKE URL
      downloads.append(1)

      try await /// MAKE REQUEST
      await MainActor.run {
            downloads.append(data)
       } // after request
}

Since the class of the method is not bound to any actor, the asynchronous doSome function can be called by practically any task (and therefore thread).

This means that the method can also be called several times concurrently and in the first line you already make a concurrent access to downloads, which is a problem because nothing protects downloads at this point.

And while your second access to downloads is made on the MainActor, other tasks may still make another concurrent call to doSome and access downloads in the first line of this method.

I would generally recommend activating the “strict concurrency checking” option, which will detect a lot of concurrency issues at compile time and emit errors.

See here and here.

This mode can also be activated with Swift 5, but does not show as many possible errors as in Swift 6 mode.

11

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật