카테고리 없음

[Swift, RxSwift] 앱에서 이메일 보내는 기능 및 최신 Device 모델 식별자(15 pro max까지)

isak(이삭) 2024. 4. 8. 21:15

 

이런 식으로 앱 내부에서 유저가 개발자에게 피드백을 줄 수 있는 소통 창구를 만들텐데, 보통 구글 폼 아니면 메일로 받게 만든다.

 

구글 폼과 메일로 보내는 방법의 가장 큰 차이점은,

유저의 디바이스 정보, os 정보, 앱 버전을 정확하게 받을 수 있는지 유무 정도 ?

 

우리는 유저의 디테일한 정보를 얻기 위해서 메일을 통해 피드백을 받는 방식으로 선택하였다.

 

RxSwift, MVVM-C 패턴을 사용하고 있어서 조금 다를 수 있지만 위치의 차이일 뿐 크게 다르지 않다.

import MessageUI //애플이 제공하는 Framework

// final class viewModel: NSObject, ViewModel, MFMailComposeViewControllerDelegate { }
input.inquryTapEvent
            .withUnretained(self)
            .subscribe(onNext: { viewModel, _ in
            	// 실기기에서 이메일의 연동이 되어있는지에 따라 분기처리
                if MFMailComposeViewController.canSendMail() {
                    let mailViewController = MFMailComposeViewController()
                    mailViewController.mailComposeDelegate = self
                    
                    let bodyString = 
                             """
                             이곳에 문의 내용을 작성해주세요.
                             ex) 버스 정류장 데이터 이상, 버스 데이터 이상 등
                             
                             ------------
                             
                             Device Model : \(String.getDeviceIdentifier())
                             Device OS : \(UIDevice.current.systemVersion)
                             App Version : \(String.getCurrentVersion())
                             
                             ------------
                             """
                    
                    // 받을 이메일
                    mailViewController.setToRecipients(["이메일1@이메일.com"])
                    // 이메일의 제목
                    mailViewController.setSubject("[버스어디] 문의하기")
                    // 이메일 내용
                    mailViewController.setMessageBody(bodyString, isHTML: false)
                    
                    // Sheet형식으로 띄우는 작업
                    viewModel.coordinator.presentMail(vc: mailViewController)
                } else {
                    guard let inquryURL = Bundle.main.object(
                        forInfoDictionaryKey: "INQURY_URL"
                    ) as? String
                    else { return }
                    viewModel.coordinator.presentPrivacy(url: inquryURL)
                }
            })
            .disposed(by: disposeBag)

 

1. if문으로 분기처리 된 이유

일단 메일을 보내기 위해서는 mail 앱에 메일을 보낼 수 있는 계정이 연결되어있어야한다. Mail 앱 계정이 연동되어있는지 여부를 확인하기 위한 분기처리를 했다.

 mail에 계정 연동이 되어있지 않거나, mail 앱이 없을 경우 앱을 다운로드 받아 계정을 연동시키는 것이 유저에게 피드백을 받기에 좋지 않은 플로우인 것 같았다. 그래서 메일을 보낼 수 없는 상황이라면 WebView를 통해 구글폼이 열리게 뷰 이동을 시켜두었다.

 

2. 이메일에 들어갈 내용

- 피드백을 받을 개발자의 이메일

- 이메일의 제목

- 이메일 내용

 

모두 애플에서 만들어준 메소드들이 존재한다.

 

순서대로 

.setToRecipients() / .setSubject() / .setMessageBody()

 

관련 내용은 애플 문서 확인해보시길 ! https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller

 

MFMessageComposeViewController | Apple Developer Documentation

A standard view controller whose interface lets the user compose and send SMS or MMS messages.

developer.apple.com

 

3. 유저의 기기 정보, OS 버전, 앱 버전 정보

- OS 버전 정보는 위의 코드에 존재

- 기기 정보

 

identifier로만 구성하면 iphone12,3 이런식으로 출력되는데, 피드백 받아서 정리할 때 기기 정보에 대해 잘 구분이 안될 거 같아서 switch-case 문으로 모델 식별자를 토대로 기기 출력될 수 있게 만들어놓음

 

static func getCurrentVersion() -> String {
        guard let dictionary = Bundle.main.infoDictionary,
              let version = dictionary["CFBundleShortVersionString"] as? String
        else { return "" }
        
        return version
}
    
static func getDeviceIdentifier() -> String {
    var systemInfo = utsname()
    uname(&systemInfo)
    let machineMirror = Mirror(reflecting: systemInfo.machine)
    let identifier
    = machineMirror.children.reduce("") { identifier, element in
        guard let value = element.value as? Int8,
              value != 0
        else { return identifier }

        return identifier + String(UnicodeScalar(UInt8(value)))
    }

    switch identifier {
    case "iPhone10,1", "iPhone10,4": 
        return "iPhone 8"
    case "iPhone10,2", "iPhone10,5":
        return "iPhone 8 Plus"
    case "iPhone10,3", "iPhone10,6":
        return "iPhone X"
    case "iPhone11,2":
        return "iPhone XS"
    case "iPhone11,4", "iPhone11,6":
        return "iPhone XS Max"
    case "iPhone11,8":
        return "iPhone XR"
    case "iPhone12,1":
        return "iPhone 11"
    case "iPhone12,3":
        return "iPhone 11 Pro"
    case "iPhone12,5":
        return "iPhone 11 Pro Max"
    case "iPhone12,8":
        return "iPhone SE (2nd generation)"
    case "iPhone13,1":
        return "iPhone 12 mini"
    case "iPhone13,2":
        return "iPhone 12"
    case "iPhone13,3":
        return "iPhone 12 Pro"
    case "iPhone13,4":
        return "iPhone 12 Pro Max"
    case "iPhone14,4":
        return "iPhone 13 mini"
    case "iPhone14,5":
        return "iPhone 13"
    case "iPhone14,2":
        return "iPhone 13 Pro"
    case "iPhone14,3":
        return "iPhone 13 Pro Max"
    case "iPhone14,6":
        return "iPhone SE (3rd generation)"
    case "iPhone14,7":
        return "iPhone 14"
    case "iPhone14,8":
        return "iPhone 14 Plus"
    case "iPhone15,2":
        return "iPhone 14 Pro"
    case "iPhone15,3":
        return "iPhone 14 Pro Max"
    case "iPhone15,4":
        return "iPhone 15"
    case "iPhone15,5":
        return "iPhone 15 Plus"
    case "iPhone16,1":
        return "iPhone 15 Pro"
    case "iPhone16,2":
        return "iPhone 15 Pro Max"
    default:
        return identifier
    }
}