Address Book crash on iOS10 - addressbook

Selecting a contact from contact picker crashes the app in iOS10.0. Contacts picker is shown using ABPeoplePickerNavigationController like this:
let contactsPicker = ABPeoplePickerNavigationController()
contactsPicker.peoplePickerDelegate = self
self.presentViewController(contactsPicker, animated: true, completion: nil)
Here is the stack trace from crash log:
*** Terminating app due to uncaught exception 'CNPropertyNotFetchedException', reason: 'A property was not requested when contact was fetched.'
*** First throw call stack:
(
0 CoreFoundation 0x0000000105a1c34b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x00000001052cd21e objc_exception_throw + 48
2 CoreFoundation 0x0000000105a85265 +[NSException raise:format:] + 197
3 Contacts 0x000000010dc6d96f -[CNContact sectionForSortingByFamilyName] + 160
4 Contacts 0x000000010dc3e18e __55-[CNContact(iOSABCompatibility) overwritePerson:error:]_block_invoke + 44
5 CoreFoundation 0x00000001059ad2fd __53-[__NSArrayI enumerateObjectsWithOptions:usingBlock:]_block_invoke + 77
6 CoreFoundation 0x00000001059ad1df -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] + 207
7 Contacts 0x000000010dc3e0f4 -[CNContact(iOSABCompatibility) overwritePerson:error:] + 240
8 Contacts 0x000000010dc3dfc0 -[CNContact(iOSABCompatibility) detachedPersonWithError:] + 46
9 AddressBookUI 0x00000001057bdd77 -[ABPeoplePickerNavigationController contactPicker:didSelectContact:] + 145
10 ContactsUI 0x0000000112396eb2 -[CNContactPickerViewController pickerDidSelectContact:property:] + 306
11 ContactsUI 0x000000011243ee6f -[CNContactPickerHostViewController pickerDidSelectContact:property:] + 95
12 ContactsUI 0x000000011243f5ec __71-[CNContactPickerExtensionHostContext pickerDidSelectContact:property:]_block_invoke + 66
I have already added NSContactsUsageDescription in the info.plist as discussed on Contact Address book crash on iOS 10 beta but that didn't help and I can't use CNContactPickerViewController as I need to support iOS8 devices.

Imran Raheem
From Erdekhayser's solution (Contact Address book crash on iOS 10 beta)
you can use this method to check CNContactPickerViewController is available?
if (NSClassFromString(#"CNContactPickerViewController")) {
// iOS 9, 10, use CNContactPickerViewController
CNContactPickerViewController *picker = [[CNContactPickerViewController alloc] init];
picker.delegate = self;
picker.displayedPropertyKeys = #[CNContactPhoneNumbersKey];
[pr presentViewController:picker animated:YES completion:nil];
}else{
// iOS 8 Below, use ABPeoplePickerNavigationController
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[pr presentViewController:picker animated:YES completion:nil];
}

The Address Book API was deprecated in iOS 9 in favor of the more object-oriented Contacts Framework.
Instead of using the ABPeoplePickerViewController, move to CNContactPickerViewController.

I was getting the same error, when I was trying to get an emailAddresses from CNContact of delegate method.
Initially, I initialize the contactpicker:
//MARK: Contact Action
#IBAction func getContactListAction(_ sender: Any) {
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
contactPicker.displayedPropertyKeys = [CNContactPhoneNumbersKey]
vcObject.present(contactPicker, animated: true, completion: nil)
}
Delegate method:
//MAKE: Contact Delegate
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
picker.dismiss(animated: true, completion: nil)
let name = CNContactFormatter.string(from: contact, style: .fullName)
print(name!)
self.textfieldName.text = name!
for number in contact.phoneNumbers {
print("number ----\(number)")
let mobile = number.value.value(forKey: "digits") as? String
if (mobile?.count)! > 7 {
// your code goes here
print("mobile---\(String(describing: mobile))")
self.textfieldMobileNumber.text = mobile!
}
}
// this line couse the crash ---> print(contact.emailAddresses[0].value(forKey: "value") as! String)
}
I was accessing the email address without declaring in initialization.
Error -- Terminating app due to uncaught exception 'CNPropertyNotFetchedException', reason: 'A property was not requested when contact was fetched.'
CORRECT CODE FOR ACCESSING EMAIL ---
Xcode 10 . and 4.2
//MARK: Contact Action
#IBAction func getContactListAction(_ sender: Any) {
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
contactPicker.displayedPropertyKeys = [CNContactPhoneNumbersKey,CNContactEmailAddressesKey] .
// <--- Here make declaration for accessing the required property from CNContact.
vcObject.present(contactPicker, animated: true, completion: nil)
}
//MAKE: Contact Delegate
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
picker.dismiss(animated: true, completion: nil)
let name = CNContactFormatter.string(from: contact, style: .fullName)
print(name!)
self.textfieldName.text = name!
for number in contact.phoneNumbers {
print("number ----\(number)")
let mobile = number.value.value(forKey: "digits") as? String
if (mobile?.count)! > 7 {
// your code goes here
print("mobile---\(String(describing: mobile))")
self.textfieldMobileNumber.text = mobile!
}
}
// print(contact.emailAddresses[0].value.value(forKey: "labelValuePair") as! String)
for email in contact.emailAddresses {
print("number ----\(email)")
let eml = email.value(forKey: "value") as? String
print("eml --\(eml!)")
}
}

