Sending photo using MFMessageComposeViewController is disabled in ios10 - 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")
}
}

Related

NSUserActivity deleteAllSavedUserActivities not working

My case is utterly simple: I use the next function
private func launchActivity(_ id: String, title: String, invocPhrase: String) {
userActivity = NSUserActivity(activityType: "Open_bank")
userActivity?.title = title
userActivity?.userInfo = ["id": id]
if #available(iOS 12.0, *) {
userActivity?.suggestedInvocationPhrase = invocPhrase
userActivity?.isEligibleForPrediction = true
userActivity?.persistentIdentifier = id
} else {
//Can't actually invoke this block
}
}
to create a certain userActivity, and then add it to Siri, so that it can be invoked by by invocPhrase. Here is the function which does this.
func presentAddOpenBankToSiriVC() {
guard let userActivity = self.userActivity else { return }
if #available(iOS 12.0, *) {
let shortcut = INShortcut(userActivity: userActivity)
let viewController = INUIAddVoiceShortcutViewController(shortcut: shortcut)
viewController.modalPresentationStyle = .formSheet
viewController.delegate = self
present(viewController, animated: true, completion: nil)
} else {
//Can't actually invoke this block
}
}
Later I try to delete it (as well as all other user activities)
NSUserActivity.deleteAllSavedUserActivities {}
And it just does not delete any user activity, contrary to what's written in Apple Documentation
https://developer.apple.com/documentation/sirikit/deleting_donated_shortcuts
Actually, at first, I've tried a method
deleteSavedUserActivities(withPersistentIdentifiers:completionHandler:)
with userActivity's persistentIdentifier, but, obviously, also to no avail.
I've no idea why it refuses to budge but would be grateful to any help or hint

How Save UILocalNotifications in CoreData

Answer is below, image is here:
I was searching how to do this for a couple of days and was only able to find people who stored UILocalNotificaations in NSUserDefaults. Saving these in NSUserDefaults seemed wrong to me because it is supposed to be used for small flags. I just now finally figured out how to store notifications in CoreData. This is Using Xcode 7.3.1 and Swift 2.2
First off you need to create a new entity in your CoreDataModel
and then add a single attribute to it. the attribute should be of type Binary Data I named my table/entity "ManagedFiredNotifications" and my attribute "notification". it should look like this:
Image linked in Question above.
Next you need to add an extension to UILocalNotification it should go like this:
extension UILocalNotification {
func save() -> Bool {
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
let firedNotificationEntity = NSEntityDescription.insertNewObjectForEntityForName("ManagedFiredNotifications", inManagedObjectContext: appDelegate!.managedObjectContext)
guard appDelegate != nil else {
return false
}
let data = NSKeyedArchiver.archivedDataWithRootObject(self)
firedNotificationEntity.setValue(data, forKey: "notification")
do {
try appDelegate!.managedObjectContext.save()
return true
} catch {
return false
}
}
}
Now for saving a notification all you need to do is call
UILocalNotification.save()
On the notification you would like to save. my notifications were named 'notification' so I would call notification.save()
To retrieve a notification you need a method like this
func getLocalFiredNotifications() -> [UILocalNotification]? {
let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)!.managedObjectContext
let firedNotificationFetchRequest = NSFetchRequest(entityName: "ManagedFiredNotifications")
firedNotificationFetchRequest.includesPendingChanges = false
do {
let fetchedFiredNotifications = try managedObjectContext.executeFetchRequest(firedNotificationFetchRequest)
guard fetchedFiredNotifications.count > 0 else {
return nil
}
var firedNotificationsToReturn = [UILocalNotification]()
for managedFiredNotification in fetchedFiredNotifications {
let notificationData = managedFiredNotification.valueForKey("notification") as! NSData
let notificationToAdd = NSKeyedUnarchiver.unarchiveObjectWithData(notificationData) as! UILocalNotification
firedNotificationsToReturn.append(notificationToAdd)
}
return firedNotificationsToReturn
} catch {
return nil
}
}
Note that this returns an array of UILocalNotifications.
When retrieving these if you plan on removing a few of them and then storing the list again you should remove them when you get them something like this works:
func loadFiredNotifications() {
let notifications = StudyHelper().getLocalFiredNotifications()
if notifications != nil {
firedNotifications = notifications!
} else {
// throw an error or log it
}
classThatRemoveMethodIsIn().removeFiredLocalNotifications()
}
I hope this helps someone who had the same problems that I did trying to implement this.

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)
}
}
}

Log list of available printers and their URLs

