Disable copy-past premission in iOS 16.1 - swift

as you may know, iOS 16 introduced a bug that when your app use UIPasteboardit keeps asking for permission every time. This annoying alert is gone after 16.1 as I heard, but still see it. User must go to the app setting in the iOS setting and change Paste from other apps to allow.
Is there anyway to do it on app that user doesn't need to do those crazy steps? Otherwise we should inform every user to do that to get rid of that alert.
Would be so grateful if you can give me a good hint to fix it. Maybe some privacy key on info.plist?
here is my simple code to read from clipboard
private func getURLFromClipboard() {
let pasteboardString: String? = UIPasteboard.general.string
if let string = pasteboardString {
if validateUrl(urlString: string as NSString) {
presentShareURL(url: string)
}
}
}

Related

CloudKit CKShare userDidAcceptCloudKitShareWith Never Fires on Mac App

I am working on accepting a CKShare in a macOS app in Swift 4. I've already done all the following:
Create the CKShare and save it with its rootRecord to CloudKit
Add a participant (CKShare.Participant)
I've confirmed that the CKShare is on the CloudKit server and that the person I invited has access to it. Here's a screenshot: https://d.pr/i/0sMFQq
When I click the share link associated with the CKShare, it opens my app, but nothing happens and userDidAcceptCloudKitShareWith doesn't fire.
func application(_ application: NSApplication, userDidAcceptCloudKitShareWith metadata: CKShareMetadata) {
print("Made it!") //<-- This never gets logged :(
let shareOperation = CKAcceptSharesOperation(shareMetadatas: [metadata])
shareOperation.qualityOfService = .userInteractive
shareOperation.perShareCompletionBlock = {meta, share, error in
print("meta \(meta)\nshare \(share)\nerror \(error)")
}
shareOperation.acceptSharesCompletionBlock = { error in
if let error = error{
print("error in accept share completion \(error)")
}else{
//Send your user to where they need to go in your app
print("successful share:\n\(metadata)")
}
}
CKContainer.default().add(shareOperation)
}
Is there some kind of URL scheme I have to include in my info.plist? Or perhaps a protocol I need to conform to in my NSApplicationDelegate delegate? I can't, for the life of me, figure out what to do. Thanks in advance!
Update
I've tried a few more things on this. When I open the share link in a web browser, I see this:
Clicking OK makes the screen fade away to this:
Not particularly helpful. :) After doing this, the participant's status in CloudKit is still Invited, so the share still hasn't been accepted.
When I click on a share link within Messages, I am shown a popup like this:
After I click open, a new copy of my app shows up in the dock, then the app suddenly closes. The crash log states:
Terminating app due to uncaught exception 'CKException', reason: 'The application is missing required entitlement com.apple.developer.icloud-services'
I've tried turning iCloud off and on again in the Capabilities section of Xcode, but nothing changes. I know this exception can't be right because I can start my app normally and use CloudKit all day long. Only the CKShare causes this crash.
This is a mess. Save me, Obi-wan Kenobi, you're my only hope.
Yes,
You need to add this to your info.plist.
<key>CKSharingSupported</key>
<true/>
** EDITED ANSWER **
I use this code to share, I don't do it manually... not sure if this is an option under OS X I must confess. I am using iOS.
let share = CKShare(rootRecord: record2S!)
share[CKShareTitleKey] = "My Next Share" as CKRecordValue
share.publicPermission = .none
let sharingController = UICloudSharingController(preparationHandler: {(UICloudSharingController, handler:
#escaping (CKShare?, CKContainer?, Error?) -> Void) in
let modifyOp = CKModifyRecordsOperation(recordsToSave:
[record2S!, share], recordIDsToDelete: nil)
modifyOp.savePolicy = .allKeys
modifyOp.modifyRecordsCompletionBlock = { (record, recordID,
error) in
handler(share, CKContainer.default(), error)
}
CKContainer.default().privateCloudDatabase.add(modifyOp)
})
sharingController.availablePermissions = [.allowReadWrite,
.allowPrivate]
sharingController.delegate = self
sharingController.popoverPresentationController?.sourceView = self.view
DispatchQueue.main.async {
self.present(sharingController, animated:true, completion:nil)
}
This presents an activity controller in which you can choose say email and then send a link. You might also want to watch this video, focus on cloudKit JS right at the beginning.
Watch this WWDC video too https://developer.apple.com/videos/play/wwdc2015/710/
It talks about the cloudkit JSON API, using it you can query what has and what hasn't been shared in a terminal window/simple script perhaps. I did the same when using dropbox API a few years back. Hey you can even use the cloudkit JSON API within your code in place of the native calls.
I finally got it to work! I did all of the following:
Deleted my app from ~/Library/Developer/Excode/DerivedData
Made sure I had no other copies of my app archived anywhere on my machine.
Said a prayer.
Rebooted.
Sheesh, that was rough. :)
If your app is a Mac Catalyst app running on any version of macOS Catalina at least up to and including 10.15.4 Beta 1, a UIApplicationDelegate userDidAcceptCloudKitShareWith method will never be invoked.
After some significant debugging, we discovered that the MacCatalyst UIKit doesn’t even have an implementation for userDidAcceptCloudKitShareWithMetadata in its UIApplication delegate. It’s not broken, it’s just not there. So, at least temporarily, our workaround is the following, which seems to work, even if it’s very inelegant:
// Add CloudKit sharing acceptance handling to UINSApplicationDelegate, which is missing it.
#if targetEnvironment(macCatalyst)
extension NSObject {
#objc func application(_ application: NSObject, userDidAcceptCloudKitShareWithMetadata cloudKitShareMetadata: CKShare.Metadata) {
YourClass.acceptCloudKitShare(cloudKitShareMetadata: cloudKitShareMetadata)
}
}
#endif
If you are using a SceneDelegate, implement the delegate callback there, instead of on AppDelegate.
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
// ...
}
You need to create the app delegate for your SwiftUI app using #NSApplicationDelegateAdaptor:
#main
struct Sharing_ServiceApp: App
{
#NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene
{
WindowGroup
{
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
I put that line in and my code instantly started receiving the share requests.

Xcode 8: UI Testing, Neither element not any descendant has keyboard focus

I've recently started to use UI testing within Xcode. I was able to record the test and play it back and it worked perfectly fine. I recently changed the accessibility labels for textFields to make the test look cleaner by giving it a better name.
However, now when I record the UI test and play it back again to test it, I get an error "Neither element not any descendant has keyboard focus.". I've attempted to uncheck hardware keyboard as mentioned in other posts, but that doesn't work for me.
//test login with username and empty password
let app = XCUIApplication()
let elementsQuery = app.scrollViews.otherElements
let emailtextfieldElement = elementsQuery.otherElements["emailTextField"]
let passwordtextfieldElement = elementsQuery.otherElements["passwordTextField"]
emailtextfieldElement.tap()
passwordtextfieldElement.tap()
emailtextfieldElement.tap()
app.typeText("wayne#test.com")
passwordtextfieldElement.tap()
app.buttons["Login - signInButton"].tap()
Anybody know the fix for this?
Use typeText on emailtextfieldElement instead of app.
let emailtextfieldElement = elementsQuery.otherElements["emailTextField"]
emailtextfieldElement.tap()
emailtextfieldElement.typeText("wayne#test.com")

Modify accessibility settings on macOS with Swift

I'm writing a macOS app in swift and want to be able to enable text to speech for a user on their computer programmatically once they download my app. I'm not sure what commands to use to reconfigure a user's settings and ask for permission to change their accessibility settings. Does anyone know how to do this or where to find documentation that explains how to do this?
Apple requires that you ask explicity for the accessibillity settings. From sierra it is not possible to set this over the Database anymore. So the User has to click and enable your app for accessibility.
public func checkAccess() -> Bool{
//get the value for accesibility
let checkOptPrompt = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString
//set the options: false means it wont ask
//true means it will popup and ask
let options = [checkOptPrompt: true]
//translate into boolean value
let accessEnabled = AXIsProcessTrustedWithOptions(options as CFDictionary?)
return accessEnabled
}

Programmatically minimize all windows or besides my Cocoa application?

I haven't been able to find anything about this in Swift. Is there a way to programmatically make my application minimize all other windows open in the background, or even just minimize Safari? I basically want my application to run against the desktop, without any clutter in the background. Is there a way to programmatically do this for a Cocoa app? I'm pretty new to swift, so any help would be appreciated.
You can use api on NSWorkspace which allows you to hide all visible app in the background.
You can find more about NSWorkspace here: link
Hides all applications other than the sender. This method must be called from your app’s main thread.
NSWorkspace.shared().hideOtherApplications()
If you only want to hide Safari,
let appPath = NSWorkspace.shared().fullPath(forApplication: "Safari")
let identifier = Bundle.init(path: appPath!)?.bundleIdentifier
if let bundleID = identifier {
let runningApps = NSRunningApplication.runningApplications(withBundleIdentifier:bundleID )
if !runningApps.isEmpty {
runningApps.first?.hide()
}
}

ABPersonHasImageData in Swift always returns false, no matter what I do

I'm just getting into Swift and I'm stumbling over this one. I want to get the contact image of a contact in the method
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecord!, property: ABPropertyID, identifier: ABMultiValueIdentifier)
I first check if the contact has an image and then copy that into a newly created constant with
if(ABPersonHasImageData(person))
{
let avatar : NSData = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail).takeRetainedValue()
}
problem is, it always returns false in the simulator. Couldn't yet try it on my real phone.
What am I doing wrong? There are no errors. Extracting all other information from the contact works just fine.
Fixed it myself just now. After trying it at work, I compared the code and noticed that I had left out one thing, which made it work:
This line:
pickerController.displayedProperties = [NSNumber(int: kABPersonPhoneProperty)]
I never thought of this as being the problem, since the image showed in the contacts view. Of course I now need to know how to include the UIImage of the contact in that list, but since it isn't available like kABPersonPhoneProperty I really don't know how to do this.
Is it maybe a bug? Shouldn't I be able to get the Image, even if the above line excludes everything but the phone numbers?
My workaround would be to implement
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecord!) {
//get the image, then hide everything except phone numbers
}
and then define what's hidden in the contacts view in the next step. But that wouldn't be very nice would it?
Maybe someone knows of a better solution. Many thanks!
Well, seems like there is no problem here after all. I just tried the code of my first post again and it works. On my device AND on the same simulator...
Since I first had this problem I reinstalled Xcode, maybe that solved it without me noticing. Weird bug...
Nonetheless: (non-)problem solved!
I guess the reason you always receive false from ABPersonHasImageData is your code doesn't have the permission to access any AddressBook data except for the data the user explicitly selected (phone number, in your case).
To request the permission, wrap your presentViewController: ... in ABAddressBookRequestAccessWithCompletion (or acquire the permission at some other point, but before you attempt to access the picture).
e.g.
ABAddressBookRequestAccessWithCompletion(nil) {
(granted:Bool, err:CFError!) in
dispatch_async(dispatch_get_main_queue()) {
if granted {
// TODO: present ABPeoplePickerNavigationController etc.
}
}
}