I am developing an iOS App that will write files to an external device. I have installed and used UIDocumentPicker with the following code:
let documentPickerController = UIDocumentPickerViewController(forExporting: usbSendungURL)
documentPickerController.delegate = self
self.present(documentPickerController, animated: true, completion: nil)
Which successfully copied to my device.
However as these files can be somewhat large I decided that I needed to install a UIProgressView. Unfortunately DocumentPicker doesn’t doesn’t have this function so I changed my code as follows:
let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: [.folder])
documentPickerController.delegate = self
self.present(documentPickerController, animated: true, completion: nil)
That enabled me to pick up the selected folder in the delegate:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
documentPickerUrls = urls
moveFilesToUSB(urls: urls)
}
I then append the filenames as appropriate and used FileManager moveItem function:
try FileManager.default.moveItem(at: usbSendungURL[0], to: url1!)
Which enabled me to install the necessary timer functions for the progress updates.
Unfortunately FileManager appears not to able to write to an external device as I am receiving an error message that states that the I don’t have authorisation to write to the device although DocumentPicker does.
Is there a way around this issue.
Any help would be appreciated.
The URLs returned by UIDocumentPickerViewController are security-scoped and access to the directory requires the use of url.startAccessingSecurityScopedResource() and url.stopAccessingSecurityScopedResource(). This is explained clearly in Providing Access To Directories.
Related
I have a SwiftUI-based Mac app with multiple WindowGroups.
1. Opening different SwiftUI WindowGroups using URL schemes
To open those windows I am using URL schemes (like described here):
WindowGroup {
// ...
}
.handlesExternalEvents(matching: Set(arrayLiteral: "primaryWindow"))
... and then calling:
NSWorkspace.shared.open(URL(string: "myapp://primaryWindow")!)
☑️ This works just fine!
2. Detecting URLs called from outside the app
I also need to be able to recognise and handle URL schemes called from outside the app, like myapp://somePath?someParameter=1. I found this solution and set an event handler within my AppDelegate:
func applicationWillFinishLaunching(_ notification: Notification) {
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(self.handleGetURL(event:reply:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL)) )
}
☑️ This works just fine, too, and my #selector method is called like you would expect.
3. Problem: How to use .handlesExternalEvents and .setEventHandler simultaneously?
🛑 Here’s where the problem starts: After calling .setEventHandler my WindowGroups no longer react on called URLs and I remain unable to open new windows.
That somehow makes sense since I’ve registered an event handler in AppDelegate specifically for kAEGetURL but I have no idea how to implement both features simultaneously. Looking forward for your advice!
I am writing a HomeKit app that successfully shows live data from my supported accessories in-app. I can read single values (HMCharacteristic.readValue) or use notifications to stay updated (HMCharacteristic.enableNotification).
Now I want to implement Widgets that show this data on the user's Home Screen. This consists of four steps:
A dynamic Intent fetches all the registered (and supported) Accessories from the HMHomeManager and enables the user to select one of them to be shown on the Widget.
Inside the IntentTimelineProvider's getTimeline function I can then again use the HMHomeManager to retrieve the Accessory I want to display on the Widget (based on the Accessory's UUID which is stored inside the getTimeline's configuration parameter - the Intent).
Still inside the getTimeline function I can choose the Services and Characteristics I need for displaying the Accessory's Widget from the HMHomeManager.
Up until here everything works fine.
However, when I try to read the values from the Characteristics I chose before using HMCharacteristic.readValue, the callback contains an error stating
Error Domain=HMErrorDomain Code=80 "Missing entitlement for API."
The Widget's Info.plist contains the 'Privacy - HomeKit Usage Description' field and the Target has the HomeKit capability.
After some research I came up with the following theory: Obviously the whole WidgetKit API runs my code in background. And it seems like HomeKit does not allow access from a background context. Well, it does allow access to Homes/Services/Characteristics, but it does not allow reading or writing on Characteristics (I guess to make sure App developers use HomeKit Automations and don't try to implement custom automations that are controlled by some background process of their app running on the iPhone).
My (simplified) getTimeline code:
func getTimeline(for configuration: SelectAccessoryIntent, in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
// id stores the uuid of the accessory that was chosen by the user using the dynamic Intent
if let id = configuration.accessory?.identifier {
// Step 2.: fetch the accessory
// hm is a HMHomeManager
let hm = HomeStore.shared.homeManager
// take a short nap until the connection to the local HomeKit instance is established (otherwise hm.homes will create an empty array on first call)
sleep(1)
let accessories = hm.homes.flatMap({ h in h.accessories })
if let a = accessories.filter({ a in a.uniqueIdentifier.uuidString == id }).first {
// a holds our HMAccessory
// Step 3.: select the characteristic I want
// obviously the real code chooses a specific characteristic
let s: HMService = a.services.first!
let c: HMCharacteristic = s.characteristics.first!
// Step 4.: read the characteristic's value
c.readValue(completionHandler: {err in
if let error = err {
print(error)
} else {
print(c.value ?? "nil")
}
// complete with timeline
completion(Timeline(entries: [RenderAccessoryEntry(date: Date(), configuration: configuration, value: c.value)], policy: .atEnd))
})
}
}
}
}
My questions:
First: Is my theory correct?
If so: What can I do? Are there any entitlements that allow me to access HomeKit in background or similar? Do I need to perform the readValue call elsewhere? Or is it just impossible to use the HomeKit API with WidgetKit with the current versions of HomeKit/WidgetKit/iOS and best I can do is hope they introduce this capability at some point in the future?
If not: What am I missing?
I want to show a single notification when a beacons enters the region. I used the following code:
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
if region is CLBeaconRegion {
BeaconNotificationFound()
}
}
The locations manager and the region is correct. The only problem is, that with this function nothing happens.
What can I do?
This is the code above:
let locationManager = CLLocationManager()
let region = CLBeaconRegion(proximityUUID: UUID(uuidString: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")!, identifier: "AirLocate")
override func viewDidLoad() {
super.viewDidLoad()
//Darf Standort genutzt werden?
locationManager.delegate = self
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedWhenInUse) {
locationManager.requestWhenInUseAuthorization()
}
locationManager.startRangingBeacons(in: region)
locationManager.startMonitoring(for: region)
}
It sounds like you are not getting callbacks to the didEnterRegion method. A few things to check:
Make sure your Info.plist contains an entry like below, otherwise it won't be able to prompt you for location access: <key>NSLocationWhenInUseUsageDescription</key><string>This app needs to access your location so it can tell when you are near a beacon.</string>
Make sure you are prompted for location access and you have actually granted it. Check in Settings -> [Your app name] -> Location, and verify it says "ALLOW LOCATION ACCESS" has a checkmark that is not next to Never.
Make sure bluetooth is on.
Force a region exit by bringing your app to the foreground, and turning off the beacon for at least 60 seconds. Once this is done, turn the beacon back on.
If none of the above works, use an off the shelf beacon detector app like Locate or AirLocate to verify it can see your beacon when transmitting. If using one of these apps, you must configure your UUID in the app for detection.
I'm facing weird crash while long pressing and then clicking a link inside UITextView. Below is my code for handling touch event on link.
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
let termsAndConditions : TRTermsAndConditionsViewController = TRTermsAndConditionsViewController(nibName: "TRTermsAndConditionsViewController", bundle: nil)
let navigationtermsAndConditions = TRBaseNavigationViewController(rootViewController: termsAndConditions)
self.present(navigationtermsAndConditions, animated: true, completion: nil)
return false
}
I'm getting below error:
*** Assertion failure in -[TRADFRI.TRTextViewNonEditable startInteractionWithLinkAtPoint:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.29.5/UITextView_LinkInteraction.m:377
I googled a lot and gone through these links as well link1 link2 but didn't get any success. I have tried solution given by "Sukhrob" and "ryanphillipthomas" on link1 and solution given by "nate.m" and "chrismorris" on link2. More weird thing is that i'm getting this crash on devices that support 3D touch like iPhone 6S, iPhone 6S Plus(with iOS 9 or above). Can anybody help me out for this issue.
At last i resolved this issue by getting some help from this link
So i have removed the link attribute from NSAttributedString and make use of underline attribute only. By making use of tap gesture , i'm detecting the index of character on which user is tapping and if that index lying in my hyperlink range i'm opening the URL.
My app keeps crashing when running in the simulator everytime I try to request authorization for the photo library. I am using the following code in my appDelegate in didFinishLaunchingWithOptions:
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) in
})
}
Using xcode 8 beta with swift 3.0.
In my testing, iOS 10 doesn't like to output useful error messages unless you're running on an actual device. In this particular case, you probably haven't provided the key NSPhotoLibraryUsageDescription in your Info.plist file, and that value must be provided before requesting authorization.
Have to allow access to photos on device. Add below key and string to your info.plist. The autocomplete in the property list view is "Privacy - Photo Library Usage Description". Or just open your info.plist in source code view and add the following:
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photos.</string>