Related

Unknown cause of crash, Xcode points to unknown function: Array._getElement(_:wasNativeTypeChecked:matchingSubscriptCheck:) + 12 (AppDelegate.swift:0)

I am investigating a crash reported through Xcode crash logs for my app that's live on the app store. In Xcode it points to line #5 with that has the line Array.getElement(:wasNativeTypeChecked:matchingSubscriptCheck:) + 12 (AppDelegate.swift:0). I can't find any information on this function. Because of the lines around it, I have investigated the App Delegate, but nothing is popping out to me as an issue. I am at a loss with how to dive into solving this bug. The main thing I gather is that it occurs when the app receives a remote notification.
Any thoughts on how to investigate this issue or what could be happening would be appreciated.
Here is the crash log
<!-- language: none -->
Incident Identifier: 3406EE83-04DB-4B61-B2E5-758F04A63614
Hardware Model: iPhone13,4
Process: FitSW [5443]
Path: /private/var/containers/Bundle/Application/4ECB1953-2547-4511-BA9B-D1BDFA9B8155/FitSW.app/FitSW
Identifier: FitSW
Version: 3.97 (1)
AppStoreTools: 14C17
AppVariant: 1:iPhone13,4:15
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: FitSW [1058]
Date/Time: 2023-01-13 19:13:15.6975 +0300
Launch Time: 2023-01-13 18:36:32.1730 +0300
OS Version: iPhone OS 15.1.1 (19B81)
Release Type: User
Baseband Version: 2.11.04
Report Version: 104
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Last Exception Backtrace:
0 CoreFoundation 0x18112504c __exceptionPreprocess + 220 (NSException.m:200)
1 libobjc.A.dylib 0x199799f54 objc_exception_throw + 60 (objc-exception.mm:565)
2 CoreFoundation 0x1811acc98 -[__NSArray0 objectAtIndex:] + 112 (CFArray.c:103)
3 libswiftCore.dylib 0x185ada7ac _CocoaArrayWrapper.subscript.getter + 36 (CocoaArray.swift:59)
4 FitSW 0x102d545b4 specialized _ArrayBuffer._getElementSlowPath(_:) + 208
5 FitSW 0x102b451d8 specialized Array._getElement(_:wasNativeTypeChecked:matchingSubscriptCheck:) + 12 (AppDelegate.swift:0)
6 FitSW 0x102b451d8 specialized Array.subscript.getter + 12 (AppDelegate.swift:210)
7 FitSW 0x102b451d8 specialized AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + 4840
8 FitSW 0x102b428d8 specialized AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + 16 (<compiler-generated>:0)
9 FitSW 0x102b428d8 #objc AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + 144
10 GoogleUtilities 0x104b2f504 -[GULAppDelegateSwizzler application:donor_didReceiveRemoteNotification:fetchCompletionHandler:] + 432 (GULAppDelegateSwizzler.m:915)
11 UIKitCore 0x183772464 -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion:] + 8016 (UIApplication.m:10900)
12 UIKitCore 0x183712d50 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 496 (UIScene.m:1492)
13 UIKitCore 0x183756524 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 288 (UIScene.m:1774)
14 UIKitCore 0x1836e0370 -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 492 (UIApplicationSceneClientAgent.m:80)
15 FrontBoardServices 0x192c00100 -[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:] + 528 (FBSScene.m:549)
16 FrontBoardServices 0x192c18d4c __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke_2 + 152 (FBSWorkspaceScenesClient.m:581)
17 FrontBoardServices 0x192bfd6b4 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 240 (FBSWorkspace.m:352)
18 FrontBoardServices 0x192c03b10 __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke + 396 (FBSWorkspaceScenesClient.m:580)
19 libdispatch.dylib 0x180d97660 _dispatch_client_callout + 20 (object.m:560)
20 libdispatch.dylib 0x180d9b118 _dispatch_block_invoke_direct + 264 (queue.c:489)
21 FrontBoardServices 0x192bfef94 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 48 (FBSSerialQueue.m:157)
22 FrontBoardServices 0x192bfe3d4 -[FBSSerialQueue _targetQueue_performNextIfPossible] + 220 (FBSSerialQueue.m:181)
23 FrontBoardServices 0x192c029e4 -[FBSSerialQueue _performNextFromRunLoopSource] + 28 (FBSSerialQueue.m:194)
24 CoreFoundation 0x181147020 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1972)
25 CoreFoundation 0x181157ce0 __CFRunLoopDoSource0 + 208 (CFRunLoop.c:2016)
26 CoreFoundation 0x181091fe8 __CFRunLoopDoSources0 + 268 (CFRunLoop.c:2053)
27 CoreFoundation 0x1810977f4 __CFRunLoopRun + 820 (CFRunLoop.c:2951)
28 CoreFoundation 0x1810ab3b8 CFRunLoopRunSpecific + 600 (CFRunLoop.c:3268)
29 GraphicsServices 0x19ca3b38c GSEventRunModal + 164 (GSEvent.c:2200)
30 UIKitCore 0x183a4b6a8 -[UIApplication _run] + 1100 (UIApplication.m:3493)
31 UIKitCore 0x1837ca7f4 UIApplicationMain + 2092 (UIApplication.m:5046)
32 FitSW 0x1025a0afc main + 68 (AppDelegate.swift:22)
33 dyld 0x10348da24 start + 520 (dyldMain.cpp:876)
Thread 0 name:
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001b80ca964 __pthread_kill + 8 (:-1)
1 libsystem_pthread.dylib 0x00000001f1cb4378 pthread_kill + 268 (pthread.c:1610)
2 libsystem_c.dylib 0x000000018bf62f50 abort + 164 (abort.c:118)
3 libc++abi.dylib 0x00000001998a1bc4 abort_message + 132 (abort_message.cpp:78)
4 libc++abi.dylib 0x0000000199892fd8 demangling_terminate_handler() + 332 (cxa_default_handlers.cpp:71)
5 libobjc.A.dylib 0x00000001997a0064 _objc_terminate() + 144 (objc-exception.mm:701)
6 libc++abi.dylib 0x00000001998a0f58 std::__terminate(void (*)()) + 20 (cxa_handlers.cpp:59)
7 libc++abi.dylib 0x00000001998a0ef4 std::terminate() + 64 (cxa_handlers.cpp:88)
8 libdispatch.dylib 0x0000000180d97674 _dispatch_client_callout + 40 (object.m:563)
9 libdispatch.dylib 0x0000000180d9b118 _dispatch_block_invoke_direct + 264 (queue.c:489)
10 FrontBoardServices 0x0000000192bfef94 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 48 (FBSSerialQueue.m:157)
11 FrontBoardServices 0x0000000192bfe3d4 -[FBSSerialQueue _targetQueue_performNextIfPossible] + 220 (FBSSerialQueue.m:181)
12 FrontBoardServices 0x0000000192c029e4 -[FBSSerialQueue _performNextFromRunLoopSource] + 28 (FBSSerialQueue.m:194)
13 CoreFoundation 0x0000000181147020 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1972)
14 CoreFoundation 0x0000000181157ce0 __CFRunLoopDoSource0 + 208 (CFRunLoop.c:2016)
15 CoreFoundation 0x0000000181091fe8 __CFRunLoopDoSources0 + 268 (CFRunLoop.c:2053)
16 CoreFoundation 0x00000001810977f4 __CFRunLoopRun + 820 (CFRunLoop.c:2951)
17 CoreFoundation 0x00000001810ab3b8 CFRunLoopRunSpecific + 600 (CFRunLoop.c:3268)
18 GraphicsServices 0x000000019ca3b38c GSEventRunModal + 164 (GSEvent.c:2200)
19 UIKitCore 0x0000000183a4b6a8 -[UIApplication _run] + 1100 (UIApplication.m:3493)
20 UIKitCore 0x00000001837ca7f4 UIApplicationMain + 2092 (UIApplication.m:5046)
21 FitSW 0x00000001025a0afc main + 68 (AppDelegate.swift:22)
22 dyld 0x000000010348da24 start + 520 (dyldMain.cpp:876)
Here is the function in question
Line 6 on the crash log highlights the line that follows the MARK
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if let messageID = userInfo["google.c.a.e"] {
print("Message ID: \(messageID)")
}
if let newMessageFlag = userInfo["newMessageFlag"] as? String {
let newMessageFlagInt = (Int(newMessageFlag) ?? 0)
UserDefaults.standard.set(newMessageFlag, forKey: "newMessageFlag")
var newNotificationsInt = 0
if let newNotifications = UserDefaults.standard.value(forKey: "newNotificationFlag") as? String {
newNotificationsInt = Int(newNotifications) ?? 0
}
UIApplication.shared.applicationIconBadgeNumber = newMessageFlagInt + newNotificationsInt
// guard let tabBarController = window?.rootViewController as? UITabBarController else { return }
let applicationState = UIApplication.shared.applicationState
if applicationState != .active {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
guard let tabBarController = self.window?.rootViewController as? MainStartingTabbarViewController else { return }
let navigationController = tabBarController.viewControllers![4] as? UINavigationController // Regardless of the other custom app versions & the user type, the more menu will be the fifth tab
navigationController?.tabBarItem.badgeValue = String(newMessageFlagInt + newNotificationsInt)
var moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController
moreTab?.messagesFlagInt = newMessageFlagInt
tabBarController.selectedViewController = tabBarController.viewControllers![4]
let numberOfTabs = tabBarController.viewControllers
let isGroup = userInfo["is_private"] as? String == "0"
let roomIDInt = Int(userInfo["room_id"] as? String ?? "") ?? 0
moreTab?.openMessages(didSelectGroup: isGroup, isShared: false, roomID: roomIDInt)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
}
}
else {
guard let tabBarController = self.window?.rootViewController as? MainStartingTabbarViewController else { return }
if (tabBarController.selectedViewController == tabBarController.viewControllers![4]) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
} else {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateTrainerButton"), object: nil)
}
}
}
else if let newNotificationFlag = userInfo["newNotificationFlag"] as? String {
let newNotificationFlagInt = (Int(newNotificationFlag) ?? 0)
UserDefaults.standard.set(newNotificationFlag, forKey: "newNotificationFlag")
var newMessagesInt = 0
//MARK: - This is the line that breaks
if let newMessages = UserDefaults.standard.value(forKey: "newMessageFlag") as? String {
newMessagesInt = Int(newMessages) ?? 0
}
UIApplication.shared.applicationIconBadgeNumber = newMessagesInt + newNotificationFlagInt
guard let tabBarController = self.window?.rootViewController as? UITabBarController else { return }
let navigationController = tabBarController.viewControllers![4] as? UINavigationController // Regardless of the other custom app versions & the user type, the more menu will be the fifth tab
navigationController?.tabBarItem.badgeValue = String(newMessagesInt + newNotificationFlagInt)
let moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController
moreTab?.notificationsFlagInt = newNotificationFlagInt
let applicationState = UIApplication.shared.applicationState
if applicationState != .active {
tabBarController.selectedViewController = tabBarController.viewControllers![4]
moreTab?.openNotifications()
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
}
else {
if (tabBarController.selectedViewController == tabBarController.viewControllers![4]) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
} else {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateTrainerButton"), object: nil)
}
}
}
completionHandler(UIBackgroundFetchResult.newData)
}
I have tried to recreate the problem by running the simulator and opening the app when it receives a remote notification without any success in getting the same error.
Edit Solution:
Thank you to #jrturton for such a helpful and insightful answer. This helped me on many different levels. I had been running through "wait for executable to run", but a little more thorough testing did uncover an error, though it still wasn't the one I posted about.
What I had overlooked was the line the crash report was telling me was the issue was on a previous version than the one I was looking at. When I went back to the version, it was in fact the line on code that you pointed out. So I put a dispatchQueue.asyncAfter in to let it load, and placed a guard statement on the count just in case it still doesn't load in time.
Problematic Code:
let moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController
Fixed Code:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
UIApplication.shared.applicationIconBadgeNumber = newMessagesInt + newNotificationFlagInt
guard let tabBarController = self.window?.rootViewController as? UITabBarController,
(tabBarController.viewControllers?.count ?? 0) >= 5,
let navigationController = tabBarController.viewControllers![4] as? UINavigationController,
navigationController.viewControllers.count >= 1 else {
print("hit error with tabBaar or Nav controller")
return }
navigationController.tabBarItem.badgeValue = String(newMessagesInt + newNotificationFlagInt)
let moreTab = navigationController.viewControllers[0] as? MoreMenuTableViewController
moreTab?.notificationsFlagInt = newNotificationFlagInt
let applicationState = UIApplication.shared.applicationState
if applicationState != .active {
tabBarController.selectedViewController = tabBarController.viewControllers![4]
moreTab?.openNotifications()
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
}
else {
if (tabBarController.selectedViewController == tabBarController.viewControllers![4]) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
} else {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateTrainerButton"), object: nil)
}
}
}
I don't think the line you've highlighted is the crashing line. The crash log points to an array access, and the line you've highlighted isn't doing that.
It seems far more likely that the problem is on one of the many times you're accessing tab view controller or navigation view controller child view controllers via index - those are the only array accessors I can see in that function. Lines like this:
let moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController
This line will crash if navigationController exists, but it has no child controller.
My guess is that this is happening when your app is being launched via the remote notification, not necessarily receiving the notification while it is open. That might mean this code is running before your app has finished launching and loading its view controllers.
Debugging notification issues can be tricky, but you can help yourself by creating an .apns file on your desktop containing the notification payload (see https://gist.github.com/muhammetkole/22cf3cc8a7b026f2ea2661b8497b773e for an example, you'll need to fill in your own details).
You can then drop this file on the simulator and it will act as if the notification has been received. You can test this with the app running, or in the background, while it's connected to the debugger.
To debug the process of launching from a notification, kill the app in the simulator, then edit the scheme / info / launch to "wait for executable to run". Build and "run" the project, then drop the file on the simulator, and it will launch the app and connect the debugger.
Hopefully one of those scenarios will allow you to reproduce the bug.

App store link in MFMessageComposeViewController shows old image

I have added the URL string for my app to the body of the MFMessageComposeViewController but when I run the app and send a message the image is an old image from the app store. How can I get it to use the current image from the app store.
fileprivate func textMessage() {
let appURL_Id = "https://apps.apple.com/us/app/simple-password-keeper/id1326895566"
let canSend = MFMessageComposeViewController.canSendText()
if canSend == true {
let messageVC = MFMessageComposeViewController()
messageVC.body = passwordString + "\n\n" + "Sent by Simple Password Keeper" + "\n\n" + appURL_Id
messageVC.messageComposeDelegate = self
self.present(messageVC, animated: true, completion: nil)
}else{
cantSendTextOrEmailAlert(alertTitle: multiUse.alertTitle, alertMessage: multiUse.messageSMS)
}
}
And it needs to be.
What is happening is that in iTunes Connect there is an old preview that was not removed. Look under the View All Sizes Media Manager.

Sending photo using MFMessageComposeViewController is disabled in ios10

I have message send functionality in my app and implemented the same using MFMessageComposeViewController. I am able to attach photos with the message in iOS9 but not in iOS 10? Is there anyone having the same issue?
Swift 5.0 version:
Call the below method named displayMessageInterface:
- Important to note:
composeViewController.addAttachmentData(dataImage!, typeIdentifier: "image/png", filename: "ImageData.png")
In the above line, filename must be of type abc.png in my case or abc.jpeg if you are using a jpeg image data and typeIdentifier must follow image/png and image/jpeg respectively. I struggled a lot to find out this. The reason I write this answer even when other answers are enough already.
For more information on typeIdentifiers, use this link: https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html#//apple_ref/doc/uid/TP40009259-SW1
fileprivate func displayMessageInterface() {
if MFMessageComposeViewController.canSendText() {
let composeViewController = MFMessageComposeViewController()
composeViewController.messageComposeDelegate = self
composeViewController.body = "Enter your text body here."
if MFMessageComposeViewController.canSendAttachments() {
let image = UIImage(named: "image-name")!
let dataImage = image.pngData()
guard dataImage != nil else {
return
}
composeViewController.addAttachmentData(dataImage!, typeIdentifier: "image/png", filename: "ImageData.png")
}
self.present(composeViewController, animated: true)
} else {
print("Can't send messages.")
}
}
Since I have mentioned the delegate in the above method, you can use it this way in case of a UIViewController:
extension UIViewController: MFMessageComposeViewControllerDelegate {
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
if result == .failed {
print("could not send message")
}
self.dismiss(animated: true)
}
}
Please find the below code as image attachment and I had successfully run on iOS 10.
- (void)sendImgAttachment {
if([MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init]; // Create message VC
messageController.messageComposeDelegate = self; // Set delegate to current instance
NSMutableArray *recipients = [[NSMutableArray alloc] init]; // Create an array to hold the recipients
[recipients addObject:#"555-555-5555"]; // Append example phone number to array
messageController.recipients = recipients; // Set the recipients of the message to the created array
messageController.body = #"Example message"; // Set initial text to example message
NSData *dataImg = UIImagePNGRepresentation([UIImage imageNamed:#"logoApple"]);//Add the image as attachment
[messageController addAttachmentData:dataImg typeIdentifier:#"public.data" filename:#"Image.png"];
[self presentViewController:messageController animated:YES completion:NULL];
}
}
Please find the screenshot for the same.
Hope it works for you!!!
i found this solution for me :
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: "sms:123456789")!, options: [:], completionHandler: nil)
} else {
// Fallback on earlier versions
if MFMessageComposeViewController.canSendText() {
if MFMessageComposeViewController.canSendAttachments() {
print("canSendAttachments")
}
let messageVC = MFMessageComposeViewController()
messageVC.body = "Enter a message";
messageVC.recipients = ["123456789"]
messageVC.messageComposeDelegate = self
messageVC.accessibilityActivate()
self.present(messageVC, animated: false, completion: nil)
} else {
print("Cant send sms")
}
}

Opening a new window on a different storyboard

I am developing an OS X app with storyboards. I get the Preview.storyboard with an Entry Point on an anonymous window with a custom ViewController. In the AppDelegate class, I get the following function.
func newPreviewWindow(sender: AnyObject) {
let storyboard = NSStoryboard.init(name: "Preview", bundle: nil)
let initialController = storyboard.instantiateInitialController()
initialController!.showWindow(nil)
initialController!.makeKeyAndOrderFront(nil)
}
When running the code, the window shows, but I get the following exception:
2016-08-11 10:27:12.434 MyApp[1090:290439] -[NSWindowController makeKeyAndOrderFront:]: unrecognized selector sent to instance 0x60000008c350
2016-08-11 10:27:12.434 MyApp[1090:290439] -[NSWindowController makeKeyAndOrderFront:]: unrecognized selector sent to instance 0x60000008c350
2016-08-11 10:27:12.440 MyApp[1090:290439] (
0 CoreFoundation 0x00007fff926284f2 __exceptionPreprocess + 178
1 libobjc.A.dylib 0x00007fff97c0ef7e objc_exception_throw + 48
2 CoreFoundation 0x00007fff926921ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00007fff92598571 ___forwarding___ + 1009
4 CoreFoundation 0x00007fff925980f8 _CF_forwarding_prep_0 + 120
5 MyApp 0x0000000100005617
...
Based on the exception message and search on Google and StackOverflow, I tried sending a Selector to the makeKeyAndOrderFront function this way :
func newPreviewWindow(sender: AnyObject) {
let storyboard = NSStoryboard.init(name: "Preview", bundle: nil)
let initialController = storyboard.instantiateInitialController()
let selector = #selector(AppDelegate.newPreviewWindow(_:))
initialController!.showWindow(nil)
initialController!.makeKeyAndOrderFront(selector) // [A]
}
But then I get the following compile error on line [A]: Cannot call value of non-function type '((AnyObject?) -> Void)!'
How is the proper way to open the new window or to pass the Selector?
Thanks! :)
initialController!.makeKeyAndOrderFront(nil) is causing a problem because makeKeyAndOrderFront: is not an NSWindowController method - it belongs to NSWindow (hence the unrecognized selector error). Cast your initial controller to your NSWindowController subclass, then bring the window to the front via the controller's window property:
var windowController: NSWindowController!
#IBAction func showOtherWindow(sender: AnyObject) {
windowController = storyboard.instantiateInitialController() as! NSWindowController
windowController.window?.makeKeyAndOrderFront(nil)
}

