Sending SMS in iOS with Swift
Asked Answered
A

7

87

First of all, I'm really surprised that this is not a duplicate, because there are TONS of stackoverflow questions that solve this in Objective-C, but I have yet to see a good answer that used Swift.

What I'm looking for is a code snippet in Swift that sends an arbitrary string as a the body of a text message to given phone number. Essentially, I'd like something like this from Apple's official documentation, but in Swift instead of Objective-C.

I imagine this isn't too difficult, as it can be done in just a couple of lines of code in Android.

EDIT: What I'm looking for is 5-20 lines of Swift code, I do not agree that this is too broad. In Java (for Android), the solution looks like this:

package com.company.appname;
import android.app.Activity;
import android.telephony.SmsManager;
public class MainActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        public static final mPhoneNumber = "1111111111";
        public static final mMessage = "hello phone";
        SmsManager.getDefault().sendTextMessage(mPhoneNumber, null, mMessage, null, null);
     }
}

Now this is the android solution, and it's only 11 lines. Java tends to be much more verbose than Swift, so I doubt what I'm asking is "too broad", it is more likely that I don't know how to use the Objective-C MessageComposer object, because the documentation that I linked to above is unclear with regard to usage in Swift.

Aglet answered 13/10, 2014 at 22:57 Comment(9)
I have edited the post and I think the question that I am intending to ask is not too broad. Please reconsider closing this question, or help me out by asking for clarifying details that I am leaving out that make the scope too broad.Aglet
I'd also like to point to the fact that this question is the same question for Objective-C and received over 300 upvotes and was protected by moderators (not put on hold as mine has been). I believe this question could actually become quite valuable to the community.Aglet
Converting Obj-C to Swift is not hard. A question that simply asks for a translation, citing code that has already been written, is not particularly useful.Zealous
Understood. I am a beginner to iOS and have not had a chance to learn Objective C. Translation may be easy once you know how it's done, but I do not know how it's done. The snippet is likely to be used by many others like me.Aglet
There is really not much to learn. The APIs are the same. You don't need to understand the detailed syntax of Obj-C because all the classes and methods have basically the same names in Swift. developer.apple.com/library/ios/documentation/Swift/Conceptual/…Zealous
Sure, but at the very least, it seems odd to be writing Swift code and refer to Objective-C answers. The reader then has to do the conversion rather than reusing a snippet. Building up a repertoire of Swift answers on StackOverflow will only be beneficial to the community in the future, as Swift may eventually become the primary language in which iOS apps are written.Aglet
They are really Cocoa questions and not Obj-C questions. I would encourage you to discuss the issue on Meta. meta.#258913Zealous
(Also keep in mind that the question you linked is 6 years old...!)Zealous
The question I linked is 6 years old, but the Swift language is much, much younger. The swift version of the question could not have been asked six years ago.Aglet
J
137

Not sure if you really got the answer. I was in a similar hunt and came across this solution and got it to work.

import UIKit
import MessageUI

class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {

    @IBOutlet weak var phoneNumber: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func sendText(sender: UIButton) {
        if (MFMessageComposeViewController.canSendText()) {
            let controller = MFMessageComposeViewController()
            controller.body = "Message Body"
            controller.recipients = [phoneNumber.text]
            controller.messageComposeDelegate = self
            self.presentViewController(controller, animated: true, completion: nil)
        }
    }

    func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
        //... handle sms screen actions
        self.dismissViewControllerAnimated(true, completion: nil)
    }

    override func viewWillDisappear(animated: Bool) {
        self.navigationController?.navigationBarHidden = false
    }
}
Jocko answered 24/12, 2014 at 3:33 Comment(5)
would this send a text message or iMessageVahe
It will simply take you to the Messaging app. The app determines if that is message or iMessage.Jocko
How to share URL and text ?Rumery
Consider updating for Swift for. For instance, it's now: func messageComposeViewController(_ controller: MFMessageComposeViewController, ...)Washroom
worked for me with little edit. controller.recipients = ([self.phoneNumber.text] as! [String])Attendance
M
30

Swift 3.0 Solution:

func sendSMSText(phoneNumber: String) {
        if (MFMessageComposeViewController.canSendText()) {
            let controller = MFMessageComposeViewController()
            controller.body = ""
            controller.recipients = [phoneNumber]
            controller.messageComposeDelegate = self
            self.present(controller, animated: true, completion: nil)
        }
    }

    func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
        //... handle sms screen actions
        self.dismiss(animated: true, completion: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        self.navigationController?.isNavigationBarHidden = false
    }
Maundy answered 4/10, 2016 at 10:12 Comment(0)
E
18

For sending iMessage in Swift 5 I use following code

Just MessageUI package and implement MFMessageComposeViewControllerDelegate

import UIKit
import MessageUI

