Skip to content

Commit

Permalink
Show an error screen on iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
squarejesse committed Oct 31, 2023
1 parent 621c4fe commit c9d0327
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public open class CodeListener {
*/
public open fun onUncaughtException(
view: TreehouseView<*>,
e: Throwable,
exception: Throwable,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ class FakeCodeListener(

override fun onUncaughtException(
view: TreehouseView<*>,
e: Throwable,
exception: Throwable,
) {
// Canonicalize "java.lang.Exception(boom!)" to "kotlin.Exception(boom!)".
val exceptionString = e.toString().replace("java.lang.", "kotlin.")
val exceptionString = exception.toString().replace("java.lang.", "kotlin.")
eventLog += "codeListener.onUncaughtException($view, $exceptionString)"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ class EmojiSearchActivity : ComponentActivity() {
}

private val codeListener: CodeListener = object : CodeListener() {
override fun onUncaughtException(view: TreehouseView<*>, e: Throwable) {
override fun onUncaughtException(view: TreehouseView<*>, exception: Throwable) {
val treehouseLayout = view as TreehouseLayout
treehouseLayout.reset()
treehouseLayout.addView(
ExceptionView(treehouseLayout, e),
ExceptionView(treehouseLayout, exception),
LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
1069C9E12AF0AECC007D6F07 /* ExceptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1069C9E02AF0AECC007D6F07 /* ExceptionView.swift */; };
10AA3D4C28C03D32006F125E /* IosEmojiSearchWidgetFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10AA3D4B28C03D32006F125E /* IosEmojiSearchWidgetFactory.swift */; };
10AA3D4E28C0EA40006F125E /* TextBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10AA3D4D28C0EA40006F125E /* TextBinding.swift */; };
635661D521F12B7E00DD7240 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635661D421F12B7E00DD7240 /* AppDelegate.swift */; };
Expand All @@ -22,6 +23,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
1069C9E02AF0AECC007D6F07 /* ExceptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExceptionView.swift; sourceTree = "<group>"; };
10AA3D4B28C03D32006F125E /* IosEmojiSearchWidgetFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IosEmojiSearchWidgetFactory.swift; sourceTree = "<group>"; };
10AA3D4D28C0EA40006F125E /* TextBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBinding.swift; sourceTree = "<group>"; };
635661D121F12B7E00DD7240 /* EmojiSearchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EmojiSearchApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -82,6 +84,7 @@
CB9F76552810A8A8008CF457 /* IosHostApi.swift */,
10AA3D4B28C03D32006F125E /* IosEmojiSearchWidgetFactory.swift */,
10AA3D4D28C0EA40006F125E /* TextBinding.swift */,
1069C9E02AF0AECC007D6F07 /* ExceptionView.swift */,
);
path = EmojiSearchApp;
sourceTree = "<group>";
Expand Down Expand Up @@ -187,6 +190,7 @@
files = (
10AA3D4C28C03D32006F125E /* IosEmojiSearchWidgetFactory.swift in Sources */,
CB85C0B725AFE61A007A2CC7 /* EmojiSearchViewController.swift in Sources */,
1069C9E12AF0AECC007D6F07 /* ExceptionView.swift in Sources */,
C2F7FBA228BEB54200A66A69 /* TextInputBinding.swift in Sources */,
10AA3D4E28C0EA40006F125E /* TextBinding.swift in Sources */,
635661D521F12B7E00DD7240 /* AppDelegate.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class EmojiSearchViewController : UIViewController, EmojiSearchEventListener {
let treehouseView = TreehouseUIView(widgetSystem: widgetSystem)
let content = treehouseApp.createContent(
source: EmojiSearchContent(),
codeListener: CodeListener()
codeListener: EmojiSearchCodeListener()
)
ExposedKt.bindWhenReady(content: content, view: treehouseView)
view = treehouseView.view
Expand Down Expand Up @@ -71,6 +71,23 @@ class EmojiSearchViewController : UIViewController, EmojiSearchEventListener {
}
}

class EmojiSearchCodeListener : CodeListener {
override func onUncaughtException(view: TreehouseView, exception: KotlinThrowable) {
let treehouseLayout = view as! TreehouseUIView
treehouseLayout.reset()

let exceptionView = ExceptionView(exception)
exceptionView.translatesAutoresizingMaskIntoConstraints = false
treehouseLayout.view.addSubview(exceptionView)
NSLayoutConstraint.activate([
exceptionView.topAnchor.constraint(equalTo: treehouseLayout.view.topAnchor),
exceptionView.leftAnchor.constraint(equalTo: treehouseLayout.view.leftAnchor),
exceptionView.rightAnchor.constraint(equalTo: treehouseLayout.view.rightAnchor),
exceptionView.bottomAnchor.constraint(equalTo: treehouseLayout.view.bottomAnchor),
])
}
}

class EmojiSearchContent : TreehouseContentSource {
func get(app: AppService) -> ZiplineTreehouseUi {
let treehouesUi = (app as! EmojiSearchPresenter)
Expand Down
91 changes: 91 additions & 0 deletions samples/emoji-search/ios-uikit/EmojiSearchApp/ExceptionView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (C) 2023 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Foundation
import UIKit
import EmojiSearchKt

/// Renders an emoji, plus the first line of the exception message, centered and wrapped. The view
/// has a light-yellow background.
///
/// ```
/// 🦨
/// app.cash.zipline.ZiplineException
/// RuntimeException
/// boom!
/// ```
class ExceptionView : UIView {
let exception: KotlinThrowable

required init(_ exception: KotlinThrowable) {
self.exception = exception
super.init(frame: CGRect.zero)

let emoji = UILabel()
emoji.textAlignment = .center
emoji.font = emoji.font.withSize(40)
emoji.text = "🦨"
emoji.numberOfLines = 0
emoji.translatesAutoresizingMaskIntoConstraints = false

let message = UILabel()
message.textAlignment = .center
message.text = exceptionToLabel(exception)
message.font = message.font.withSize(16)
message.numberOfLines = 0
message.translatesAutoresizingMaskIntoConstraints = false

let centeredContent = UIView()
centeredContent.translatesAutoresizingMaskIntoConstraints = false
centeredContent.addSubview(emoji)
centeredContent.addSubview(message)

NSLayoutConstraint.activate([
emoji.topAnchor.constraint(equalTo: centeredContent.topAnchor),
emoji.leftAnchor.constraint(equalTo: centeredContent.leftAnchor),
emoji.rightAnchor.constraint(equalTo: centeredContent.rightAnchor),
])

NSLayoutConstraint.activate([
message.topAnchor.constraint(equalTo: emoji.bottomAnchor, constant: 10),
message.leftAnchor.constraint(equalTo: centeredContent.leftAnchor, constant: 10),
message.rightAnchor.constraint(equalTo: centeredContent.rightAnchor, constant: -10),
message.bottomAnchor.constraint(equalTo: centeredContent.bottomAnchor),
])

backgroundColor = UIColor(red: 255/255.0, green: 250/255.0, blue: 225/255.0, alpha: 1.0)
translatesAutoresizingMaskIntoConstraints = false
addSubview(centeredContent)
NSLayoutConstraint.activate([
centeredContent.centerYAnchor.constraint(equalTo: centerYAnchor),
centeredContent.leftAnchor.constraint(equalTo: leftAnchor),
centeredContent.rightAnchor.constraint(equalTo: rightAnchor),
])
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func exceptionToLabel(_ exception: KotlinThrowable) -> String {
var result = exception.description()
let endOfString = result.firstIndex(of: "\n")
if (endOfString != nil) {
result = String(result[..<endOfString!])
}
return result.replacingOccurrences(of: ": ", with: "\n")
}
}

0 comments on commit c9d0327

Please sign in to comment.