Cannot find in scope in swift in DispatchQueue.main.async - swift

I have this code in SWift:
import UIKit
import Foundation
func whatever() async -> Int{
return 2;
}
func test(){
Task.init{
var testing:Int = 0
do {
testing = try await whatever()
}
catch {
print("some error happened")
}
}
DispatchQueue.main.async {
print("from dispa")
if(testing == 0){
print("testing was never set")
}
}
}
test()
It is a playground and upon running it, I get Cannot find 'testing' in scope in the if statement in the DispatchQueue
What am I doing wrong? Moving the code within the Task.init throws a different error: Reference to captured var 'testing' in concurrently-executing code

The variable testing is declared inside the Task.init scope right now, meaning the DispatchQueue closure has no access to it. To use it in both closures, you must move it to a scope in which both closures have access to it (ie a shared parent scope).
Once you've done that, I'll say it's a little unclear on what exactly you want happening here, but I'm guessing this is the result you are looking for. Note that you should be careful about mixing Tasks and DispatchQueues -- they are different paradigms and do not necessarily lead to the results that you think they would.
var testing = 0
func whatever() async throws -> Int {
return 2 //note that nothing async or throwing is actually happening here
}
func test(){
Task {
do {
testing = try await whatever()
}
catch {
print("some error happened")
}
}
DispatchQueue.main.async {
print("from dispatch")
if testing == 0 {
print("testing was never set")
}
}
}

Related

Task #Sendable operation

Writing a simple code:
class App {
private var value = 0
func start() async throws {
await withTaskGroup(of: Void.self) { group in
for _ in 1...100 {
group.addTask(operation: self.increment) // 1
group.addTask {
await self.increment() // 2
}
group.addTask {
self.value += 1 // 3
}
}
}
}
#Sendable private func increment() async {
self.value += 1 // 4
}
}
I got compile time warnings at lines 2, 3:
Capture of 'self' with non-sendable type 'App' in a #Sendable closure
However with enabled Thread Sanitizer, and removed lines 2 and 3, I got ThreadSanitizer runtime warning at line 4:
Swift access race in (1) suspend resume partial function for asyncTest.App.increment#Sendable () async -> () at 0x106003c80
So I have questions:
What is the difference between using these 3 .addTask ways?
What does #Sendable attribute does?
How can I make increment() function thread safe (data race free)?
For illustration of how to achieve thread-safety, consider:
class Counter {
private var value = 0
func incrementManyTimes() async {
await withTaskGroup(of: Void.self) { group in
for _ in 1...1_000_000 {
group.addTask {
self.increment() // no `await` as this is not `async` method
}
}
await group.waitForAll()
if value != 1_000_000 {
print("not thread-safe apparently; value =", value) // not thread-safe apparently; value = 994098
} else {
print("ok")
}
}
}
private func increment() { // note, this isn't `async` (as there is no `await` suspension point in here)
value += 1
}
}
That illustrates that it is not thread-safe, validating the warning from TSAN. (Note, I bumped the iteration count to make it easier to manifest the symptoms of non-thread-safe code.)
So, how would you make it thread-safe? Use an actor:
actor Counter {
private var value = 0
func incrementManyTimes() async {
await withTaskGroup(of: Void.self) { group in
for _ in 1...1_000_000 {
group.addTask {
await self.increment()
}
}
await group.waitForAll()
if value != 1_000_000 {
print("not thread-safe apparently; value =", value)
} else {
print("ok") // ok
}
}
}
private func increment() { // note, this still isn't `async`
value += 1
}
}
If you really want to use a class, add your own old-school synchronization. Here I am using a lock, but you could use a serial GCD queue, or whatever you want.
class Counter {
private var value = 0
let lock = NSLock()
func incrementManyTimes() async {
await withTaskGroup(of: Void.self) { group in
for _ in 1...1_000_000 {
group.addTask {
self.increment()
}
}
await group.waitForAll()
if value != 1_000_000 {
print("not thread-safe apparently; value =", value)
} else {
print("ok") // ok
}
}
}
private func increment() {
lock.synchronize {
value += 1
}
}
}
extension NSLocking {
func synchronize<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
Or, if you want to make Counter a Sendable type, too, confident that you are properly doing the synchronization yourself, like above, you can declare it as final and declare that it is Sendable, admittedly #unchecked by the compiler:
final class Counter: #unchecked Sendable {
private var value = 0
let lock = NSLock()
func incrementManyTimes() async {
// same as above
}
private func increment() {
lock.synchronize {
value += 1
}
}
}
But because the compiler cannot possibly reason about the actual “sendability” itself, you have to designate this as a #unchecked Sendable to let it know that you personally have verified it is really Sendable.
But actor is the preferred mechanism for ensuring thread-safety, eliminating the need for this custom synchronization logic.
For more information, see WWDC 2021 video Protect mutable state with Swift actors or 2022’s Eliminate data races using Swift Concurrency.
See Sendable documentation for a discussion of what the #Sendable attribute does.
BTW, I suspect you know this, but for the sake of future readers, while increment is fine for illustrative purposes, it is not a good candidate for parallelism. This parallelized rendition is actually slower than a simple, single-threaded solution. To achieve performance gains of parallelism, you need to have enough work on each thread to justify the modest overhead that parallelism entails.
Also, when testing parallelism, be wary of using the simulator, which significantly/artificially constrains the cooperative thread pool used by Swift concurrency. Test on macOS target, or a physical device. Neither the simulator nor a playground is a good testbed for this sort of parallelism exercise.

