Created
February 11, 2019 10:42
-
-
Save takasek/a8d7cffe7856a14973f28c112bc89c47 to your computer and use it in GitHub Desktop.
iOSアプリ設計パターン入門 第3章サンプルコード
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
protocol MessageSenderDelegate { | |
func stateの変化を伝える() | |
} | |
protocol Message { | |
} | |
protocol MessageInput { | |
associatedtype Payload | |
func validate() throws -> Payload | |
} | |
protocol MessageSenderAPI { | |
associatedtype Payload | |
associatedtype Response: Message | |
func send(payload: Payload, completion: @escaping (Response?) -> Void) | |
} | |
final class MessageSender<API: MessageSenderAPI, Input: MessageInput> | |
where API.Payload == Input.Payload { | |
var delegate: MessageSenderDelegate? | |
let api: API | |
var input: Input { | |
didSet { state = State(evaluating: input) } | |
} | |
enum State { | |
case inputting(validationError: Error?) | |
case sending | |
case sent(API.Response) | |
case connectionFailed | |
init(evaluating input: Input) { | |
do { | |
try input.validate() | |
self = .inputting(validationError: nil) | |
} catch let e { | |
self = .inputting(validationError: e) | |
} | |
} | |
mutating func accept(response: API.Response?) { | |
self = response.map(State.sent) ?? .connectionFailed | |
} | |
} | |
private(set) var state: State { | |
didSet { delegate?.stateの変化を伝える() } | |
} | |
init(api: API, input: Input) { | |
self.api = api | |
self.input = input | |
self.state = State(evaluating: input) | |
} | |
func send() { | |
do { | |
let payload = try input.validate() | |
state = .sending | |
api.send(payload: payload) { [weak self] in | |
self?.state.accept(response: $0) | |
} | |
} catch let e { | |
state = .inputting(validationError: e) | |
} | |
} | |
} | |
enum ImageMessageInputError: Error { | |
case noImage, tooLongText(count: Int) | |
} | |
struct ImageMessageInput: MessageInput { | |
var text: String? | |
var image: UIImage? | |
func validate() throws -> (text: String?, image: UIImage) { | |
guard let image = image | |
else { throw ImageMessageInputError.noImage } | |
if let text = text, text.count >= 80 | |
{ throw ImageMessageInputError.tooLongText(count: text.count) } | |
return (text, image) | |
} | |
} | |
struct ImageMessage: Message { | |
let id: Int | |
let text: String? | |
let imageURL: URL | |
} | |
final class ImageMessageSenderAPI: MessageSenderAPI { | |
var successes: Bool = true | |
func send(payload: (text: String?, image: UIImage), completion: @escaping (ImageMessage?) -> Void) { | |
if successes { | |
completion(ImageMessage(id: 1, text: payload.text, imageURL: URL(string: "localhost://")!)) | |
} else { | |
completion(nil) | |
} | |
} | |
} | |
let api = ImageMessageSenderAPI() | |
let sender = MessageSender( | |
api: api, | |
input: ImageMessageInput(text: nil, image: nil) | |
) | |
class SpyMessageSenderDelegate: MessageSenderDelegate { | |
func stateの変化を伝える() { | |
print(sender.state) | |
} | |
} | |
sender.delegate = SpyMessageSenderDelegate() | |
sender.input.text = String(repeating: "あ", count: 100) | |
sender.input.image = nil | |
sender.send() | |
sender.input.image = UIImage() | |
sender.send() | |
sender.input.text = "ほげほげ" | |
api.successes = false | |
sender.send() | |
sender.send() | |
api.successes = true | |
sender.send() | |
/* | |
inputting(validationError: Optional(__lldb_expr_15.ImageMessageInputError.noImage)) | |
inputting(validationError: Optional(__lldb_expr_15.ImageMessageInputError.noImage)) | |
inputting(validationError: Optional(__lldb_expr_15.ImageMessageInputError.noImage)) | |
inputting(validationError: Optional(__lldb_expr_15.ImageMessageInputError.tooLongText(count: 100))) | |
inputting(validationError: Optional(__lldb_expr_15.ImageMessageInputError.tooLongText(count: 100))) | |
inputting(validationError: nil) | |
sending | |
connectionFailed | |
sending | |
connectionFailed | |
sending | |
sent(__lldb_expr_15.ImageMessage(id: 1, text: Optional("ほげほげ"), imageURL: localhost://)) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment