Created
September 14, 2022 08:27
-
-
Save lis186/286be52904b4f43408573b8ed8a319cd to your computer and use it in GitHub Desktop.
Use MFMessageComposeViewController with the Composable Architecture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// TextComposerViewApp | |
// | |
// Created by Yi-Hsiu Lee on 2022/9/14. | |
// | |
import ComposableArchitecture | |
import SwiftUI | |
public struct ContentEnvironment {} | |
public struct ContentState: Equatable { | |
public var textMessageComposer: TextMessageComposerState | |
public init(textMessageComposer: TextMessageComposerState = .init()) { | |
self.textMessageComposer = textMessageComposer | |
} | |
} | |
public enum ContentAction: Equatable { | |
case textMessageComposer(TextMessageComposerAction) | |
public var textMessageComposer: TextMessageComposerAction? { | |
get { | |
guard case .textMessageComposer(let value) = self else { return nil } | |
return value | |
} | |
set { | |
guard case .textMessageComposer = self, let newValue = newValue else { return } | |
self = .textMessageComposer(newValue) | |
} | |
} | |
} | |
public let contentReducer = Reducer<ContentState, ContentAction, ContentEnvironment>.combine( | |
textMessageComposerReducer.pullback( | |
state: \ContentState.textMessageComposer, | |
action: /ContentAction.textMessageComposer, | |
environment: { _ in | |
TextMessageComposerEnvironment(mainQueue: DispatchQueue.main.eraseToAnyScheduler()) | |
} | |
), | |
Reducer { _, action, _ in | |
switch action { | |
case .textMessageComposer(let textMessageComposerAction): | |
switch textMessageComposerAction { | |
case .setRreceipients(let rreceipients): | |
return .none | |
case .setMessageBody(let messageBody): | |
return .none | |
case .setErrorMessage(let errorMessage): | |
return .none | |
case .showSheet: | |
return .none | |
case .onMessageComposeFinished(let messageComposeResult): | |
return .none | |
case .dismissSheet: | |
return .none | |
} | |
} | |
} | |
) | |
.debug() | |
struct ContentView: View { | |
let store: Store<ContentState, ContentAction> | |
var body: some View { | |
WithViewStore(self.store) { viewStore in | |
ZStack { | |
Button("Open TextMessageComposer") { | |
viewStore.send( | |
.textMessageComposer( | |
.showSheet( | |
.init(receipients: [], | |
messageBody: "Hello!") | |
) | |
) | |
) | |
} | |
} | |
.ignoresSafeArea(.all) | |
.sheet( | |
isPresented: viewStore.binding( | |
get: \.textMessageComposer.showingTextMessageComposer, | |
send: .textMessageComposer(.dismissSheet) | |
) | |
) { | |
TextMessageComposerView( | |
viewStore: .init( | |
self.store.scope( | |
state: \ContentState.textMessageComposer, | |
action: ContentAction.textMessageComposer | |
) | |
) | |
) | |
.task { | |
print("task") | |
}.onAppear { | |
print("onAppear") | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView( | |
store: Store( | |
initialState: .init(), | |
reducer: contentReducer, | |
environment: .init() | |
) | |
) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ErrorView.swift | |
// | |
// | |
//// Created by Yi-Hsiu Lee on 2022/9/13. | |
// | |
import Foundation | |
import SwiftUI | |
public struct ErrorView: View { | |
public var errorMessage: String | |
public var body: some View { | |
Text(errorMessage) | |
} | |
public init(errorMessage: String) { | |
self.errorMessage = errorMessage | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// TextComposerViewAppApp.swift | |
// TextComposerViewApp | |
// | |
// Created by Yi-Hsiu Lee on 2022/9/14. | |
// | |
import ComposableArchitecture | |
import SwiftUI | |
@main | |
struct TextComposerViewAppApp: App { | |
var body: some Scene { | |
WindowGroup { | |
ContentView( | |
store: Store( | |
initialState: .init(), | |
reducer: contentReducer, | |
environment: .init() | |
) | |
) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//// | |
//// TextMessageComposerView.swift | |
//// | |
//// | |
//// Created by Yi-Hsiu Lee on 2022/9/13. | |
//// | |
// | |
import ComposableArchitecture | |
import Foundation | |
import MessageUI | |
import SwiftUI | |
public struct TextMessageComposerView: UIViewControllerRepresentable { | |
public let viewStore: ViewStore<TextMessageComposerState, TextMessageComposerAction> | |
public func makeUIViewController(context: UIViewControllerRepresentableContext<TextMessageComposerView>) -> UIViewController { | |
guard MFMessageComposeViewController.canSendText() else { | |
let errorView = ErrorView(errorMessage: context.coordinator.errorMessage) | |
let vc = UIHostingController(rootView: errorView) | |
return vc | |
} | |
let msgCompose = MFMessageComposeViewController() | |
msgCompose.messageComposeDelegate = context.coordinator | |
msgCompose.recipients = context.coordinator.receipients | |
msgCompose.body = context.coordinator.messageBody | |
return msgCompose | |
} | |
public func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<TextMessageComposerView>) {} | |
public func makeCoordinator() -> Coordinator { | |
Coordinator( | |
self, | |
receipients: viewStore.binding(get: \.receipients, send: TextMessageComposerAction.setRreceipients), | |
messageBody: viewStore.binding(get: \.messageBody, send: TextMessageComposerAction.setMessageBody), | |
errorMessage: viewStore.binding(get: \.errorMessage, send: TextMessageComposerAction.setErrorMessage) | |
) | |
} | |
public class Coordinator: NSObject, MFMessageComposeViewControllerDelegate { | |
let parent: TextMessageComposerView | |
@Binding var receipients: [String] | |
@Binding var messageBody: String | |
@Binding var errorMessage: String | |
public init(_ parent: TextMessageComposerView, receipients: Binding<[String]>, messageBody: Binding<String>, errorMessage: Binding<String>) { | |
self.parent = parent | |
self._receipients = receipients | |
self._messageBody = messageBody | |
self._errorMessage = errorMessage | |
} | |
public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) { | |
parent.viewStore.send(.onMessageComposeFinished(result)) | |
parent.viewStore.send(.dismissSheet) | |
} | |
} | |
public init(viewStore: ViewStore<TextMessageComposerState, TextMessageComposerAction>) { | |
self.viewStore = viewStore | |
} | |
} | |
public struct TextMessageComposerState: Equatable { | |
public var showingTextMessageComposer: Bool | |
public var receipients: [String] | |
public var messageBody: String | |
public var errorMessage: String | |
public init(showingTextMessageComposer: Bool = false, receipients: [String] = [], messageBody: String = "", errorMessage: String = "Cannot send message") { | |
self.showingTextMessageComposer = showingTextMessageComposer | |
self.receipients = receipients | |
self.messageBody = messageBody | |
self.errorMessage = errorMessage | |
} | |
} | |
public enum TextMessageComposerAction: Equatable { | |
case setRreceipients([String]) | |
case setMessageBody(String) | |
case setErrorMessage(String) | |
case showSheet(SheetConfiuration) | |
case onMessageComposeFinished(MessageComposeResult) | |
case dismissSheet | |
} | |
public struct SheetConfiuration: Equatable { | |
public var receipients: [String] | |
public var messageBody: String | |
public init(receipients: [String], messageBody: String) { | |
self.receipients = receipients | |
self.messageBody = messageBody | |
} | |
} | |
public struct TextMessageComposerEnvironment { | |
public var mainQueue: AnySchedulerOf<DispatchQueue> | |
public static let live = Self( | |
mainQueue: DispatchQueue.main.eraseToAnyScheduler() | |
) | |
public static let test = Self( | |
mainQueue: DispatchQueue.main.eraseToAnyScheduler() | |
) | |
public init(mainQueue: AnySchedulerOf<DispatchQueue>) { | |
self.mainQueue = mainQueue | |
} | |
} | |
public let textMessageComposerReducer = Reducer<TextMessageComposerState, TextMessageComposerAction, TextMessageComposerEnvironment> { state, action, _ in | |
switch action { | |
case .setRreceipients(let receipients): | |
state.receipients = receipients | |
return .none | |
case .setMessageBody(let messageBody): | |
state.messageBody = messageBody | |
return .none | |
case .setErrorMessage(let errorMessage): | |
state.errorMessage = errorMessage | |
return .none | |
case .showSheet(let sheetConfiuration): | |
state.receipients = sheetConfiuration.receipients | |
state.messageBody = sheetConfiuration.messageBody | |
state.showingTextMessageComposer = true | |
return .none | |
case .onMessageComposeFinished(let result): | |
print(result) | |
return .none | |
case .dismissSheet: | |
state.showingTextMessageComposer = false | |
return .none | |
} | |
} | |
.debug() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment