Is Process always unresolved in iOS app or Playground? - ios

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.

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.

for-await-in loop not working in Swift 5.5

I was reading the Concurrency section in the Swift 5.5 documentation (https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#ID640) and copy pasted this example code in a MacOS command line project:
import Foundation
func someFunction() async throws {
let handle = FileHandle.standardInput // Directly from example
for try await line in handle.bytes.lines { // Directly from example
print(line) // Directly from example
} // Directly from example
}
When I built this I got the following errors:
await must precede try (This is weird considering in the documentation await comes after try)
Expected '{' to start the body of for-each loop
Expected pattern
Value of type 'FileHandle' has no member 'bytes'
I literally copy pasted the code so I shouldn't be getting any such errors. I am using MacOS 11.4 and Xcode version 12.5.1. I downloaded the latest toolchain for Swift 5.5 which was released July 1st, 2021. All the previous code with async and await is working for me. Any idea why this is happening?
Things I've tried so far that have not worked:
Making the function return Void as suggested by Ayrton
Tried adding if #available(macOS 12.0, *) in the function body
I tried extending FileHandle to add conformance to AsyncSequence: extension FileHandle: AsyncSequence { }
It said that FileHandle doesn't conform to AnySequence.

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.

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

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

Swift App Extension: instance method count is unavailable

I just create my first app extension using XCode 7.1. One code file containing the code below is shared with both targets:
var str = "";
var l = str.count; //Compile error for extension target App: count is unavailable: There is no ...
The reason for this compile error seams to be that App extension compiles with swift 1.2 while the container target compiles with swift 2.0.
One solution would be importing the content App into the extension App doesn't appear to be a good solution from what i read about it. Sharing the code between targets can be difficult if both are not compiled using the same compiler.
I just run through all target settings and didn't find nothing that could be changed.
Can't find any post about this problem, witch is not so uncommon, so it is must likely i am interpreting something in a wrong way.
The only solution i can think of is using NSString instead of String but that is just an workaround for one class type. More problems of this kind will emerge in the future.
In Swift 2 it's
str.characters.count
Use str.characters.count to get String length in Swift 2

Resources