Posts

Post not yet marked as solved
3 Replies
1.1k Views
I saw that the macOS 14 SDK added this: @available(*, unavailable) extension NSImage : @unchecked Sendable { } Do I interpret this correctly to mean that NSImage is not Sendable and this extension’s sole purpose is to document this fact? I understand why NSImage isn’t safe to use from multiple threads at the same time. But what is the best way to pass around images in async contexts, then? CGImage is Sendable and is a fine substitute for pixel-based images, but what about PDFs? Is the only feasible way to create a PDF-based NSImage by doing everything on the main thread? (Except maybe load the file into a Data?) On the other hand, UIImage is Sendable, but I suppose that is because it uses a CGImage under the hood?
Posted Last updated
.
Post not yet marked as solved
0 Replies
924 Views
I’m trying to use a ShareLink to share a PDF file. When using the file URL to the PDF directly, everything looks as I would expect: let fileURL = URL(filePath: "…") ShareLink(item: fileURL) But in my actual app, the PDF isn’t available right away and is expensive to create, so I would like to only create it on demand. The documentation on FileRepresentation sounds to me like this is a use case for a custom type that implements Transferable with a FileRepresentation as its transferRepresentation. Before adding all the actual code to generate the PDF, I’m now trying to write a simple type that wraps a file URL and leads to the same results as using the URL directly. And I’m failing miserably. This is what I tried: struct PDFFile: Transferable { var fileURL: URL static var transferRepresentation: some TransferRepresentation { FileRepresentation(exportedContentType: .pdf) { pdfFile in SentTransferredFile(pdfFile.fileURL) } } } …and then in the view: let fileURL = URL(filePath: "…") let pdfFile = PDFFile(fileURL: fileURL) ShareLink( item: pdfFile, preview: SharePreview(fileURL.deletingPathExtension().lastPathComponent, image: Image(systemName: "doc.richtext"), icon: Image(systemName: "doc")) ) Why are no sharing services showing up? Why is there no icon? I also tried using .fileURL as the exportedContentType, which then makes the sharing services show up, but actually sharing the file leads to an empty file being shared whose filename is garbage (it looks like the system tries to decode the PDF data as a UTF-8 string and use that as the filename). So my question is: Is it possible to replicate the behavior of sharing a file URL with a custom type that asynchronously generates the data to share?
Posted Last updated
.
Post not yet marked as solved
4 Replies
687 Views
Consider this example: struct IdentifiedValue<T> { typealias ID = UUID var id: ID var value: T } class ValueStore { func add<T>(_ value: T) -> IdentifiedValue<T>.ID { let id = nextID() let identifiedValue = IdentifiedValue(id: id, value: value) // Do something with identifiedValue… return id } } What this add(_:) method does is that it gets a UUID from somewhere, passes it into the IdentifiedValue initializer and then return that ID. What is the best way to turn this add(_:) into a method that takes multiple parameters? I can’t just add the each and repeat keywords because then the return statement isn’t correct (obviously): func add<each T>(_ value: repeat each T) -> (repeat IdentifiedValue<each T>.ID) { let id = nextID() let identifiedValue = repeat IdentifiedValue(id: id, value: each value) // Do something with identifiedValue… return id // error } The only way I found was to get everything into a single line so the repeat and each keywords act on the whole line. This means adding a separate method that does the thing for a single value and calling that. At first, I tried adding both of the add(_:) variants and implementing the one that takes a pack by calling the one that takes a single value, but then the compiler says it doesn’t know which version of add(_:) to call. I could rename one of the two but I ended up using an inner function (where it isn’t confused, I guess due to shadowing) like this: func add<each T>(_ value: repeat each T) -> (repeat IdentifiedValue<each T>.ID) { func add<T>(_ value: T) -> IdentifiedValue<T>.ID { let id = nextID() let identifiedValue = IdentifiedValue(id: id, value: value) // Do something with identifiedValue… return id } return (repeat self.add(each value)) } So my question is: Is there way to write this without a second function?
Posted Last updated
.
Post marked as solved
2 Replies
812 Views
I have this code: struct Tweak<Value> { var name: String var value: Value } func name<Value>(of tweak: Tweak<Value>) -> String { return tweak.name } func names<Value1, Value2>(of tweak1: Tweak<Value1>, _ tweak2: Tweak<Value2>) -> (String, String) { return (tweak1.name, tweak2.name) } Now I want to implement the second function using parameter packs: func names<each Value>(of tweak: repeat Tweak<each Value>) -> (repeat String) { return repeat each tweak.name } But that gives this error: error: pack expansion 'String' must contain at least one pack reference func names<each Value>(of tweak: repeat Tweak<each Value>) -> (repeat String) { ^~~~~~~~~~~~~ What is the correct syntax for doing this? Or is it impossible to spell this? Thanks, Marco
Posted Last updated
.
Post marked as solved
1 Replies
1k Views
I’m trying to share CKRecords from my app’s CloudKit database using a CKShare from an NSSharingServicePicker, but the picker doesn’t show any sharing services. I’m pretty much doing exactly what the code samples in the WWDC22 video Enhance collaboration experiences with Messages show: let itemProvider = NSItemProvider() if let existingShare { itemProvider.registerCKShare(existingShare, container: container) } else { itemProvider.registerCKShare(container: container, preparationHandler: { return try await createAndSaveNewCKShare() }) } Then, I add some metadata as described in the video: let activityItem = NSPreviewRepresentingActivityItem( item: itemProvider,     title: "The title", imageProvider: nil, // TODO: Quick Look preview image iconProvider: .init(object: NSApp.applicationIconImage) ) Finally, I create the picker and show it: let picker = NSSharingServicePicker(items: [activityItem]) picker.show(relativeTo: .zero, of: view, preferredEdge: .minY)) What I then get is a sharing service picker that only shows my recent contacts, but not a single sharing service below that. The metadata is there, i.e. the title and app icon show up: What am I doing wrong here? I’m pretty confident that most of the code is correct because it works fine on iOS. There, I use the same itemProvider as above, but present the share UI like this, which is pretty much the same: let configuration = UIActivityItemsConfiguration(itemProviders: [itemProvider]) configuration.perItemMetadataProvider = { _, key in     switch key {     case .linkPresentationMetadata:         let metadata = LPLinkMetadata() metadata.title = title         metadata.imageProvider = nil // TODO: Quick Look preview image metadata.iconProvider = nil // TODO: App icon?     default:         return nil     } } let viewController = UIActivityViewController(activityItemsConfiguration: configuration)
Posted Last updated
.
Post not yet marked as solved
0 Replies
823 Views
The Mac app I’m working on stores documents in iCloud Drive. Showing an SWCollaborationView in the toolbar works when creating an NSItemProvider using the document’s fileURL like this: if let documentURL = document.fileURL { toolbarItem.view = SWCollaborationView(itemProvider: NSItemProvider(object: documentURL)) } This only works if a document was previously saved to disk and is then opened, though, because a new, unsaved document doesn’t have a fileURL yet. So this means I can’t show an SWCollaborationView for unsaved documents. What I don’t quite understand is what we are supposed to do in this situation. The current version of Pages (which doesn’t use SWCollaborationView yet, of course) seems to show a button for collaboration for new documents and when you click that, the document is saved to iCloud without user interaction before the popover opens. How would I go about doing this in my app? And here’s also a bonus question: Do I have to handle renaming/moving of the document manually? That is, does SWCollaborationView automatically handle the case where the document is renamed in Finder/Files? I’m curious because the SWCollaborationView references an NSItemProvider which in turn references an NSURL and it’s not clear to me if that should work. And if I have to do it manually: Does this mean I’ll have to create a new SWCollaborationView and set it on my toolbar item?
Posted Last updated
.
Post not yet marked as solved
1 Replies
976 Views
On iOS, when rendering an NSAttributedString with link attributes to a PDF using a CGContext, the resulting PDF contains the expected text and it automatically is rendered in blue with an underline to indicate it has a link, but there is no actual link there, i.e. it can’t be clicked. When running the equivalent code on macOS, the text does have the expected link (but it’s neither blue, nor underlined there 🤷‍♂️). Does anybody know if this is a known issue on iOS or what can be done to fix this? For Apple folks: FB8728485 This is the code I’m using: import Foundation import CoreGraphics #if os(macOS) import AppKit typealias Font = NSFont #elseif os(iOS) import UIKit typealias Font = UIFont #endif let outputBounds = CGRect(x: 0.0, y: 0.0, width: 400.0, height: 400.0) let string = NSAttributedString(string: "Link", attributes: [     .font: Font(name: "Helvetica", size: 20.0)!,     .link: URL(string: "https://apple.com")! ]) // // Setup context // let data = NSMutableData() let consumer = CGDataConsumer(data: data as CFMutableData)! var mediaBox = outputBounds let context = CGContext(consumer: consumer, mediaBox: &mediaBox, nil)! #if os(macOS) NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: false) #elseif os(iOS) UIGraphicsPushContext(context) #endif // // Render PDF // context.beginPDFPage(nil) string.draw(in: outputBounds) context.endPDFPage() context.closePDF() // // Write PDF to file // let fileURL = URL(fileURLWithPath: NSTemporaryDirectory().appending("/test.pdf")) try! data.write(to: fileURL, options: [.atomicWrite]) print("Path of generated PDF file:") print(fileURL.path)
Posted Last updated
.
Post marked as solved
1 Replies
5.8k Views
The app I’m working on uses a kernel extension and whenever users install or update the app, the kernel cache must be rebuilt. This is done using `/usr/sbin/kextcache -v 6 -invalidate /` which has worked fine (-ish) for quite a few years. But starting with the macOS Catalina betas up to and including the public release of 10.15 (i.e. now) this doesn’t work anymore.It appears that *triggering* a kext cache update does indeed work using either the above command or by using `touch /Library/Extensions` (as the man page for kextcache suggests). I know this because in Activity Monitor, `kextd` runs with high CPU usage for quite a while. But it seems that the actual result of that update is never written to the appropriate places on disk. The end result is that the previous stale kext cache is used when the computer starts up the next time.In the case of the app I’m working on that means that a user space component complains about a version mismatch and instructs the user to get help from our support. And we see a lot of these reports.Unfortunately, the only way to fix this seems to be to restart into Recovery Mode and convince the system to perform the kext cache update from there. This does indeed work, probably because System Integrity Protection is not active in Recovery Mode.Has anybody else encountered this problem? Is there any solution for this?(And yes, the kext is properly signed with an appropriate certificate. And yes, this was reported to Apple: FB7340720)Edit: Here’s a blog post that details our findings about how updating the kernel works and where it breaks down on macOS Catalina: https://blog.obdev.at/how-kernel-prelininkg-works-on-macos-catalina/
Posted Last updated
.