Send SMS in iOS - swift

I am brand new in iOS developing and xCode.
Previously i developed android and i know i should work with Intent to make call and send SMS in android App.
Now i want to develop a simple App that when i press a Button it send a SMS to a specific number.
So i installed a Mac OS 10.11 VM on my Ubuntu.
And could connect connect my iPhone 5 to that and run my simple Hello World App in real iPhone Device and not simulator.
Now i created a Button in StoryBoard and make a funcion by following this article :
Send SMS Message Toturial
Also look at other links and was similar.
Here is my simple ViewController.swift.
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sendMessage(sender: AnyObject) {
print("Send button pressed") //display a text to make sure is calling
if MFMessageComposeViewController.canSendText() {
let controller = MFMessageComposeViewController()
controller.body = "TestMessage "
controller.recipients = ["xxxxxxxxx", "xxxxxxxxx"] // sending for two numbers
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
print("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
print("Message failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
print("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}
}
And also created a Simple Button and link that to sendMessage above.
When i run the app, displays the button and when i press it, it seems it is doing something, but the SMS is not sent.
Any Idea??
I really appreciate it.
Update : As our friend said i added print("Send button pressed") in sendMessage Button, so i find out the button call sendMessage when i press it.

I finally did it, I mixed up two solutions,
First of all,i changed the function of sendMessage and renamed it to sendText, in this way :
#IBAction func sendText(sender: AnyObject) {
if (MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = "Text Body"
controller.recipients = ["xxxxxxxxxxx"]
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
And the protocol of MessageComposeViewController is :
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
print("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
print("Message failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
print("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}
And then click on the button in storyboards, and press control key and drag the button to ViewController and select the sendText function, now it works correctly.
Thx to my Friend #Kabiroberai for help and give some time.

Heres the bottom function converted to swift 4:
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResult.cancelled.rawValue:
print("Message was cancelled")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
print("Message failed")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
print("Message was sent")
self.dismiss(animated: true, completion: nil)
default:
break;
}
}

Related

Dismiss alert after task on MainViewController swift

I need to download some content from API when users login for the first time at my app and show them I'm doing that. I do this at my MainViewController:
override func viewDidAppear(_ animated: Bool) {
let alert = UIAlertController(title: nil, message: "Wait please...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
let parceiroId = self.defaults.getParceiroId()
if !self.defaults.getDownloadInicialTipoEntrega() {
TipoEntregaAPI().loadTiposEntrega(parceiroId){ (dados) in
if dados != nil {
for tipoEntrega in dados!{
// Do some stuff, no errors
}
}
}
}
if !self.defaults.getDownloadInicialPedido() {
PedidoAPI().loadOrders(parceiroId){ (dados) in
if dados != nil {
for pedidos in dados!{
// Do some stuff, no errors
}
}
}
}
self.dismiss(animated: false, completion: { () in print("Done") })
}
The problem is that my alert with the loading never gets dismissed. It never prints "Done". Can anyone help me please?
I dont know if it is useful, but I always get this warning:
Warning: Attempt to dismiss from view controller <MyApp.MainViewController: 0x0000000> while a presentation or dismiss is in progress!
The problem is exactly what the error says. Your presentation call isn't completed at the moment you're calling self.dismiss(...). For further explanation, you're calling present(alert, animated: true, completion: nil) with animated: true parameter so it's not going to finish the presentation instantly. On the other hand, you are calling self.dismiss(animated: false, completion: { () in print("Done") }) on the same thread in the relatively short instruction block, so it gets executed before the iOS finishes the animated presentation of the dialog and that's why you get the error.
But furthermore, before actually fixing the issue you should ask yourself do you really want to dismiss the dialog as soon as it's presented. Judging from the code you posted, I'd assume you want it to be dismissed after either or both of the API calls are finished. If that's the case you need to move the dismiss method call within the completion block (closures) of your API calls.
Solution for me (Working fine and printing "Done"):
override func viewDidAppear(_ animated: Bool) {
if !self.defaults.getDownloadInicialTipoEntrega() || !self.defaults.getDownloadInicialPedido() || !self.defaults.getDownloadInicialVitrine() {
Functions.showAlertWaiting("Wait please...", self)
}
loadDeliveryTypesNOrders { (completed) in
if completed {
self.dismiss(animated: false, completion: { () in print("Done") })
}
}
}
func loadDeliveryTypesNOrders (completion: #escaping (Bool) -> ()) {
let parceiroId = self.defaults.getParceiroId()
if !self.defaults.getDownloadInicialTipoEntrega() {
TipoEntregaAPI().loadTiposEntrega(parceiroId){ (dados) in
if dados != nil {
for tipoEntrega in dados!{
// Do some stuff, no errors
}
}
}
}
if !self.defaults.getDownloadInicialPedido() {
PedidoAPI().loadOrders(parceiroId){ (dados) in
if dados != nil {
for pedidos in dados!{
// Do some stuff, no errors
}
}
}
}
completion(true)
}

Game Center match is invalid always

I'm trying to learn how to build a Game-Center based app. its a turn based extremely simple game that just basically logs what button you press and sends it to he other player. I am having a very hard time implementing all the game center features since Apple's documentation has not been updated for Swift. I've been guessing at everything and reading off Objective-C examples and hoping for the best (somehow I've managed to get a few things going, altough I'm not sure if they are correct)
Anyways, I made a new iCloud account for my Simulator and ran the app on my phone and the simulator simultaneously, trying to get them to match up in a game. However I always get a "match request is invalid" error:
EDIT I have registered the app in iTunesConnect, I've implemented leaderboards and tested them and they work (So I assume the iTunesConnect thing is properly working and set-up)
#IBAction func startTapped(_ sender: Any) {
let request = GKMatchRequest()
request.maxPlayers = 2
request.minPlayers = 1
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
present(mmvc!, animated: true, completion: nil)
}
extension HomeVC: GKGameCenterControllerDelegate
{
func authenticatePlayer()
{
//CALLED ON VIEWDIDAPPEAR
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {
(view, error) in
if view != nil
{
self.present(view!, animated: true, completion: nil)
} else {
print("AUTHENTICATED!")
print(GKLocalPlayer.localPlayer().isAuthenticated)
}
}
}
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true, completion: nil)
}
}
Here's the matchmaking code. Note I can get the Game Center screen to appear and tell me "how many players" and that its missing a player and gives the choice to invite friends and all that
extension HomeVC: GKMatchmakerViewControllerDelegate {
func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {
print("match was cancelled")
viewController.dismiss(animated: true, completion: nil)
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) {
print("didFailwithError: \(error.localizedDescription)")
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {
print("Match found, ID: \(match.description)")
let gameScreenVC = self.storyboard?.instantiateViewController(withIdentifier: "mainGame") as! ViewController
gameScreenVC.providesPresentationContextTransitionStyle = true
gameScreenVC.definesPresentationContext = true
gameScreenVC.modalPresentationStyle = UIModalPresentationStyle.fullScreen
gameScreenVC.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
match.delegate = gameScreenVC
self.present(gameScreenVC, animated: true, completion: nil)
}
}
If anyone is having this issue I fixed it by just replacing the "minelayers and max-layers" with the same amount, so the code now looks like this:
#IBAction func startTapped(_ sender: Any) {
let request = GKMatchRequest()
request.maxPlayers = 2 //min and max should be the same
request.minPlayers = 2 //apparently they need to be the same
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
present(mmvc!, animated: true, completion: nil)
}
apparently Game Center does not like a min.player count of 1, its invalid. but thats how I got it to stop giving me Invalid Match Request warnings

MFMessageComposeViewController make the app reload after close?

i have table of contacts , the logic user will multi select contacts to send them sms invite to the app , my issue is after popup sms compose( when close or send ) the composer is closed and the app is been restarted, is it how its work? why it should reset the app i am just closing or canceling the sms compose controller !
here is what i did
//Send SMS
func sendSMS( to : [String] = [] , message : String = "" )
{
if (MFMessageComposeViewController.canSendText())
{
let controller = MFMessageComposeViewController()
controller.body = message
controller.recipients = to
if let topController = UIApplication.topViewController()
{
controller.messageComposeDelegate = topController as MFMessageComposeViewControllerDelegate
topController.present(controller, animated: true, completion: nil)
}
}
}
extension UIViewController: MFMessageComposeViewControllerDelegate {
public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResult.cancelled.rawValue:
print("Message was cancelled")
controller.dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
print("Message failed")
controller.dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
print("Message was sent")
controller.dismiss(animated: true, completion: nil)
default:
break;
}
}
}
is there anyway to close sms composer without reloading the app ?

UIButton does not bring up MFMailViewController

I'm having an error with a bit of code. I am new to this and I am trying to teach myself. I am finding most of my answers online but can't seem to find anything about this particular issue. I want to send an email within the app but anytime I press the email button, the MFMailViewController does not come up. It is like my UIButton isn't working. But I know I have it as an IBAction. Here is my code so far. Any help is much appreciated.
import UIKit
import MessageUI
class RequestService: UIViewController,MFMailComposeViewControllerDelegate {
#IBOutlet weak var CustomerName: UITextField!
#IBOutlet weak var emailButton: UIButton!
#IBAction func sendEmail(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
let ComposeVC = MFMailComposeViewController()
ComposeVC.mailComposeDelegate = self
ComposeVC.setToRecipients(["jwelch#ussunsolar.com"])
ComposeVC.setSubject("New Support Ticket")
ComposeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(ComposeVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController,didFinishWithResult result:MFMailComposeResult, error: NSError?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
}
}
You made an error in the syntax in your sendMail function. The code you posted will only open the view controller if the device can't send mail. Change it to this:
#IBAction func sendEmail(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
return
}
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
composeVC.setToRecipients(["jwelch#ussunsolar.com"])
composeVC.setSubject("New Support Ticket")
composeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(composeVC, animated: true, completion: nil)
}
You only attempt to display the mail controller if the device can't send email. That's backwards.
#IBAction func sendEmail(_ sender: UIButton) {
if MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
let ComposeVC = MFMailComposeViewController()
ComposeVC.mailComposeDelegate = self
ComposeVC.setToRecipients(["jwelch#ussunsolar.com"])
ComposeVC.setSubject("New Support Ticket")
ComposeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(ComposeVC, animated: true, completion: nil)
}
}
func mailComposeController(controller: MFMailComposeViewController,didFinishWithResult result:MFMailComposeResult, error: NSError?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
And you need the delegate method outside of the other method.

SWIFT - mailComposeDelegate not called : cancel of MFMailComposeViewController doesn't work

On the same viewcontroller, we can send an email or a text message to send an information to a friend.
The text message in app fully works. But for the email, the email app opens inside my app with all the informations I asked to write but it's impossible to dismiss it by pushing cancel, nothing happens.
I tried mc.mailComposeDelegate = self or mc.delegate = self and MFMailComposeViewControllerDelegate is at the top too.
I looked everything on internet, I didn't find any explanation.
mailComposeController is never called!
Do you have any idea ?
class inviteAFriendViewController: UIViewController, MFMessageComposeViewControllerDelegate, MFMailComposeViewControllerDelegate {
#IBAction func emailButtonDidTouch(sender: AnyObject) {
sendEmail()
}
func sendEmail() {
let emailTitle = "text"
let messageBody = "text"
let toRecipents = [""]
let mc = MFMailComposeViewController()
//mc.mailComposeDelegate = self
mc.delegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
presentViewController(mc, animated: true, completion: nil)
}
func mailComposeController(controller2: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
switch result.rawValue {
case MFMailComposeResultCancelled.rawValue:
print("Mail cancelled")
controller2.dismissViewControllerAnimated(true, completion: nil)
case MFMailComposeResultSaved.rawValue:
print("Mail saved")
controller2.dismissViewControllerAnimated(true, completion: nil)
case MFMailComposeResultSent.rawValue:
print("Mail sent")
controller2.dismissViewControllerAnimated(true, completion: nil)
case MFMailComposeResultFailed.rawValue:
print("Mail sent failure.")
controller2.dismissViewControllerAnimated(true, completion: nil)
default:
break
}
controller2.dismissViewControllerAnimated(true, completion: nil)
}
I got it working without any problems, but my delegate method looks a little bit different to yours:
func mailComposeController(controller:MFMailComposeViewController, didFinishWithResult result:MFMailComposeResult, error:NSError?) {
switch result.rawValue {
case MFMailComposeResultCancelled.rawValue:
print("Mail cancelled")
case MFMailComposeResultSaved.rawValue:
print("Mail saved")
case MFMailComposeResultSent.rawValue:
print("Mail sent")
case MFMailComposeResultFailed.rawValue:
print("Mail sent failure: %#", [error.localizedDescription])
default:
break
}
self.dismissViewControllerAnimated(true, completion: nil)
}
you may try this one.
And you need to set
mc.mailComposeDelegate = self
and not the
mc.delegate = self
First of all use
mc.mailComposeDelegate = self
instead of
mc.delegate = self
Moreover, in case of Swift 3, delegate method is somehow updated which is:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?){
controller.dismiss(animated: true, completion: nil)
}
For Swift 4:
switch result.rawValue {
case MFMailComposeResult.cancelled.rawValue:
print("Mail cancelled")
case MFMailComposeResult.saved.rawValue:
print("Mail saved")
case MFMailComposeResult.sent.rawValue:
print("Mail sent")
case MFMailComposeResult.failed.rawValue:
print("Mail sent failure: \(error?.localizedDescription)")
default:
break
}