Swift is a powerful and intuitive programming language for Apple platforms and beyond.

Posts under Swift tag

27 Posts

Post

Replies

Boosts

Views

Activity

AsyncStream stops dispatching
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) } }
1
0
497
Oct ’24
ValueTransformer: Unable to determine the primitive for Attribute Error
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!
0
1
323
Oct ’24
MapKit elevation information for a route
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.
1
1
661
Oct ’24
NSMetadataQuery threading issues
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() } } }
7
0
926
Oct ’24
SwiftData relationship crash on 17.x
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 } }
6
0
777
Oct ’24
FaceID changes in iOS 18
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
12
1
4.2k
Oct ’24
SwiftUI SF Symbols bug?
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)) } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1
0
353
Oct ’24