Skip to content

Commit

Permalink
Add iOS frontend for test app (#1473)
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeWharton authored Sep 12, 2023
1 parent d930d78 commit 107f6eb
Show file tree
Hide file tree
Showing 22 changed files with 1,133 additions and 4 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@ jobs:
script: ./gradlew -p samples/counter connectedCheck

- name: Build Counter iOS (UIKit)
run: |
xcodebuild -project samples/counter/ios-uikit/CounterApp.xcodeproj -scheme CounterApp -destination 'platform=iOS Simulator,name=iPhone 12,OS=latest'
run: xcodebuild -project samples/counter/ios-uikit/CounterApp.xcodeproj -scheme CounterApp -destination 'platform=iOS Simulator,name=iPhone 12,OS=latest'

sample-emoji:
runs-on: macos-latest
Expand All @@ -134,8 +133,7 @@ jobs:
- run: ./gradlew -p samples/emoji-search build

- name: Build Emoji Search iOS (UIKit)
run: |
xcodebuild -project samples/emoji-search/ios-uikit/EmojiSearchApp.xcodeproj -scheme EmojiSearchApp -destination 'platform=iOS Simulator,name=iPhone 12,OS=latest'
run: xcodebuild -project samples/emoji-search/ios-uikit/EmojiSearchApp.xcodeproj -scheme EmojiSearchApp -destination 'platform=iOS Simulator,name=iPhone 12,OS=latest'

test-app:
runs-on: macos-latest
Expand All @@ -153,6 +151,9 @@ jobs:

- run: ./gradlew -p test-app build

- name: Build TestApp iOS (UIKit)
run: xcodebuild -project test-app/ios-uikit/TestApp.xcodeproj -scheme TestApp -destination 'platform=iOS Simulator,name=iPhone 12,OS=latest'

publish:
runs-on: macos-latest
if: ${{ github.ref == 'refs/heads/trunk' && github.repository == 'cashapp/redwood' }}
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ include ':test-app:schema:widget-protocol'
if (!hasProperty('redwoodNoApps')) {
include ':test-app:android-views'
include ':test-app:browser'
include ':test-app:ios-shared'
include ':test-app:launcher'
include ':test-app:presenter'
include ':test-app:presenter-treehouse'
Expand Down
25 changes: 25 additions & 0 deletions test-app/ios-shared/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apply plugin: 'org.jetbrains.kotlin.multiplatform'

kotlin {
[
iosArm64(),
iosX64(),
iosSimulatorArm64(),
].each { iosTarget ->
iosTarget.binaries.framework {
baseName = 'TestAppKt'
}
}

sourceSets {
commonMain {
dependencies {
implementation projects.testApp.launcher
implementation projects.testApp.presenterTreehouse
implementation projects.testApp.schema.widgetProtocol
implementation projects.redwoodLayoutUiview
implementation projects.redwoodLazylayoutUiview
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.
*/
package com.example.redwood.testing.ios

import app.cash.redwood.treehouse.EventListener
import app.cash.redwood.treehouse.TreehouseApp
import app.cash.redwood.treehouse.TreehouseAppFactory
import app.cash.zipline.Zipline
import app.cash.zipline.ZiplineManifest
import app.cash.zipline.loader.ManifestVerifier.Companion.NO_SIGNATURE_CHECKS
import app.cash.zipline.loader.asZiplineHttpClient
import app.cash.zipline.loader.withDevelopmentServerPush
import com.example.redwood.testing.launcher.TestAppSpec
import com.example.redwood.testing.treehouse.HostApi
import com.example.redwood.testing.treehouse.TestAppPresenter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.flowOf
import platform.Foundation.NSLog
import platform.Foundation.NSURLSession

class TestAppLauncher(
private val nsurlSession: NSURLSession,
private val hostApi: HostApi,
) {
private val coroutineScope: CoroutineScope = MainScope()
private val manifestUrl = "http://localhost:8080/manifest.zipline.json"

@Suppress("unused") // Invoked in Swift.
fun createTreehouseApp(): TreehouseApp<TestAppPresenter> {
val ziplineHttpClient = nsurlSession.asZiplineHttpClient()

val treehouseAppFactory = TreehouseAppFactory(
httpClient = ziplineHttpClient,
manifestVerifier = NO_SIGNATURE_CHECKS,
eventListener = object : EventListener() {
override fun codeLoadFailed(app: TreehouseApp<*>, manifestUrl: String?, exception: Exception, startValue: Any?) {
NSLog("Treehouse: codeLoadFailed: $exception")
}

override fun codeLoadSuccess(app: TreehouseApp<*>, manifestUrl: String?, manifest: ZiplineManifest, zipline: Zipline, startValue: Any?) {
NSLog("Treehouse: codeLoadSuccess")
}
},
)

val manifestUrlFlow = flowOf(manifestUrl)
.withDevelopmentServerPush(ziplineHttpClient)

val treehouseApp = treehouseAppFactory.create(
appScope = coroutineScope,
spec = TestAppSpec(
manifestUrl = manifestUrlFlow,
hostApi = hostApi,
),
)

treehouseApp.start()

return treehouseApp
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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.
*/
@file:Suppress("unused", "UNUSED_PARAMETER")

package com.example.redwood.testing.ios

import app.cash.redwood.Modifier
import app.cash.redwood.layout.uiview.UIViewRedwoodLayoutWidgetFactory
import app.cash.redwood.lazylayout.uiview.UIViewRedwoodLazyLayoutWidgetFactory
import app.cash.redwood.treehouse.AppService
import app.cash.redwood.treehouse.Content
import app.cash.redwood.treehouse.TreehouseUIView
import app.cash.redwood.treehouse.TreehouseView
import app.cash.redwood.treehouse.TreehouseView.WidgetSystem
import app.cash.redwood.treehouse.bindWhenReady
import com.example.redwood.testing.treehouse.TestAppPresenter
import com.example.redwood.testing.widget.TestSchemaProtocolNodeFactory
import com.example.redwood.testing.widget.TestSchemaWidgetFactories
import com.example.redwood.testing.widget.TestSchemaWidgetFactory
import okio.ByteString
import okio.ByteString.Companion.toByteString
import okio.Closeable
import platform.Foundation.NSData

// Used to export types to Objective-C / Swift.
fun exposedTypes(
testAppPresenter: TestAppPresenter,
testAppLauncher: TestAppLauncher,
testSchemaWidgetFactory: TestSchemaWidgetFactory<*>,
protocolNodeFactory: TestSchemaProtocolNodeFactory<*>,
treehouseUIView: TreehouseUIView,
uiViewRedwoodLayoutWidgetFactory: UIViewRedwoodLayoutWidgetFactory,
uiViewRedwoodLazyLayoutWidgetFactory: UIViewRedwoodLazyLayoutWidgetFactory,
widgetSystem: WidgetSystem<*>,
widgetFactories: TestSchemaWidgetFactories<*>,
) {
throw AssertionError()
}

fun byteStringOf(data: NSData): ByteString = data.toByteString()

fun modifier(): Modifier = Modifier

fun <A : AppService> bindWhenReady(
content: Content,
view: TreehouseView<*>,
): Closeable = content.bindWhenReady(view)
Loading

0 comments on commit 107f6eb

Please sign in to comment.