I am attempting to set up a UIPrinter instance so that my iPad app can print directly to that printer without having to present the print controller dialogue. The problem I'm having is that I can't seem to find the URL of this printer. It is connected via AirPrint.
Visiting http://localhost:631/printers/ does show the printer, but it shows the USB version of the printer's URL (i.e. usb://Brother/QL-710W?serial=12345).
What I am wondering is, how can I print (to the debug output) a list of my available printers, and their URLs? I figure by doing this I can then locate my printer's AirPrint URL and go from there.
Thanks!
Here's a simplified version in Swift 3 for anyone stumbling upon this same question in 2017:
let pickerController = UIPrinterPickerController(initiallySelectedPrinter: nil)
pickerController.present(animated: true) { (controller, completed, error) in
if completed == true {
print(controller.selectedPrinter!.url)
}
}
This might not be the best way to do it, but I ended up displaying the Printer Picker Controller, then printing (to the debug area) the URL of the selected UIPrinter:
let pickerController = UIPrinterPickerController(initiallySelectedPrinter: nil)
pickerController.presentFromRect(CGRectMake(0, 0, 300, 500), inView: self, animated: true) { (controller:UIPrinterPickerController!, completed:Bool, error:NSError!) -> Void in
println(controller.selectedPrinter?.URL)
}
Open to suggestions if there is a better way!
Here is what I did.
Global Var
var ReceiptPrinterHolder = NSURL()
var currentPrinter: UIPrinter?
var ReceiptPrinter: UIPrinter?
func Works(){
let printerPicker = UIPrinterPickerController(initiallySelectedPrinter: currentPrinter2)
printerPicker.presentFromRect(CGRectMake(0, 0, 300, 500), inView: view, animated: true, completionHandler: {
(printerPicker, userDidSelect, error) in
if userDidSelect {
var selectedPrinter: UIPrinter? { return printerPicker.selectedPrinter }
currentPrinter = selectedPrinter
self.DisplaySelectedAction()
}else{
print("Did not work")
}
})
// return currentPrinter2!
}
#IBAction func ReceiptPrinterAction() {
Works()
if currentPrinter != nil {
Label2.text = "Receipt Printer \(ReceiptPrinter!.displayName)"
ReceiptPrinter = currentPrinter
ReceiptPrinterHolder = ReceiptPrinter!.URL
}
}

Headphones plugin/out detection in Swift

im working on an iphone app for iOS 8.1 that works with core audio to generate frequencies and adjust intensities. In the view controller that i generate the frequencies i need to control if the headphones are plugged out in some moment, i'm already controlling if headphones are connected before proceed to my frequencies generator view with the following function:
- (BOOL)isHeadsetPluggedIn {
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription* desc in [route outputs]) {
if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
return YES;
}
return NO;
}
this function is in C because im working with core-audio to generate the frequencies, but in the view controllers im working with swift so a need a way to implement a listener to detect the headphones plug-out event and return to the user to the previous view, i don't know if i can use my function isHeadsetPluggedin() with an event listener or i should make a new one.
In my MenuViewController i control if the headphones are plugged in using the following function:
func isHeadsetPluggedIn() -> Bool {
return freqController.isHeadsetPluggedIn();
}
In Swift 4
func activateHeadPhonesStatus(){
NotificationCenter.default.addObserver(self, selector: #selector(audioRouteChangeListener(_:)), name: AVAudioSession.routeChangeNotification, object: nil)
}
#objc func audioRouteChangeListener(_ notification:Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable:
let session = AVAudioSession.sharedInstance()
for output in session.currentRoute.outputs where output.portType == AVAudioSession.Port.headphones {
headphonesConnected = true
print("headphone plugged in")
break
}
case .oldDeviceUnavailable:
if let previousRoute =
userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
for output in previousRoute.outputs where output.portType == AVAudioSession.Port.headphones {
headphonesConnected = false
print("headphone pulled out")
break
}
}
default: ()
}
}
You can track the route changes by observing AVAudioSessionRouteChangeNotification notification.
//Observe for route changing notification
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(handleRouteChange:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
-(void)handleRouteChange:(NSNotification *)notif
{
NSDictionary *dict = notif.userInfo;
AVAudioSessionRouteDescription *routeDesc = dict[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *prevPort = [routeDesc.outputs objectAtIndex:0];
if ([prevPort.portType isEqualToString:AVAudioSessionPortHeadphones]) {
//Head phone removed
}
}
This article worked for me. There is also a GitHub repo with solution. If you don't want to read, here is my code:
Put this in your INIT method:
self.session = AVAudioSession.sharedInstance()
let currentRoute = self.session.currentRoute
if currentRoute.outputs.count != 0 {
for description in currentRoute.outputs {
if description.portType == AVAudioSessionPortHeadphones {
print("headphone plugged in")
} else {
print("headphone pulled out")
}
}
} else {
print("requires connection to device")
}
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(YOUR_VIEW_CONTROLLER_OR_VIEW.audioRouteChangeListener(_:)),
name: AVAudioSessionRouteChangeNotification,
object: nil)
And put this anywhere in your class:
dynamic private func audioRouteChangeListener(notification:NSNotification) {
let audioRouteChangeReason = notification.userInfo![AVAudioSessionRouteChangeReasonKey] as! UInt
switch audioRouteChangeReason {
case AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue:
print("headphone plugged in")
case AVAudioSessionRouteChangeReason.OldDeviceUnavailable.rawValue:
print("headphone pulled out")
default:
break
}
}
Take care!