Hello I'm a beginner to Swift Concurrency and have run into an issue with AsyncStream. I've run into a situation that causes an observing of a for loop to receiving a values from an AsyncStream.
At the bottom is the code that you can copy it into a Swift Playground and run.
The code is supposed to mock a system that has a service going through a filter to read and write to a connection.
Here is a log of the prints
🙈🫴 setupRTFAsyncWrites Start
⬅️ Pretend to write 0
➡️ Pretend to read 0
feed into filter 0
yield write data 1
🙈🫴 setupRTFAsyncWrites: write(1 bytes)
⬅️🙈🫴 Async Event: dataToDevice: 1 bytes
⬅️ Pretend to write 1
➡️ Pretend to read 1
feed into filter 1
yield write data 2
// here our for loop should have picked up the value sent down the continuation. But instead it just sits here.
Sample that can go into a playground
//: A UIKit based Playground for presenting user interface
import SwiftUI
import PlaygroundSupport
import Combine
import CommonCrypto
import Foundation
class TestConnection {
var didRead: ((Data) -> ()) = { _ in }
var count = 0
init() {
}
func write(data: Data) {
// pretend we sent this to the BT device
print("⬅️ Pretend to write \(count)")
Task {
try await Task.sleep(ms: 200)
print("➡️ Pretend to read \(self.count)")
self.count += 1
// pretend this is a response from the device
self.didRead(Data([0x00]))
}
}
}
enum TestEvent: Sendable {
/// ask some one to write this to the device
case write(Data)
/// the filter is done
case handshakeDone
}
class TestFilter {
var eventsStream: AsyncStream<TestEvent>
var continuation: AsyncStream<TestEvent>.Continuation
private var count = 0
init() {
(self.eventsStream, self.continuation) = AsyncStream<TestEvent>.makeStream(bufferingPolicy: .unbounded)
}
func feed(data: Data) {
print("\tfeed into filter \(count)")
count += 1
if count > 5 {
print("\t✅ handshake done")
self.continuation.yield(.handshakeDone)
return
}
Task {
// data delivered to us by a bluetooth device
// pretend it takes time to process this and then we return with a request to write back to the connection
try await Task.sleep(ms: 200)
print("\tyield write data \(self.count)")
// pretend this is a response from the device
let result = self.continuation.yield(.write(Data([0x11])))
}
}
/// gives the first request to fire to the device for the handshaking sequence
func start() -> Data {
return Data([0x00])
}
}
// Here we facilitate communication between the filter and the connection
class TestService {
private let filter: TestFilter
var task: Task<(), Never>?
let testConn: TestConnection
init(filter: TestFilter) {
self.filter = filter
self.testConn = TestConnection()
self.testConn.didRead = { [weak self] data in
self?.filter.feed(data: data)
}
self.task = Task { [weak self] () in
await self?.setupAsyncWrites()
}
}
func setupAsyncWrites() async {
print("🙈🫴 setupRTFAsyncWrites Start")
for await event in self.filter.eventsStream {
print("\t\t🙈🫴 setupRTFAsyncWrites: \(event)")
guard case .write(let data) = event else {
print("\t\t🙈🫴 NOT data to device: \(event)")
continue
}
print("\t\t⬅️🙈🫴 Async Event: dataToDevice: \(data)")
self.testConn.write(data: data)
} // for
// This shouldn't end
assertionFailure("This should not end")
}
public func handshake() async {
let data = self.filter.start()
self.testConn.write(data: data)
await self.waitForHandshakedone()
}
private func waitForHandshakedone() async {
for await event in self.filter.eventsStream {
if case .handshakeDone = event {
break
}
continue
}
}
}
Task {
let service = TestService(filter: TestFilter())
await service.handshake()
print("Done")
}
/*
This is what happens:
🙈🫴 setupRTFAsyncWrites Start
⬅️ Pretend to write 0
➡️ Pretend to read 0
feed into filter 0
yield write data 1
🙈🫴 setupRTFAsyncWrites: write(1 bytes)
⬅️🙈🫴 Async Event: dataToDevice: 1 bytes
⬅️ Pretend to write 1
➡️ Pretend to read 1
feed into filter 1
yield write data 2
// It just stops here, the `for` loop in setupAsyncWrites() should have picked up the event sent down the continuation after "yield write data 2"
// It should say
🙈🫴 setupRTFAsyncWrites: write(1 bytes)
⬅️🙈🫴 Async Event: dataToDevice: 1 bytes
*/
extension Task<Never, Never> {
public static func sleep(ms duration: UInt64) async throws {
try await Task.sleep(nanoseconds: 1_000_000 * duration)
}
}
Swift
RSS for tagSwift is a powerful and intuitive programming language for Apple platforms and beyond.
Posts under Swift tag
27 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I've been struggling to get a ValueTransformer to work while developing in Xcode 16 for iOS 18. Despite thinking I had everything set up correctly, I keep encountering the following error whenever I create a tag:
let tag = Tag(name: name, color: color.toPlatformColor()) // Converts it to NSColor or UIColor
modelContext.insert(tag)
SwiftData/DataUtilities.swift:184: Fatal error: Unable to determine the primitive for Attribute - name: color, options: [transformable with Optional("ColorTransformer")], valueType: UIColor, defaultValue: UIExtendedSRGBColorSpace 0 0 1 1, hashModifier: nil
Here’s what I’m dealing with:
Tag Model:
@Model
public final class Tag: Identifiable {
var name: String = ""
@Attribute(.transformable(by: ColorTransformer.self))
var color: PlatformColor = PlatformColor.blue
init(name: String, color: PlatformColor) {
self.name = name
self.color = color
}
}
ColorTransformer:
final class ColorTransformer: ValueTransformer {
override func transformedValue(_ value: Any?) -> Any? {
guard let color = value as? PlatformColor else { return nil }
do {
let data = try NSKeyedArchiver.archivedData(
withRootObject: color, requiringSecureCoding: true)
return data
} catch {
assertionFailure("Failed to transform `PlatformColor` to `Data`")
return nil
}
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return PlatformColor.black }
do {
let color = try NSKeyedUnarchiver.unarchivedObject(
ofClass: PlatformColor.self, from: data as Data)
return color
} catch {
assertionFailure("Failed to transform `Data` to `PlatformColor`")
return nil
}
}
override class func transformedValueClass() -> AnyClass {
return PlatformColor.self
}
override class func allowsReverseTransformation() -> Bool {
return true
}
public static func register() {
ValueTransformer.setValueTransformer(ColorTransformer(), forName: .colorTransformer)
}
}
extension NSValueTransformerName {
static let colorTransformer = NSValueTransformerName(rawValue: "ColorTransformer")
}
Platform Alias:
#if os(macOS)
typealias PlatformColor = NSColor
#else
typealias PlatformColor = UIColor
#endif
The ValueTransformer is registered when the ModelContainer is created at app startup:
var sharedModelContainer: ModelContainer = {
ColorTransformer.register()
// Other configurations...
}()
I've also tried not aliasing the colors to see if that changes anything, but I still encounter the same issue. Any guidance or suggestions would be greatly appreciated!
Hello all, I am playing with MapKit for SwiftUI, so far so good. There is one thing I have not seen any documentations, or sample codes around and that's elevation data, e.g.
My questions are:
Is there a way to get this information from an MKRoute?
Is it possible to get the elevation gain/drop at a given point in the route?
Many thank in advance for your help.
The code below is a simplified form of part of my code for my Swift Package Manager, Swift 5.6.1, PromiseKit 6.22.1, macOS command-line executable.
It accepts a Mac App Store app ID as the sole argument. If the argument corresponds to an app ID for an app that was installed from the Mac App Store onto your computer, the executable obtains some information from Spotlight via a NSMetadataQuery, then prints it to stdout.
I was only able to get the threading to work by calling RunLoop.main.run(). The only way I was able to allow the executable to return instead of being stuck forever on RunLoop.main.run() was to call exit(0) in the closure passed to Promise.done().
The exit(0) causes problems for testing. How can I allow the executable to exit without explicitly calling exit(0), and how can I improve the threading overall?
I cannot currently use Swift Concurrency (await/async/TaskGroup) because the executable must support macOS versions that don't support Swift Concurrency. A Swift Concurrency solution variant would be useful as additional info, though, because, sometime in the future, I might be able to drop support for macOS versions older than 10.15.
Thanks for any help.
import Foundation
import PromiseKit
guard CommandLine.arguments.count > 1 else {
print("Missing adamID argument")
exit(1)
}
guard let adamID = UInt64(CommandLine.arguments[1]) else {
print("adamID argument must be a UInt64")
exit(2)
}
_ = appInfo(forAdamID: adamID)
.done { appInfo in
if let jsonData = try? JSONSerialization.data(withJSONObject: appInfo),
let jsonString = String(data: jsonData, encoding: .utf8)
{
print(jsonString.replacingOccurrences(of: "\\/", with: "/"))
}
exit(0)
}
RunLoop.main.run()
func appInfo(forAdamID adamID: UInt64) -> Promise<[String: Any]> {
Promise { seal in
let query = NSMetadataQuery()
query.predicate = NSPredicate(format: "kMDItemAppStoreAdamID == %d", adamID)
query.searchScopes = ["/Applications"]
var observer: NSObjectProtocol?
observer = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSMetadataQueryDidFinishGathering,
object: query,
queue: .main
) { _ in
query.stop()
defer {
if let observer {
NotificationCenter.default.removeObserver(observer)
}
}
var appInfo: [String: Any] = [:]
for result in query.results {
if let result = result as? NSMetadataItem {
var attributes = ["kMDItemPath"]
attributes.append(contentsOf: result.attributes)
for attribute in attributes {
let value = result.value(forAttribute: attribute)
switch value {
case let date as Date:
appInfo[attribute] = ISO8601DateFormatter().string(from: date)
default:
appInfo[attribute] = value
}
}
}
}
seal.fulfill(appInfo)
}
DispatchQueue.main.async {
query.start()
}
}
}
Is this Relationship correct? Does this cause a circular reference? This runs on 18 but crashes on 17 in the swift data internals.
@Model
final class Item {
@Attribute(.unique) var id: UUID
var date: Date
@Relationship(deleteRule: .nullify, inverse: \Summary.item) var summary: Summary?
init(date: Date = Date.now) {
self.id = UUID()
self.date = Calendar.current.startOfDay(for: date)
self.summary = Summary(self)
}
}
@Model
final class Summary {
@Attribute(.unique) var id = UUID()
@Relationship var item: Item?
init(_ item: Item) {
self.item = item
}
}
I currently do FaceID validation in my apps but it looks like Apple is offering FaceID ad the App level. Does this mean we still need to or can code for it in iOS 18 apps? Right now I've been working on migrating to iOS 18 using beta but my swift code just returns an "unknown error". From a developer perspective I can't find any examples or guidance on how handle FaceID currently in iOS 18 or going forward.
Anyone have any insights or resources.
This is the code that used to work but now under iOS 18 returns the error. Maybe the simulator and swift have not caught up but I don't think so given that it's been two beta release that I know of where this has not worked.
class biometric {
class func authenticateUser() async -> (Bool, Error?) {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let biometryType = context.biometryType
var reason = "Authenticate with \(biometryType)"
if biometryType == .faceID {
reason = "Authenticate with Face ID"
} else if biometryType == .touchID {
reason = "Authenticate with Touch ID"
}
do {
let success = try await context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason)
LogEvent.print(module: "Authentication.biometric.authenticateUser", message: "Biometric authentication. success: \"\(success)\".")
return (success, nil)
} catch let evaluationError as LAError {
LogEvent.print(module: "Authentication.biometric.authenticateUser", message: "Biometric authentication failed. evaluationError: \"\(evaluationError.localizedDescription)\"")
handleEvaluationError(evaluationError)
I do get past the .canEvaluatePolicy but fail on the .evaluatePolicy
I have noticed a strange bug with TabView in the settings window of an macOS app. When text is typed into a textbox, the icon jiggles a little bit, but ONLY on a non-retina monitor (1920x1200 in my test). Some icons jiggle, others don’t. Below is code for the gearshape which exhibits this behavior. “sparkle” doesn’t. Very odd. Tried on macOS 15.0.1, Xcode 16.
You can see it in action here
https://imgur.com/a/huCw7sN
// App file
@main
struct TabViewJiggleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
Settings {
SettingsView()
}
}
}
// Settings view
struct SettingsView: View {
@State private var input: String = ""
var body: some View {
TabView {
TextField("Enter text", text: $input)
.tabItem {
Label("General", systemImage: "gearshape")
.font(.system(size: 20))
}
}
}
}