dismissGrantingAccessToURL: failing with "didPickDocumentURLs called with nil or 0 URLS" - ios

I have an iOS application that is providing Document Picker feature working perfectly on iOS 10 but that on iOS 11 always calls the documentPickerWasCancelled: with this message in logs:
[UIDocumentLog] UIDocumentPickerViewController : didPickDocumentURLs
called with nil or 0 URLS
I'm correctly calling dismissGrantingAccessToURL: with a valid NSURL on the provider extension but it never calls the documentPicker:didPickDocumentsAtURLs: on the other side.
I think I'm missing something, can you give me an explanation for this bad behaviour?

I'm having the same problems. Unfortunately I think the explanation is a bug or backwards-incompatibility in iOS 11. According to the documents it should be enough with a Document Picker extension:
"The Document Picker View Controller extension can perform import and export operations on its own. If you want to support open and move operations, you must pair it with a File Provider extension."
https://developer.apple.com/documentation/uikit/uidocumentpickerextensionviewcontroller?language=objc
And indeed this worked fine in iOS 10 and earlier. iOS 11 was probably meant to be backwards compatible with the existing FileProvider-less DocumentPickers, but seems it's not. Or perhaps they forgot to update the documents.
Instead, one can implement the new updated File Provider that gives access to your files via the standard document browser UI:
https://developer.apple.com/documentation/fileprovider

This does work with an iOS 11 FileProvider backing the iOS10 picker. You probably want to create a new FileProvider using the new Xcode template, then use :
#available(iOSApplicationExtension 11.0, *)
on the FileProviderItem and FileProviderEnumerator classes, then :
if #available(iOSApplicationExtension 11.0, *) {
in the methods on your FileProviderExtension
I find that my iOS 10 picker does correctly call this method, but note the completionHandler?(nil) was required to make it work. By default, the template for iOS11 inserts a completion that reports a failure. This code works for me:
override func startProvidingItem(at url: URL, completionHandler: ((_ error: Error?) -> Void)?) {
completionHandler?(nil)
// completionHandler?(NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:]))
}
However, that isn't the end to this iOS10/11 incompatibility. If you make an iOS10/11 compatible file provider, it won't run on some iOS10 devices as far as I can see. I can run or debug mine on a 32-bit iOS device, but the FileProvider crashes on a 64-bit iOS 10 device with this error:
dyld: Library not loaded: /System/Library/Frameworks/FileProvider.framework/FileProvider
Referenced from: /private/var/containers/Bundle/Application/61BBD1A7-EA1E-4C10-A208-CA1DFA433C8D/test.app/PlugIns/testFileProvider.appex/testFileProvider
Reason: image not found

Related

iOS app crashes with Symbol Not Found HKVerifiableClinicalRecordQuery in xcode 13.3

I have an app that runs fine on iPhone iOS 15 but crashes on simulator iOS 13. The
call to HKVerifiableClinicalRecordQuery is wrapped in a #available block but the library tries to be uploaded at launch time:
Referenced from: /Users/johndoe/Library/Developer/Xcode/DerivedData/appHere-ebozeeuyrbpizofrvpxydtfbydkf/Build/Products/Debug-iphonesimulator/MyFramework.framework/MyFramework
Expected in: /System/Library/Frameworks/HealthKit.framework/HealthKit
in /Users/johndoe/Library/Developer/Xcode/DerivedData/appHere-ebozeeuyrbpizofrvpxydtfbydkf/Build/Products/Debug-iphonesimulator/MyFramework.framework/MyFramework
This is very similar but kind of old now: https://developer.apple.com/forums/thread/12110
All entitlements seem right as the same app without changes work fine in xcode 13.1 + iOS 13.
Apple release notes don't show anything special with HK so I wonder what could this be.
The snippet of code where the call is done is this:
func requestVerifiableHealthRecords() {
if #available(iOS 15, *) {
let healthStore = HKHealthStore()
let credentialTypes = ["https://smarthealth.cards#immunization"]
let dateInterval = DateInterval(start: .distantPast, end: .now)
let predicate = HKQuery.predicateForVerifiableClinicalRecords(withRelevantDateWithin: dateInterval)
let query = HKVerifiableClinicalRecordQuery(recordTypes: credentialTypes, predicate: predicate) {
// some code here
}
healthStore.execute(query)
}
}
But as I mentioned above, crash happens at launch time, this piece of code is never even close to being executed. The simulator shows the splash screen and nothing more. Debug navigator shows start then __abort_with_payload.
The concerning line is this one:
Expected in: /System/Library/Frameworks/HealthKit.framework/HealthKit
Why is even looking for a framework in MacOS, in any case, it should be looking it in xCode bundle and it shouldn't fail/crash.
I think the error is misleading, it must be something else but I still can't figure it out.
Found the issue for this one. Hopefully can help anyone with similar crash.
Select your crashing target
Select Build Phases tab
Expand Link Binary With Libraries and change Embed setting to be Optional for the offending framework. In this case HealthKit framework
Build again and crash should've been gone now.

