이런 식으로 앱 내부에서 유저가 개발자에게 피드백을 줄 수 있는 소통 창구를 만들텐데, 보통 구글 폼 아니면 메일로 받게 만든다.
구글 폼과 메일로 보내는 방법의 가장 큰 차이점은,
유저의 디바이스 정보, 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
}
}