Hello everyone
I am looking to build a simple app for displaying a spatial video using the quick look preview API. I have been following this video which is useful:
https://developer.apple.com/videos/play/wwdc2024/10166/#:~:text=QuickLook%20is%20the%20system%20standard,just%20like%20the%20Photos%20app.
I am new to building apps in Xcode, and I could do with some advice on how to build the rest of the project mentioned in the above video. I was wondering if there is source code or a project example available anywhere for an app the uses the Quick Look preview API?
QuickLook
RSS for tagCreate previews of files to use inside your app or perform simple edits on previews using QuickLook.
Posts under QuickLook tag
45 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
When opening the 3D model in Augmented Reality on Safari, the model initially displays correctly. However, when attempting to move, resize, or rotate it, the device screen freezes along with the Augmented Reality view, and sometimes it crashes the application. This same model does not have any issues when opened in Chrome.
Has anyone experienced something similar, and if so, do you know how to resolve it?
QLPreviewController's PencilKit doesn't get mirrored when used with a RTL language in iOS18b3.
Moreover, the sub-menu of the actions is not translated and is shown in English.
Steps to reproduce:
Set an app with QLPreviewController and set its app language to Hebrew (or any other RTL language)
Run the app
Tap the Markup button on bottom-left side.
Look on the PencilKit element.
Tap the + button to show the annotation actions.
Look on the annotation actions menu.
Current: The Pencil Kit element is not mirrored when app language is RTL Language and the sub-menu actions are shown in English.
Expected: The Pencil Kit element is mirrored when app language is RTL Language and the sub-menu actions are shown in the RTL Language.
Submitted Feedback: FB14452847
Notes:
This wasn't reproducible in iOS 17.5.1
The top bar buttons are mirrored as expected with a RTL language.
Hi,
I have a Spatial Video that I am trying to load in a visionOS app with PreviewApplication API
let url = URL(string: "https://mauiman.azureedge.net/videos/SpatialJourney/watermelon_cat.MOV")
let item = PreviewItem(url: url!)
_ = PreviewApplication.open(items: [item])
When I run the application, I am getting the following error. Did I miss anything?
QLUbiquitousItemFetcher: <QLUbiquitousItemFetcher: 0x6000022edfe0> could not create sandbox wrapper. Error: Error Domain=NSPOSIXErrorDomain Code=2 "couldn't issue sandbox extension com.apple.quicklook.readonly for '/videos/SpatialJourney/watermelon_cat.MOV': No such file or directory" UserInfo={NSDescription=couldn't issue sandbox extension com.apple.quicklook.readonly for '/videos/SpatialJourney/watermelon_cat.MOV': No such file or directory} #PreviewItem
The screen shows up as:
Putting the spatial video locally, I get the following error:
let url = URL(fileURLWithPath: "watermelon_cat.MOV")
let item = PreviewItem(url: url)
_ = PreviewApplication.open(items: [item])
Error getting the size of file(watermelon_cat.MOV -- file:///) with error (Error Domain=NSCocoaErrorDomain Code=260 "The file “watermelon_cat.MOV” couldn’t be opened because there is no such file." UserInfo={NSURL=watermelon_cat.MOV -- file:///, NSFilePath=/watermelon_cat.MOV, NSUnderlyingError=0x600000ea1650 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}) #Generic
Any help is greatly appreciated. Thank you in advance.
There seem to be a regression in QLPreviewController with iOS17, since it works in both iOS16(latest) and iOS18b3.
After following the steps (described below), using iOS17.X the QLPreviewController gets frozen up so all of it's top buttons are no tappable, although you can still draw on the canvas.
Steps to reproduce:
Setup QLPreviewController to preview a single image in png format.
The QLPreviewController should be presented as the rootViewController of a UINavigationController.
Present the UINavigationController that has the QLPreviewController as its rootViewController.
Tap the Markup button.
Pick the leftest tool from the PencilKit tools at the bottom.
Draw a curvy long line from the bottom-right corner of the canvas, to the top-left corner of the canvas.
Tap any of the top buttons.
Current: The top buttons are not tappable.
Expected: The top buttons are tappable.
Any idea how to workaround/fix this issue?
Notes:
The issue seems to be reproducible in iOS17, and isn't reproducible in iOS16 or iOS18b3 using Xcode 15.4 and Xcode 16b3.
The issue is reproducible in both iOS17 Simulator or devices.
The issue is reproducible in both UIKit and SwiftUI.
Possible same issue was mentioned here: https://stackoverflow.com/questions/78090416/navigation-toolbar-buttons-stop-working-after-keyboard-is-shown-on-ios-17-sw
I couldn't attach a sample project to here due to file format limitation, but you can use the project from this repository and use a png instead of a pdf: https://github.com/NilCoalescing/SwiftUI-Code-Examples/tree/main/QuickLook/SwiftUIQuickLookInUINavigationController
(code by Natalia Panferova)
Attached is the image of the drawing done in iOS17.5 Simulator.
I'm having a problem where any buttons won't be shown when displaying a QLPreviewController with swift.
With .quickLookPreview() these buttons are shown, including share, done, annotation etcetera.
The buttons are not shown when using QLPreviewController with SwiftUI, however if used with other UIKit they are shown.
How to fix this? I'm not really looking forward in implementing all of the annotation functions and other QuickLook capabilities again.
The ultimate goal of mine is to allow annotation and other pdf editing with the ability to save the file via the Done button, but on .quickLookPreview() the Done button displays "Save to files" and "Discard" and on QLPreviewController the button is not shown.
The issue has been addressed on several forums, yet a solution has not been found.
Reproducement in nutshell:
new QLPreviewController conforming to UIViewControllerRepresentable
display the QLPreviewController in SwiftUI view
Thanks.
QLPreview keeps blinking and stops previewing the file when opening some high-resolution PDF files with heavy content. This can only be simulated in a real device built in xcode simulators cant simulate the issue.
Tested in Xcode Version 15.4 (15F31d) and iPadOS Version 17.5.1.
Following is My Sample code :
import UIKit
import QuickLook
class ViewController: UIViewController {
var currentViewFilePath = ""
let preview = QLPreviewController()
override func viewDidLoad() {
super.viewDidLoad()
self.setupQLPreview()
loaddata()
// Do any additional setup after loading the view.
}
func loaddata(){
if let fileURL = Bundle.main.url(forResource: "CD28048D", withExtension: "pdf") {
self.currentViewFilePath = fileURL.relativePath
self.preview.reloadData()
// Use the fileURL here
print("File URL: \(fileURL)")
} else {
print("File not found")
}
}
}
extension ViewController:QLPreviewControllerDelegate,QLPreviewControllerDataSource{
func setupQLPreview(){
preview.delegate = self
preview.dataSource = self
self.view.addSubview(preview.view)
let previewhight = (UIScreen.main.bounds.height - ((self.navigationController?.navigationBar.frame.height ?? 0)))
preview.view.translatesAutoresizingMaskIntoConstraints = false
preview.view.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
preview.view.leftAnchor.constraint(equalTo: self.view.leftAnchor,constant: 4).isActive = true
preview.view.rightAnchor.constraint(equalTo: self.view.rightAnchor,constant: -4).isActive = true
preview.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,constant: 4).isActive = true
preview.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,constant: -4).isActive = true
preview.view.widthAnchor.constraint(equalToConstant: self.view.frame.width-8).isActive = true
preview.view.heightAnchor.constraint(equalToConstant: previewhight-4).isActive = true
preview.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
preview.navigationController?.isNavigationBarHidden = true
self.addChild(preview)
preview.didMove(toParent: self)
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
if self.currentViewFilePath.isEmpty || self.currentViewFilePath == ""{
return 0
}else{
return 1
}
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
let fileUrl = URL(fileURLWithPath: self.currentViewFilePath)
return fileUrl as QLPreviewItem
}
}
while running the above code Xcode debugger prints following errors:
View service did terminate with error:
Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted} #Remote
Preview collection viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted} #Remote
[u 0AA9D4C5-BBA1-56F6-8FFF-F5F56B99399A:m (null)] [com.apple.quicklook.extension.previewUI(1.0)] Connection to plugin invalidated while in use.
I'm stuck on a problem where I need to be able to have the same editing capabilities as in .quickLookPreview and be able to save the edited file to the application with the "Done" button.
So, in nutshell, I need to implement the same functionality many other applications provide including Apple's Files.
However with .quickLookPreview I don't get the ability to save edited files directly to the application, and I've had no luck finding help from the internet (thus this question).
Perhaps somebody has implemented this before and could give me a lead somewhere?
PS. I'm trying to find a solution without any third party libraries
I'm developing an application in which saving files and opening them with .quickLookis in important role.
However, after putting my application to TestFlight and letting a friend of mine test it with his phone (iOS 17.4.1, iPhone 15 Pro) it appears that sometimes when opening a file the QuickLook sheet doesn't open until the app's focus is lost.
@State
Button("Open file") {
url = someFileUrl
}.quickLookPreview($url)
this week i was watching https://developer.apple.com/videos/play/wwdc2024/10105/
with the amazing "configuration" feature to change the color or mesh straight in quick look, but i tried a lot with goarounds but nothing bring me to success
how do i write in the usda files?
anytiome i overwrite the usda even with just a "{}" inside... Reality composer pro rejects the file to be open again
where is the developer man in the tutorial writing the usda?
how is the usda compressed in usdz? (none of the compressors i tried accepeted the modified usda file)
this is the code it's suggested in the video
#usda 1.0
(
defaultPrim = "iPhone"
)
def Xform "iPhone" (
variants = {
string Color = "Black_Titanium"
}
prepend variantSets = ["Color"]
)
{
variantSet "Color" = {
"Black_Titanium" { }
"Blue_Titanium" { }
"Natural_Titanium" { }
"White_Titanium" { }
}
}
but i dont understand how to do it with my own files,
It is pretty cool that you can preview Spatial Photos and Videos with just PreviewApplication.push(...), but the issue is, you can disable the editing, but buttons to export content to Files still going to be there.
So if my application uses this to showcase some proprietary or licensed content in my application, I cannot really disable an option of exporting it from my app. And another, more major issue, a lot of developers would not even know that user can save Previewed item, as this Save to Files button is not that easy to find.
We have QuickLook thumbnailing framework which is used to provide thumbnails for custom file types. However, how can we provide thumbnails for system defined file types for eg. image file types?
Is there any way to achieve this?
Though I cannot find any documentation, it seems that UIPasteboard cannot be used from a Quick Look Preview app extension.
I have such an extension, which contains a view that supports copying text as follows:
- (IBAction)copy:(nullable id)sender {
UIPasteboard * pboard = UIPasteboard.generalPasteboard;
pboard.string = _rep.text;
}
This is invoked from a context menu (edit menu) item.
This works fine In the simulator, but on device the pasteboard remains empty and errors like the following are emitted:
-[PBServerConnection pasteboardWithName:createIfNeeded:authenticationBlock:dataOwnerBlock:error:] failed with error: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.pasteboard.pasted was invalidated: failed at lookup with error 159 - Sandbox restriction." UserInfo={NSDebugDescription=The connection to service named com.apple.pasteboard.pasted was invalidated: failed at lookup with error 159 - Sandbox restriction.}
It's unclear to me why such functionality would be problematic and necessary to block.
It would be nice if this were documented clearly, as I wasted a lot of time trying to figure out why this was not working.
(And no, I have not filed a feedback report or TSI yet, as I'm presently very short on time, and I don't have a sample project prepared to demonstrate the issue.)
I am using QlPreviewController in my applications, I am download the data one by one using observer, The observer detects currentIndexItem in the preview controller, The image is only download when the currentIndexItem will changed ,After downloading i will refresh the currentPreviewItem , My Problem is , while downloading an files in first item and i am moving to the next items , which also downloading an files,And both screens are freezing , after click the menu button, in the sections show images is loaded , but not reflected.
While i am calling download data, The thread is changed to Background thread
DispatchQueue.global().async {
interactor.processData(kbPath: categoriesPath)
}
}else{
interactor.processData(kbPath: categoriesPath)
}
This is the downloading file which will happens in Background thread, The above code will change
let url = fileData.contentUrl
let fileName = fileData.name
let contentUrl = fileData.contentUrl
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destinationURL = documentsURL.appendingPathComponent(fileName)
// Check if the file already exists at the destination URL
if FileManager.default.fileExists(atPath: destinationURL.path){
return completion(.success(File(data: destinationURL, name: fileName, contentUrl: contentUrl, hasData: true)))
}
// If the file doesn't exist, proceed with downloading
let request = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.downloadTask(with: request) {(tempFileURL, response, error) in
if let error = error {
print("Error downloading file: \(error)")
completion(.failure(.FailedToDownload))
return
}
guard let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode == 200,let tempFileURL = tempFileURL else {
print("Failed to download file. Invalid response.")
completion(.failure(.FailedToDownload))
return
}
do {
try FileManager.default.moveItem(at: tempFileURL, to: destinationURL)
completion(.success(File(data: destinationURL, name: fileName, contentUrl: url, hasData: true)))
} catch {
print("Error moving file: \(error)")
completion(.failure(.FailedToDownload))
}
}
task.resume()
}
After Downloading the data , it will inform to the viewcontroller to check the thread and call
DispatchQueue.main.async {
handler()
}
}else{
handler()
}
This is my Observer
if context == &previewControllerContext{
if keyPath == "currentPreviewItemIndex" {
let currentIndex = preview.currentPreviewItemIndex
//api calls for the specific files
if currentIndex < previewData.count && !previewData[currentIndex].hasData {
self.loadingView.startAnimating()
let builder = Builder()
self.previewData[currentIndex].hasData = true
builder.build(instance: self, categoriesPath: .KBDownloadFile(fileData: self.previewData[currentIndex]))
}
}
}else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
After downloaded image the result will notify and i will refresh the current previewItem self.preview.refreshCurrentPreviewItem()
The Issue video link -> https://drive.google.com/file/d/18pBZawbvcUU3SA5vS53X9R9q-AGMG5Sv/view
Any one facing this issues with this ,my thought this issues is thread based , but i will handle the thread
Hi all,
I have created a QuickLook Preview for my custom datatype in my app.
I use SwiftUI wrapped in UIKit for the preview. My issue is that when I try and play audio using AVAudioPlayer, I receive a status code 50 error.
Does anyone know if there are seperate permissions I need to request before being able to do this?
Here are the errors I get while trying to set my audio session as active and play on the avaudioplayer
Thanks for your help and advice!
The operation couldn’t be completed. (OSStatus error -50.)
nwi_state: registration failed (9)
connection <connection: 0x100e0b270> { name = com.apple.audio.AudioQueueServer, listener = false, pid = 0, euid = 4294967295, egid = 4294967295, asid = 4294967295 } : error <dictionary: 0x251524530> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x2515246c8> { length = 18, contents = "Connection invalid" }
}
auto-cancelling <connection: 0x100e0b270> { name = com.apple.audio.AudioQueueServer, listener = false, pid = 0, euid = 4294967295, egid = 4294967295, asid = 4294967295 }
0x2816bf680 reply: XPC_ERROR_CONNECTION_INVALID
throwing swix::exception: !(is_valid())
AQ_API_V2Impl.cpp:134 AudioQueueNew: <-AudioQueueNew failed -302
rebuilding null connection
0x2816bf680 reply: XPC_ERROR_CONNECTION_INVALID
connection <connection: 0x100822a90> { name = com.apple.audio.AudioQueueServer, listener = false, pid = 0, euid = 4294967295, egid = 4294967295, asid = 4294967295 } : error <dictionary: 0x251524530> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x2515246c8> { length = 18, contents = "Connection invalid" }
}
throwing swix::exception: !(is_valid())
auto-cancelling <connection: 0x100822a90> { name = com.apple.audio.AudioQueueServer, listener = false, pid = 0, euid = 4294967295, egid = 4294967295, asid = 4294967295 }
AQ_API_V2Impl.cpp:134 AudioQueueNew: <-AudioQueueNew failed -302
Hello,
I am trying to write a simple QuickLook generator, which takes a file, runs an external binary to produce the data, and then creates and shows an image in a preview. The input files are uncommon data formats, and the binary in this case converts these to PNG.
I have tried multiple methods and followed many discussions on these forums and StackOverflow, but unfortunately I am yet to get it working. That said, the program I have builds and runs using the qlmanage tool, however when running in Finder or on the server qlmanage -x the program fails.
As I understand, it when using QuickLook in Finder it calls the QuickLook daemon quicklookd which is also called when using the qlmanage -x CLI command, but please correct me if I am wrong about this.
The basic idea for all generator functions is:
create NSTask or Process and run the third-party binary, using the requested file path as input
run the task, pipe output, and read output
convert the piped output to NSImage
show image
Attempt 1 (old-style qlgenerator plugin in Obj-C):
Initially I tried adding the binary as an asset to the QuickLook Preview target and the generator would work as mentioned using qlmanage, however it would not work using qlmanage -x or from Finder.
Here is the preview function:
OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
{
NSLog(@"QuickLook plugin started");
NSInteger width = 512;
NSURL *fileURL = (__bridge NSURL *)url;
NSString *escapedFilePath = [fileURL.path stringByReplacingOccurrencesOfString:@" " withString:@"\\ "];
NSLog(@"Original Path: %@", fileURL.path);
NSLog(@"Escaped Path: %@", escapedFilePath);
NSBundle *bundle = [NSBundle bundleWithIdentifier:@"com.pjharrison.qlgwyddion"];
NSURL *nsURLExecutable = [bundle URLForAuxiliaryExecutable:@"gwyddion-thumbnailer"];
if (nsURLExecutable) {
// The resource was found, and nsURLData is not nil.
// Proceed with using nsURLData.
NSLog(@"Resource 'gwyddion-thumbnailer' found in the bundle.");
} else {
// The resource was not found. Handle the situation accordingly.
NSLog(@"Resource 'gwyddion-thumbnailer' not found in the bundle.");
}
NSTask *task = [[NSTask alloc] init];
NSLog(@"Pathhhh %@", nsURLExecutable.path);
[task setExecutableURL:nsURLExecutable];
[task setArguments:@[@"kde4", [@(width) stringValue], escapedFilePath]];
NSLog(@"Task set");
NSString *argumentString = [task.arguments componentsJoinedByString:@" "];
NSLog(argumentString);
NSPipe *pipe = [NSPipe pipe];
NSFileHandle *file = pipe.fileHandleForReading;
[task setStandardOutput:pipe];
NSLog(@"Pipe set");
[task launch];
NSLog(@"Task launched");
NSData *data = [file readDataToEndOfFile];
NSLog(@"Got data");
[task waitUntilExit];
NSLog(@"Task completed");
NSInteger taskStatusCode = [task terminationStatus];
NSLog(@"Task finished with status code %ld", taskStatusCode);
NSString *dataLength = [NSString stringWithFormat:@"%lu", data.length];
NSLog(dataLength);
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData((CFDataRef)data);
CGImageRef cgImage = CGImageCreateWithPNGDataProvider(imageDataProvider, NULL, true, kCGRenderingIntentDefault);
if (cgImage) {
CGFloat height = CGImageGetHeight(cgImage);
CGFloat width = CGImageGetWidth(cgImage);
CGSize size = CGSizeMake(width, height);
// Preview will be drawn in a vectorized context
CGRect rect = CGRectMake(0, 0, width, height);
CGContextRef cgContext = QLPreviewRequestCreateContext(preview, size, true, NULL);
CGContextDrawImage(cgContext, rect, cgImage);
NSLog(@"Image drawn");
QLPreviewRequestFlushContext(preview, cgContext);
NSLog(@"Flushed");
// release resources
if (cgContext) {
CFRelease(cgContext);
NSLog(@"Context released");
}
CGImageRelease(cgImage);
NSLog(@"Image released");
}
else {
NSLog(@"cgImage is NULL");
}
return noErr;
}
Inspection console logs would give various Operation not permitted errors when run from the quicklookd server. I suspect that possibly this is due to App Sandboxing issues.
Attempt 2
After coming across various articles online by @eskimo and others, including the official docs and various other posts, I have tried to sign the binary executable as described in the documentation and embed it in the project. Unfortunately after signing the app I have not been able to get the executable to run. When called from the QuickLook Preview function, the process returns with an error code. Perhaps the issue is here and the executable has not been signed properly?
Attempt 3
I have also tried to create a Swift-based app and add a QuickLook Extension to the app (creating an app with a .appex) using a similar flow as tried in Attempt 1. Also no luck here so far.
At this point I am stuck and would appreciate any help or pointers! Many thanks in advance.
Hi folks,
My app is reading proprietary files with the file name extension .JPX - which is out of my control. In addition I’m providing QuickLook and Thumbnail extensions, used system-wide and in my app.
Unfortunately iOS is assigning the JPEG-2000 file type (UTI „public.jpeg-2000“) to this file extension, and therefore - to work with associated files - my app is importing this UTI and both extensions are listing „public.jpeg-2000“ in their info.plist as QLSupportedContentTypes. This works to some extent in simulators and when debugging from Xcode on a device: Files with the file extension „.JPX“ are listed with thumbnails provided by my extension, although the preview seems to invoke the system-provided viewer and fails. Not perfect, but good enough as my app requires an icon preview (aka thumbnail) in its UIDocumentBrowserViewController.
But when I try to submit my app incl. extensions to the Apple App Store / TestFlight asset validation is reporting an error:
„Asset validation failed. Invalid Info.plist value. The value for the key ‚QLSupportedContentTypes‘ in bundle … is invalid. [public.jpeg-2000] are system-supported types.“
How to assign QuickLook / Thumbnail extensions to 3rd party files types whose extension is conflicting with a system-supported UTI?
I just spent one of my TSIs for this question - as my Apple developer membership is renewed shortly - but maybe this community as some smart tip to share...
Appreciate any help, Mattes
As available on iOS we can allow users to rate/submit review from within the app. Do we have the same functionality available on Apple TV aka TVOS apps?
Regardless of the fact that they all have different filenames, they all come out with the above label. Is there a way for me to specify what the asset is called in Windowed Quicklook?
Hi!
I'm participating in the Swift Student Developer competition this year, which requires developers to present a Swift Playground to Apple. I'm used to making normal Xcode projects, and am having trouble finding a Swift Playgrounds version of the Copy Bundle Resources build phase (I don't think it is possible to edit build phases in a Swift Playground).
I created a '.usdz' file from a 3D model I designed using Reality Converter and added it to the root of my Swift Playground project. I access the file programmatically from the App Bundle like so (fileName is a non-nullable String):
guard let path = Bundle.main.path(forResource: fileName, ofType: "usdz") else { fatalError("Couldn't find the USDZ file.") }
At runtime, this throws the Couldn't find the USDZ file error, as the file isn't being copied to the App Bundle.
In a normal Xcode project, according to this StackOverflow question, I can get xcodebuild to copy my file over by specifying it in the Copy Bundle Resources build phase, however, in a Swift Playground (required by Apple), I am restricted from modifying Xcode's buildphases (the option is not present when clicking on the default target - the only options are General, Signing & Capabilites and Package Dependencies).
How can I ensure that resources are copied over to the App Bundle at buildtime in a Swift Playground?
If this is not possible, are there any other options besides using the Bundle.main.path API for accessing the USDZ file (to load a QuickLook preview) at runtime?