availableRawPhotoPixelFormatTypes Missing in Xcode 12 beta 6

I get the following snippet:
let newPhotoSettings = AVCapturePhotoSettings(rawPixelFormatType: OSType(self.photoOutput.availableRawPhotoPixelFormatTypes.first!), processedFormat: nil)
When I build in Xcode 12 beta 6, I get the following error:
Value of type 'AVCapturePhotoOutput' has no member 'availableRawPhotoPixelFormatTypes'
When I check the API documentation (here), it does not show availableRawPhotoPixelFormatTypes as deprecated.
Anyone else has this issue?
UPDATE
The above errors are only present when I try to render SwiftUI preview for Home Screen Widget. If I run the project normally, it runs perfectly fine without errors.
Also, I am getting same error for preview pixel types:
photoSettings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: photoSettings.availablePreviewPhotoPixelFormatTypes[0]]
Value of type 'AVCapturePhotoSettings' has no member 'availablePreviewPhotoPixelFormatTypes'
It seems this is now split into two queries:
Use availableRawPhotoFileTypes to get the supported RAW file types, choose one of them, and then ask for the corresponding supported format types with supportedRawPhotoPixelFormatTypes(for fileType: AVFileType).
I think right now only DNG files are supported in iOS, but separating the APIs is probably more future-proof.

Is Process always unresolved in iOS app or Playground?

In a iOS app, I want to use a file which have an following function using Process:
public func system(_ body: String) throws {
if #available(macOS 10.0, *) {
let process = Process()
...
} else {
fatalError()
}
}
Then, I got a fallowing error even though I applied Availability Condition and I don't evoke this function:
Use of unresolved identifier 'Process'.
I tried a similar code in Playground, and I got the same error.
I learned we cannot use Process in iOS Apps with a regular way by this question: How to execute terminal commands in Swift 4? , and I have a solution that I separate these codes with files by each using platforms. But I want to use this single file if I can.
Please give me your another solution for my ideal.
if #available() does a runtime check for OS versions.
if #available(macOS 10.0, *)
evaluates to true if the code is running on macOS 10.0 or later,
or on iOS/tvOS/watchOS with an OS which is at least the minimum deployment target.
What you want is a conditional compilation, depending on the platform:
#if os(macOS)
let process = Process()
#else
// ...
#endif
Even though you already solved this problem, just for you to know, I want to tell you that actually, Process() (or CommandLine() in Swift 3.0 or newer) is available for iOS, but you'll need to use a custom Objective-C header file which creates the object Process()/CommandLine(), or rather NSTask(), and everything it needs.
Then, in order to use this code with Swift, you'll need to create a Bridging-Header, in which you'll need to import the NSTask.h file for it to be exposed to Swift and being able to use it in your Swift code.
Once done this, use NSTask() instead of Process():
let process = NSTask() /* or NSTask.init() */
Or just use the following function in your code whenever you want to run a task:
func task(launchPath: String, arguments: String...) -> NSString {
let task = NSTask.init()
task?.setLaunchPath(launchPath)
task?.arguments = arguments
// Create a Pipe and make the task
// put all the output there
let pipe = Pipe()
task?.standardOutput = pipe
// Launch the task
task?.launch()
task?.waitUntilExit()
// Get the data
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
return output!
}
As you can see, NSTask() would be the equivalent to Process() in this case.
And call it like this:
task(launchPath: "/usr/bin/echo", arguments: "Hello World")
This will also return the value, so you can even display it by doing:
print(task(launchPath: "/usr/bin/echo", arguments: "Hello, World!"))
Which will print:
~> Hello, World!
For this to work and not throwing an NSInternalInconsistencyException, you'll need to set the launchPath to the executable's full path instead to just the directory containing it.
You'll also need to set all the command arguments separated by commas.
Tested on both iPad Mini 2 (iOS 12.1 ~> Jailbroken) and iPhone Xr (iOS 12.2 ~> not jailbroken).
NOTE: Even though this works both on non-jailbroken and jailbroken devices, your App will be rejected on the AppStore, as #ClausJørgensen said:
You're using private APIs, so it'll be rejected on the App Store. Also, Xcode 11 has some new functionality that will trigger a build failure when using certain private APIs.
If your app is targeting jailbroken iOS devices and will be uploaded to a third-party store like Cydia, Zebra, Thunderbolt or Sileo, then this would work correctly.
Hope this helps you.

