Posts

Post not yet marked as solved
3 Replies
It seems that SwiftData and Codable model properties are seriously broken (at least as of Xcode 15.0 beta 3). I'm getting the same error shown in the above question for any model property whose type is anything marked as Codable. I am finding that in many cases the custom encode method is never called when storing a value in an instance of the model. And when reading the property, the init(from:) isn't encoded as expected. I was able to get things to work for CLLocationCoordinate2D with the following implementation: extension CLLocationCoordinate2D: Codable { // These exact case values must be used to decode a location enum CodingKeys: CodingKey { case latitude case longitude } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let lat = try container.decode(CLLocationDegrees.self, forKey: .latitude) let lon = try container.decode(CLLocationDegrees.self, forKey: .longitude) self = CLLocationCoordinate2D(latitude: lat, longitude: lon) } // Never called by SwiftData public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.latitude, forKey: .latitude) try container.encode(self.longitude, forKey: .longitude) } }
Post not yet marked as solved
1 Replies
NSBeep() and AudioServicesPlayAlertSound(kSystemSoundID_UserPreferredAlert) seem to play the same sound. kSystemSoundID_UserPreferredAlert isn't available under Mac Catalyst but it has a value of 0x00001000. The following code works under Mac Catalyst (at least in my tests with macOS 13.4). AudioServicesPlayAlertSound(0x00001000); // same sound as NSBeep() You may need to import AudioToolbox.
Post not yet marked as solved
3 Replies
OK, let's say I check the original purchase date instead of the original purchase version. I still don't know what is supposed to happen between the iOS and macOS versions of the app. If a user purchased some earlier version of the iOS app (pre or post in-app purchases), should the receipt received in the macOS version of the app reflect that past purchase? Right now I know of one user where the receipt obtained in the macOS version makes no mention of the past iOS purchase. The user has confirmed they are using the same AppleID on both devices. As extra info, my app only does receipt validation if the user clicks on the "restore purchases" button in the app. Is there any reason that waiting until then to request a new receipt would cause the wrong receipt to be received?
Post not yet marked as solved
1 Replies
LSApplicationQueriesSchemes is only used with canOpenURL. It's not used with openURL. So you can open all of the apps you want. You are just limited if you want to check for installed apps with canOpenURL. From the docs for canOpenURL: Unlike this method, the openURL:options:completionHandler: method isn’t constrained by the LSApplicationQueriesSchemes requirement. If an app is available to handle the URL, the system will launch it, even if you haven’t declared the scheme.
Post marked as solved
1 Replies
tl;dr - I unknowingly had a localized Info.plist strings file in my project and the only string in it was the Contacts privacy string. Once the strings file was deleted, the proper privacy string began to appear as expected. This issue was driving me crazy. No matter what I did to change the Info.plist, the reviewers were seeing the old privacy string. I finally broke down and pushed an updated build of my iOS app to Test Flight. On one of my iOS device I deleted the app and reset Location and Privacy settings. I installed the iOS app from Test Flight and to my shock, the old privacy string was appearing just like it did for the reviewers. Until the Mac version of the app was rejected a few days again, I hadn't updated any of the app's privacy strings in years. It was never an issue. So then I deleted the app and reset Location and Privacy settings again but this time I did a plain development build from Xcode. And sure enough, the old privacy string appeared again. Why? The Info.plist clearly has the updated privacy string. I was dumbfounded. What is going on? I finally did a deep search of the project for part of the message and there it was. InfoPlist.strings with just that one value in it. So no matter what I did to Info.plist, the value in Info.strings was being used. I have no idea how it was ever created or when. Looking at git it seems it was somehow created and added just 3 weeks ago. Very odd. Anyway, if you ever have trouble with your privacy strings not updating, check for an InfoPlist.strings file in your project.
Post marked as solved
3 Replies
Success! (mostly). I finally found a way to remove the "Take a Photo", "Scan Documents", and "Add Sketch" menus. The solution doesn't result in any stack traces either which is nice. The only minor piece left is that this solution does leave the grayed out device name menu item in the context menu. And if there is more than one device, the "Import from iPhone or iPad" menu still appears but only with the grayed out device names. Here's the code that removes the three unwanted menu items (for each device): override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { if action == Selector(("importFromDevice:")) { // sender will be an NSMenuItem. There will be one each for the 3 menus we are trying to hide if let menu = sender as? NSObject, menu.responds(to: Selector(("setHidden:"))) { menu.setValue(true, forKey: "hidden") } return false } return super.canPerformAction(action, withSender: sender) } Add this to any view controller that has a UITextView to eliminate the 3 context menu options. Or create a custom subclass of UITextView and put the code in there. Then use the custom text view class anywhere you don't want these menus.
Post marked as solved
3 Replies
One more slight step forward. I have found a way to make the "Take Photo", "Scan Documents", and "Add Sketch" menus do nothing. It's still a poor user experience having the menus at all, but at least now the user doesn't get to go through all of the motions of taking a photo or scanning a document with their iOS device just to have the results ignored. I was able to make this happen by adding the following code: @objc func importFromDevice(_ sender: Any) { // no-op } override func target(forAction action: Selector, withSender sender: Any?) -> Any? { if action == #selector(importFromDevice) { return self } return super.target(forAction: action, withSender: sender) } You can add this to a custom UITextView subclass or to the view controller class containing a text view. The only issue with this code is that it results in assertion failures and big old stack traces in the console each time the menu gets validated. There's a message about: -[UINSResponderProxy validateMenuItem:]: We're being asked to validate a menu item whose proxy isn't the one we wrapped. But I've tested via TestFlight and the app continues to run just fine.
Post marked as solved
3 Replies
I have not found any way to remove the "Import from iPhone or iPad" menu. But I have at least found a way to prevent the ensuing image from being added as an attachment. When a user selects a menu, such as "Take Photo", and then takes a picture with their iOS device and chooses "Use Photo", the UITextViewDelegate textView(_:shouldChangeTextIn:replacementText:) delegate method is called. The replacement text will contain the character NSTextAttachment.character. So to prevent the picture from being added as an attachment to the text view, return false if you find that the replacement text contains NSTextAttachment.character. It's still a terrible user experience. The user sees the menu, goes through all of the steps to take a photo, and then nothing happens. It would be so much better if there was a way to remove the menu when not wanted.
Post marked as solved
5 Replies
Overdue update - The AVCam demo now runs under Mac Catalyst. Much of AVFoundation has been supported since Mac Catalyst 14.0. The AVCam demo needs a few small changes to build for Mac Catalyst. By default, the project is not setup with a Mac Catalyst destination. It only supports iPad apps running on Apple Silicon. Delete the "Mac (Designed for iPad)" destination and add the "Mac (Mac Catalyst)" destination. You may also have to change the macOS Deployment target in the Build Settings depending on the version of macOS running on your Mac. Thanks Apple for adding support for AVFoundation. Now I just need to figure out if I can support Camera Continuity under Mac Catalyst.
Post not yet marked as solved
2 Replies
I recently figured out a way to remove the "Show Fonts" and "Show Colors" menus from the context menu of a UITextView. This only works on iOS 16.0/Mac Catalyst 16.0 or later. The following is my Objective-C code. Should be easy enough to translate to Swift. I started by adding new method to UIMenu: Category declaration: @interface UIMenu (Additions) - (UIMenu *)menuByRemovingChildWithAction:(SEL)action; @end Category implementation: @implementation UIMenu (Additions) - (UIMenu *)menuByRemovingChildWithAction:(SEL)action { NSArray<UIMenuElement *> *children = self.children; for (NSInteger i = 0; i < children.count; i++) { UIMenuElement *element = children[i]; if ([element isKindOfClass:[UICommand class]]) { UICommand *cmd = (UICommand *)element; if (cmd.action == action) { NSMutableArray *newChildren = [children mutableCopy]; [newChildren removeObjectAtIndex:i]; if (newChildren.count == 0) { return nil; } else { return [self menuByReplacingChildren:newChildren]; } } } else if ([element isKindOfClass:[UIMenu class]]) { UIMenu *menu = (UIMenu *)element; UIMenu *newMenu = [menu menuByRemovingChildWithAction:action]; if (newMenu == nil) { NSMutableArray *newChildren = [children mutableCopy]; [newChildren removeObjectAtIndex:i]; return [self menuByReplacingChildren:newChildren]; } else if (newMenu != menu) { NSMutableArray *newChildren = [children mutableCopy]; newChildren[i] = newMenu; return [self menuByReplacingChildren:newChildren]; } } } return self; } @end This recursively goes through a menu hierarchy and removes any menu item that is a UICommand with the given action. With that in place you need to implement the iOS 16.0 UITextViewDelegate method: - (UIMenu *)textView:(UITextView *)textView editMenuForTextInRange:(NSRange)range suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0)) { UIMenu *menu = [UIMenu menuWithChildren:suggestedActions]; menu = [menu menuByRemovingChildWithAction:NSSelectorFromString(@"toggleFontPanel:")]; menu = [menu menuByRemovingChildWithAction:NSSelectorFromString(@"orderFrontColorPanel:")]; return menu; } That's it. All other attempts failed. I was able to disable the "Show Fonts" menu by overriding canPerformAction:sender: in the App Delegate. But for some reason it did nothing for the "Show Colors" menu. - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { if (action == NSSelectorFromString(@"toggleFontPanel:") || action == NSSelectorFromString(@"orderFrontColorPanel:")) { return NO; } BOOL res = [super canPerformAction:action withSender:sender]; return res; }
Post not yet marked as solved
1 Replies
This is asked and answered already: https://developer.apple.com/forums/thread/714388
Post not yet marked as solved
4 Replies
Without you posting a symbolicated crash report it's impossible to determine the issue. One suggestion with testing though - delete your app from your real test device (or even the simulator) and do a clean build so you are testing a fresh install like the reviewer. It's not uncommon for an app to work in development over time but fail on a fresh install.
Post marked as solved
4 Replies
When debugging a message such as: [general] *** -[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber' (0x205da88f8) [/System/Library/Frameworks/Foundation.framework]' for key 'NS.objects', even though it was not explicitly included in the client allowed classes set: '{(     "'NSDictionary' (0x205da1178) [/System/Library/Frameworks/CoreFoundation.framework]",     "'NSString' (0x205da8948) [/System/Library/Frameworks/Foundation.framework]" )}'. This will be disallowed in the future. you want to look for a use of NSKeyedUnarchiver where you only list NSDictionary and NSString. Then you want to add NSNumber. Do not add NSObject.
Post not yet marked as solved
39 Replies
Same issue but on an Intel MacBook Pro running macOS 12.6. One would have hoped that today's Xcode 14 would support all of the versions of iOS and iPadOS also released today. Oh well. I guess we all need to wait for Xcode 14.0.1 or something. Can't wait to download yet another 7+GB Xcode release on my 10Mbps Internet connection.
Post not yet marked as solved
3 Replies
I was dealing with this same issue in some older Objective-C code I was trying to update. After lots of different attempts I found the following solution (Swift version): if let token = FileManager.default.ubiquityIdentityToken {     do {         let data = try NSKeyedArchiver.archivedData(withRootObject: token, requiringSecureCoding: true)         if let newToken = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from: data) {             print("We got the token")             if newToken.isEqual(token) {                 print("tokens match")             } else {                 print("tokens do not match")             }         }     } catch {         print("oops: ", error)     } } This works and it compiles cleanly. But I'm worried that passing in NSData.self could possibly fail in some future implementation if Apple changes how the token is encoded.