How to test a method that contains Task Async/await in swift

Given the following method that contains a Task.
self.interactor is mocked.
func submitButtonPressed() {
Task {
await self.interactor?.fetchSections()
}
}
How can I write a test to verify that the fetchSections() was called from that method?!
My first thought was to use expectations and wait until it is fulfilled (in mock's code).
But is there any better way with the new async/await?
Ideally, as you imply, your interactor would be declared using a protocol so that you can substitute a mock for test purposes. You then consult the mock object to confirm that the desired method was called. In this way you properly confine the scope of the system under test to answer only the question "was this method called?"
As for the structure of the test method itself, yes, this is still asynchronous code and, as such, requires asynchronous testing. So using an expectation and waiting for it is correct. The fact that your app uses async/await to express asynchronousness does not magically change that! (You can decrease the verbosity of this by writing a utility method that creates a BOOL predicate expectation and waits for it.)
I don't know if you already find a solution to your question, but here is my contribution to other developers facing the same problem.
I was in the same situation as you, and I solved the problem by using Combine to notify the tested class that the method was called.
Let's say that we have this method to test:
func submitButtonPressed() {
Task {
await self.interactor?.fetchSections()
}
}
We should start by mocking the interaction:
import Combine
final class MockedInteractor: ObservableObject, SomeInteractorProtocol {
#Published private(set) var fetchSectionsIsCalled = false
func fetchSection async {
fetchSectionsIsCalled = true
// Do some other mocking if needed
}
}
Now that we have our mocked interactor we can start write unit test:
import XCTest
import Combine
#testable import YOUR_TARGET
class MyClassTest: XCTestCase {
var mockedInteractor: MockedInteractor!
var myClass: MyClass!
private var cancellable = Set<AnyCancellable>()
override func setUpWithError() throws {
mockedInteractor = .init()
// the interactor should be injected
myClass = .init(interactor: mockedInteractor)
}
override func tearDownWithError() throws {
mockedInteractor = nil
myClass = nil
}
func test_submitButtonPressed_should_callFetchSections_when_Always(){
//arrage
let methodCallExpectation = XCTestExpectation()
interactor.$fetchSectionsIsCalled
.sink { isCalled in
if isCalled {
methodCallExpectation.fulfill()
}
}
.store(in: &cancellable)
//acte
myClass.submitButtonPressed()
wait(for: [methodCallExpectation], timeout: 1)
//assert
XCTAssertTrue(interactor.fetchSectionsIsCalled)
}
There was one solution suggested here (#andy) involving injecting the Task. There's a way to do this by the func performing the task returning the Task and allows a test to await the value.
(I'm not crazy about changing a testable class to suit the test (returning the Task), but it allows to test async without NSPredicate or setting some arbitrary expectation time (which just smells)).
#discardableResult
func submitButtonPressed() -> Task<Void, Error> {
Task { // I'm allowed to omit the return here, but it's returning the Task
await self.interactor?.fetchSections()
}
}
// Test
func testSubmitButtonPressed() async throws {
let interactor = MockInteractor()
let task = manager.submitButtonPressed()
try await task.value
XCTAssertEqual(interactor.sections.count, 4)
}
I answered a similar question in this post: https://stackoverflow.com/a/73091753/2077405
Basically, given code defined like this:
class Owner{
let dataManager: DataManagerProtocol = DataManager()
var data: String? = nil
init(dataManager: DataManagerProtocol = DataManager()) {
self.dataManager = dataManager
}
func refresh() {
Task {
self.data = await dataManager.fetchData()
}
}
}
and the DataManagerProtocol is defined as:
protocol DataManagerProtocol {
func fetchData() async -> String
}
a mock/fake implementation can be defined:
class MockDataManager: DataManagerProtocol {
func fetchData() async -> String {
"testData"
}
}
Implementing the unit test should go like this:
...
func testRefreshFunctionFetchesDataAndPopulatesFields() {
let expectation = XCTestExpectation(
description: "Owner fetches data and updates properties."
)
let owner = Owner(mockDataManager: DataManagerProtocol())
// Verify initial state
XCTAssertNil(owner.data)
owner.refresh()
let asyncWaitDuration = 0.5 // <= could be even less than 0.5 seconds even
DispatchQueue.main.asyncAfter(deadline: .now() + asyncWaitDuration) {
// Verify state after
XCTAssertEqual(owner.data, "testData")
expectation.fulfill()
}
wait(for: [expectation], timeout: asyncWaitDuration)
}
...
Hope this makes sense?

Swift defer statement placement [duplicate]

I have the following swift code executing in playground:
func A() {
print ("Hello")
guard 1 == 2 else {
return
}
defer {
print ("World")
}
}
A()
I expected to see
Hello
World
Instead only the Hello is printed. Why is this? What am I missing?
Here is a better example:
enum MyError: ErrorType {
case TriggerDefer
}
func throwsMyError() throws {
let myzero = Int(arc4random_uniform(1))
guard myzero > 1 else {
throw MyError.TriggerDefer
}
}
func A() throws {
try throwsMyError()
defer {
print ("Hello World")
}
}
As per the answers and comments, the correct way to do this (with an example) is
enum MyError: ErrorType {
case TriggerDefer
}
func throwsMyError() throws {
let myzero = Int(arc4random_uniform(1))
print("Hello")
guard myzero > 1 else {
throw MyError.TriggerDefer
}
}
func A() throws {
defer {
print ("World")
}
try throwsMyError()
}
The output will now be
Hello
World
What you're missing is that deferis not magic. It is executable code, just like any other code. If the path of execution never encounters it, there is nothing to be deferred. This is why it should always be dead first in the block on whose exit it is to be executed — so that we guarantee that it is encountered.
Put the defer block before the scope is exited:
func A() {
print ("Hello")
defer {
print ("World")
}
guard 1 == 2 else {
return
}
}
A()

Why does iterating over closures cause a bus error in swift?

I'm getting a strange Bus Error when running what appears to be perfectly safe swift code. I've tried to reduce it down to a minimal test case, as follows:
Apple Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 0ddf238ad7)
Target: x86_64-apple-macosx10.9
This code:
public enum MyError: ErrorType {
case SomeError(code: Int)
}
public typealias MyType = () throws -> Bool
public class Foo {
var a:MyType = { () throws -> Bool in
print("A")
return true
}
var b:MyType = { () throws -> Bool in
print("B")
return true
}
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
}
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
for child in mirror.children {
if let callable = child.value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
}
let foo = Foo()
handle(foo)
Generates this output:
Foo
A
B
C
Bus error: 10
Running it in the debugger works fine, so I assume it has something to do with a timing issue at runtime.
Am I doing something illegal or unsafe in this code?
Are exceptions somehow illegal in closures?
What's causing this error?
Edit:
I've created a bug on the swift issue tracker for this now here: https://bugs.swift.org/browse/SR-324
What's causing this error?
The error doesn't happen until you get to the last closure:
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
Obviously, you're throwing an exception here, and I suspect that the problem has less to do with iterating over the children and more to do with throwing an exception while you're doing that iterating. I tried calling c without iterating:
public func trythis() {
let foo = Foo()
do {
try (foo.c)()
}
catch MyError.SomeError(let id) {
print(id)
}
catch { print("unknown") }
}
trythis()
and found that it worked fine. I also tried removing the throw from c:
var c:MyType = { () throws -> Bool in
print("C")
// throw MyError.SomeError(code: 0)
return true
}
and found that the code works fine in that case. So it's the combination of throwing while iterating over the list that's the problem, and that makes me suspect that it's just a compiler bug or maybe some problem with the Mirror class.
I think you should file a bug report with Apple for this one.
I agree with Caleb that this must be a bug.
But just to be clear, it is not the combination of throwing while iterating. It is the combination of reflecting and throwing.
This is a modified version of your handle function:
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
// Extract only the last function, no iteration...
if let callable = mirror.children[AnyForwardIndex(2)].value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
This function will cause the same error as your function.
You simply can not call a function that throws, if found using reflection.
Bug I would say.

Can't initialize constant in Swift Structure while handling initialization errors

Title is pretty self explanatory. Here's the code:
struct APIConnection {
enum APIConnectionError: ErrorType {
case InvalidRegion(invalidRegion:String)
}
let validRegions:Set<String> = ["na","eune","euw","kr","lan","las","br","oce","ru"]
let apiKey:String, region:String
var url:NSURL!
init(region:String,apiKey:String) {
func setRegion() throws {
if self.validRegions.contains(region) { self.region = region } // Can't assing since region is a let constant
else { throw APIConnectionError.InvalidRegion(invalidRegion: region) }
}
do { try setRegion() } /* If I change region to a var, compiler says setRegion() utilizes a yet non-initialized
apiKey constant. */
catch APIConnectionError.InvalidRegion(let invalidRegion) { print("Invalid region: \(invalidRegion)") }
catch let unknownError { print("Unknown error: \(unknownError)") }
}
}
I can't finish the initializer properly, since it won't assign self.region to the initializer's region constant inside the setRegion function.
apiKey also produces me a strange bug: It apparently is copied when I execute the setRegion function, not allowing the latter to be executed since it hasn't been initialized yet; though I didn't use it anywhere in my code yet.
Edit: Rewrote this part of the code:
func isRegionValid(region:String) throws -> Bool{
if self.validRegions.contains(region) { return true } // Can't assing since region is a let constant
else { throw APIConnectionError.InvalidRegion(invalidRegion: region) }
}
do { if try isRegionValid(region) { self.region = region } }
However, I still get an error saying the closure 'setRegion()' is capturing api-key's value, and I don't know why.