Cross platform code sharing in Swift

I have an app created from Xcode 9 / New Project / Cross Platform Game App, targets: macOS and iOS.
I wrote the following simple function to load text from a Data Asset.
func dataAssetAsString(_ name: String) -> String? {
if let asset = NSDataAsset(name: NSDataAsset.Name(name)) {
return String(data: asset.data, encoding: .utf8)
}
return nil
}
I'm puzzled by the following things:
This function only works if I import either UIKit, or AppKit.
But somehow importing MetalKit instead of UIKIt or AppKit makes it work. This really puzzles me. This should have nothing to do with MetalKit.
Most answers I found for code sharing between iOS and macOS suggest some kind of conditional import at the beginning of the file / shimming.
I went through the slides of WWDC 2014 Session 233: Sharing code between iOS and OS X, and it explicitly says we should not do shimming in Swift.
On the other hand, I don't understand what we should do instead of shimming. The mentioned very complicated process of creating and compiling shared frameworks for a 5 line function cannot be the right direction.
In 2018 (Swift 4, Xcode 9, iOS 11, macOS 10.13), what would be the recommended way to add such a trivial cross platform code to an app?
What is the magic of MetalKit which makes it work without shimming? Does it shim internally?

Accidentally overriding getter with swift extension - Only during runtime when app enters foreground again

The last 2 days i brooded over a strange behavior i found in my iOS app. It was one of these moments when you start to have doubts about everything. I found the problem and fixed it. But i want to write this here to search for answers and gain knowledge about the system behind.
I am the developer of "Controller for HomeKit", an app to control HomeKit devices. In HomeKit you have HMActionSets to execute many changes at once. E.g. Turn on all lights.
Executing HMActionSet is a basic functionality and worked always. Here with a swipe of UITableViewCell.
let performAction = UIContextualAction(style: .normal, title: AppStrings.Actions.perform, handler: { (ac: UIContextualAction, view: UIView, success: #escaping (Bool) -> Void) in
self.home?.executeActionSet(selectedActionSet, completionHandler: { (error: Error?) in
if let err = error {
UIApplication.printToConsole(text: err)
success(false)
return
}
success(true)
})
})
BUT suddenly it stopped working flawlessly with a new development version. It took me a while to reproduce the situation, when it wasn't executing the action set.
Every time i start the app from scratch, everything works.
If i execute the HMActionSet right after navigating from the previous UIViewController, everything works. (Most common use case)
It stops working when being in the view, press the home button, reentering the app. After that all executions won't work, until going one view backwards and forward again.
Console logs this error:
Error Domain=HMErrorDomain Code=2 "Object not found." UserInfo={NSLocalizedDescription=Object not found.}
I walked backwards every commit until the problem was solved. After digging in the dark in a too big commit, i found the root cause.
But why am i not getting compile errors? Why does going to the home screen breaks it?
For a new feature i needed a new way to identify a HomeKit object on multiple devices, because the .uniqueIdentifier parameter is salted on every device.
import Foundation
import HomeKit
protocol Identifyable {
func getUniqueIdentifier() -> String?
}
extension HMActionSet: Identifyable {
func getUniqueIdentifier() -> String? {
return self.name.toBase64()
}
}
I created a swift protocol and made an extension to HMActionSet. Of course now that i found the error it looks stupid, to name the method like a getter. But this should not be the discussion now. It seems like this methods overrides the attribute of HMActionSet. When executing the actionSet internal functions accidentally use the wrong uniqueIdentifier, therefore the error is correct. (No object found).
/*!
* #brief A unique identifier for the action set.
*/
#available(iOS 9.0, *)
open var uniqueIdentifier: UUID { get }
If i name the method func uniqueIdentifier() -> String? Xcode immediately shows me a warning.
Method 'uniqueIdentifier()' with Objective-C selector 'uniqueIdentifier' conflicts with getter for 'uniqueIdentifier' with the same Objective-C selector
As i know HomeKit is written in Objective-C. I am using swift 4.
My questions to you:
Why are there no compiler errors (getUniqueIdentifier())?
If its a runtime problem mixing a swift extension into a objective-c framework, why does it only occur when reentering the app and not at the first start or later?
Looking forward to read your explanations about the problem that stole my sleep. :)

Resources