Cannot convert type '[String: Any Object]' to expected type 'String'

Trying to get the phone number string out of CNContacts. I pull up a contact picker view controller and when the user selects multiple contacts, I create a message compose view controller. I need to create an array of strings to pass along as the recipients of the message compose view controller. Error comes from the following line...contactsPhoneNumber.append(phoneNumber)
func AddFriendTapped() {
let contactPickerViewController = CNContactPickerViewController()
contactPickerViewController.delegate = self
presentViewController(contactPickerViewController, animated: true, completion: nil)
}
func contactPicker(picker: CNContactPickerViewController,didSelectContacts contacts: [CNContact]) {
//check if phone can send texts, if so, continue
if !MFMessageComposeViewController.canSendText(){
let composeVC = MFMessageComposeViewController()
composeVC.messageComposeDelegate = self
//must get phone number strings from CNContact
let phoneNumberKey = [CNContactPhoneNumbersKey]
for contact in contacts {
var phoneNumber = contact.dictionaryWithValuesForKeys(phoneNumberKey)
contactsPhoneNumber.append(phoneNumber)
}
composeVC.recipients = contactsPhoneNumber
composeVC.body = "Hi, test message"
// Present the view controller modally.
dismissViewControllerAnimated(true) {
self.presentViewController(composeVC, animated: true, completion: nil)
}
}
}
func messageComposeViewController(controller: MFMessageComposeViewController,
didFinishWithResult result: MessageComposeResult) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismissViewControllerAnimated(true, completion: nil)
}
A contact can have multiple phone numbers so contact.phoneNumbers returns an array of CNlabeledValue. You need two loops, one to iterate all the contacts other to iterate all the numbers. Then you have to extract the phone number which is of type CNPhoneNumber and then convert it to string.
I have made some changes in your code. Hope it helps. :)
func contactPicker(picker: CNContactPickerViewController,didSelectContacts contacts: [CNContact]) {
//check if phone can send texts, if so, continue
if !MFMessageComposeViewController.canSendText(){
let composeVC = MFMessageComposeViewController()
composeVC.messageComposeDelegate = self
//must get phone number strings from CNContact
//let phoneNumberKey = [CNContactPhoneNumbersKey]
for contact in contacts {
let contactNumberArray = contact.phoneNumbers
for contactNumber in contactNumberArray{
let number = contactNumber.value as! CNPhoneNumber
contactsPhoneNumber.append(number.stringValue)
}
}
composeVC.recipients = contactsPhoneNumber
composeVC.body = "Hi, test message"
// Present the view controller modally.
dismissViewControllerAnimated(true) {
self.presentViewController(composeVC, animated: true, completion: nil)
}
}
}