class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func sendNewIMessage(_ sender: Any) {
        let messageVC = MFMessageComposeViewController()
        messageVC.body = "Enter a message details here";
        messageVC.recipients = ["recipients_number_here"]
        messageVC.messageComposeDelegate = self
        self.present(messageVC, animated: true, completion: nil)
    }

    func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
        switch (result) {
        case .cancelled:
            print("Message was cancelled")
        case .failed:
            print("Message failed")
        case .sent:
            print("Message was sent")
        default:
            return
        }
        dismiss(animated: true, completion: nil)
    }
}
Endoergic answered 16/5, 2019 at 12:54 Comment(1)
Thank you, however the message popup does not dismiss, so I needed to add the controller to the line: controller.dismiss(animated: true, completion: nil)Chinch
I
12

Simpler solution may be opening html link:

let mPhoneNumber = "1111111111";
let mMessage = "hello%20phone";
if let url = URL(string: "sms://" + mPhoneNumber + "&body="+mMessage) {
    UIApplication.shared.open(url)
}

Make sure you replaced spaces with "%20"

Inoue answered 20/9, 2020 at 21:29 Comment(0)
A
9

Swift 3, 4, 5

@IBAction func sendSmsClick(_ sender: AnyObject) {
        guard MFMessageComposeViewController.canSendText() else {
            return
        }

        let messageVC = MFMessageComposeViewController()

        messageVC.body = "Enter a message";
        messageVC.recipients = ["Enter tel-nr"]
        messageVC.messageComposeDelegate = self;

        self.present(messageVC, animated: false, completion: nil)
    }

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

UI will look like: enter image description here

Amadaamadas answered 3/5, 2017 at 19:23 Comment(0)
R
5

If you do not want to depend on an UIViewController, follows a Swift 3.0 solution:

import UIKit
import MessageUI

class ECMMessageComposerBuilder: NSObject {

    private dynamic var customWindow: UIWindow?
    private var body: String?
    private var phoneNumber: String?
    fileprivate var messageController: MFMessageComposeViewController?

    var canCompose: Bool {
        return MFMessageComposeViewController.canSendText()
    }

    func body(_ body: String?) -> ECMMessageComposerBuilder {
        self.body = body
        return self
    }

    func phoneNumber(_ phone: String?) -> ECMMessageComposerBuilder {
        self.phoneNumber = phone
        return self
    }

    func build() -> UIViewController? {
        guard canCompose else { return nil }

        messageController = MFMessageComposeViewController()
        messageController?.body = body
        if let phone = phoneNumber {
            messageController?.recipients = [phone]
        }
        messageController?.messageComposeDelegate = self

        return messageController
    }

    func show() {
        customWindow = UIWindow(frame: UIScreen.main.bounds)
        customWindow?.rootViewController = MNViewController()

        // Move it to the top
        let topWindow = UIApplication.shared.windows.last
        customWindow?.windowLevel = (topWindow?.windowLevel ?? 0) + 1

        // and present it
        customWindow?.makeKeyAndVisible()

        if let messageController = build() {
            customWindow?.rootViewController?.present(messageController, animated: true, completion: nil)
        }
    }

    func hide(animated: Bool = true) {
        messageController?.dismiss(animated: animated, completion: nil)
        messageController = nil
        customWindow?.isHidden = true
        customWindow = nil
    }
}

extension ECMMessageComposerBuilder: MFMessageComposeViewControllerDelegate {

    func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
        controller.dismiss(animated: true, completion: nil)
        hide()
    }
}

You call the composer this way:

let phoneNumber = "987654321"
let composer = MNMessageComposerBuilder()
composer.phoneNumber(phoneNumber).show()

or using a lazy var

let phoneNumber = "987654321"
private lazy var messageComposer: MNMessageComposerBuilder = {
    let composer = MNMessageComposerBuilder()
    return composer
}()
messageComposer.phoneNumber(phoneNumber).show()
Reina answered 3/10, 2017 at 20:23 Comment(0)
N
-1
@IBAction func sendMessageBtnClicked(sender: AnyObject) {
    var messageVC = MFMessageComposeViewController()

    messageVC.body = "Enter a message";
    messageVC.recipients = ["Enter tel-nr"]
    messageVC.messageComposeDelegate = self;

    self.presentViewController(messageVC, animated: false, completion: nil)
}

func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
    switch (result.value) {
    case MessageComposeResultCancelled.value:
      println("Message was cancelled")
      self.dismissViewControllerAnimated(true, completion: nil)
    case MessageComposeResultFailed.value:
      println("Message failed")
      self.dismissViewControllerAnimated(true, completion: nil)
    case MessageComposeResultSent.value:
      println("Message was sent")
     self.dismissViewControllerAnimated(true, completion: nil)
    default:
      break;
    }
}
Nothingness answered 4/10, 2016 at 12:55 Comment(1)
This will crash in a simulator, because you don't check canSendText(). Also, recipients is placed in the compose window. In most devices, MMS will allow the placeholder text to turn green, fail to match a number in contacts, and then let the user send a message to your sample text, which will go nowhere. (Possibly costing the device user a message, if the carrier charges for it.)Ploss

© 2022 - 2024 — McMap. All rights reserved.