Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for New Architecture - iOS & Android (Fabric) #931

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6dcb22d
Init `fabricfixture` app
j-piasecki Aug 20, 2022
80f4b02
Setup codegen
j-piasecki Aug 21, 2022
72933ad
Update podspec
j-piasecki Aug 21, 2022
1ad219c
Implement view components
j-piasecki Aug 21, 2022
b1edc02
Move `CellContainer` to objective c
j-piasecki Aug 21, 2022
9b76327
Fix `AutoLayoutView` subviews and footer
j-piasecki Aug 21, 2022
377f4cd
Implement `onBlankAreaEvent` on iOS
j-piasecki Aug 21, 2022
e3d228f
Update Android view managers
j-piasecki Aug 21, 2022
9b9eeb2
Add generated files for Paper
j-piasecki Aug 21, 2022
342377d
Make `onBlankAreaEvent` work on Android
j-piasecki Aug 21, 2022
016ce13
Update syntax in podspec
j-piasecki Aug 22, 2022
0687e83
Remove space
j-piasecki Aug 22, 2022
e25181c
Update codegen specs to use only TS
j-piasecki Aug 30, 2022
5695dd8
Bump React Native
j-piasecki Nov 4, 2022
e1fa7ca
Get fabricfixture working
markrickert Oct 6, 2023
3a57e73
Upgrade fabricfixture to react-native 0.72.5 - iOS
markrickert Oct 9, 2023
a7ff840
Upgrade fabricfixture to react-native 0.72.5 - Android
markrickert Oct 9, 2023
42f8c26
Cleanup
markrickert Oct 9, 2023
8823a55
Change RN_FABRIC_ENABLED to RCT_NEW_ARCH_ENABLED
markrickert Nov 12, 2023
092f784
Update RNFlashList.podspec
markrickert Jan 30, 2024
d2f2e30
Recommended PR changes.
markrickert Jan 31, 2024
0548cbc
Upgrade fabricfixture to react-native 0.72.10 and get builds working
markrickert Jan 31, 2024
c3d68aa
Gets the fixture app working again in latest Mac OS 14.3 & Xcode 15.2
markrickert Jan 31, 2024
a7c0777
Fix fixture app’s eslint rules to inherit from the base and pass CI l…
markrickert Jan 31, 2024
4131972
Adds @react-native/eslint-config to the main package.
markrickert Jan 31, 2024
0ddf2c0
Another attempt at getting the linter to pass.
markrickert Jan 31, 2024
5f2f9b1
Update fabricfixture to latest released react-native: 0.73.3
markrickert Feb 5, 2024
0093be5
Get normal fixture android app running on modern java setup.
markrickert Feb 5, 2024
1eb4662
Update the podspec to remove unnecessary new arch stuff
markrickert Feb 5, 2024
e303991
Remove unnecessary codegen srcDirs addition in build.gradle
markrickert Feb 5, 2024
69acae2
Refactor the emitBlankAreaEvent event emitter to work with both paper…
markrickert Feb 5, 2024
e3f38c7
Fix RNFlashList-Swift.h import based on suggestion
markrickert Mar 28, 2024
0e48b1d
Upgrade react-native fixture to rn 0.73.6
markrickert Mar 28, 2024
6329ccd
Get versions up to date for fabric in the react-native fixture app
markrickert Mar 28, 2024
0932c96
Remove fabricfixture
markrickert Mar 28, 2024
f6c2004
Fix unnecessary ci script.
markrickert Mar 28, 2024
c1ba66a
Linter Fixes
markrickert Mar 28, 2024
dbcc26b
More linter fixes.
markrickert Mar 28, 2024
cc4f6a7
Attempt to fix CI Pipelines.
markrickert Mar 28, 2024
c9f7534
Trying to get ios to build in CI
markrickert Mar 28, 2024
195d1f5
Maybe static frameworks will build?
markrickert Mar 28, 2024
41eaca0
fix blank area event name
naqvitalha May 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/flash-list.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ jobs:
cd fixture/react-native
yarn
cd ios
bundle exec pod install
gem install bundler:2.4.8
bundle install
USE_FRAMEWORKS=static bundle exec pod install
- name: Build iOS
run: |
mkdir -p /tmp/artifacts
Expand All @@ -155,6 +157,11 @@ jobs:
steps:
- name: Repository checkout
uses: actions/checkout@v3
- name: Java setup
uses: actions/setup-java@v2
with:
distribution: "temurin"
java-version: 17
- name: Node setup
uses: actions/setup-node@v3
with:
Expand Down Expand Up @@ -199,7 +206,9 @@ jobs:
cd fixture/react-native
yarn
cd ios
bundle exec pod install
gem install bundler:2.4.8
bundle install
USE_FRAMEWORKS=static bundle exec pod install
- name: Build and Run e2e tests
run: |
cd fixture/react-native
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
- https://github.com/Shopify/flash-list/pull/1076
- Fix stale reference to onScroll and onLoad
- https://github.com/Shopify/flash-list/pull/1112
- Added Fabric (new architecture) support for iOS and Android
- https://github.com/Shopify/flash-list/pull/931

## [1.6.4] - 2024-03-18

Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,17 @@ To avoid common pitfalls, you can also follow these steps for migrating from `Fl
## App / Playground

The [fixture](https://github.com/Shopify/flash-list/tree/main/fixture) is an example app showing how to use the library.

To get started clone the repository and run `yarn` in the root directory

#### If using expo:

`yarn fixture:expo:up && yarn fixture:expo:start`

#### If using bare react-native:

`yarn fixture:rn:up && yarn fixture:rn:ios` or `yarn fixture:rn:android`

#### For the new architecture:

`yarn fixture:rn:fabric:up && fixture:rn:ios:fabric` or `yarn fixture:rn:android:fabric`
11 changes: 8 additions & 3 deletions RNFlashList.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@ Pod::Spec.new do |s|
s.homepage = package['homepage']
s.license = package['license']
s.author = package['author']
s.platforms = { :ios => '11.0', :tvos => '12.0' }
s.platforms = { ios: '12.4', tvos: '12.0' }
s.source = { git: 'https://github.com/shopify/flash-list.git', tag: "v#{s.version}" }
s.source_files = 'ios/Sources/**/*'
s.requires_arc = true
s.swift_version = '5.0'

# Dependencies
s.dependency 'React-Core'
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naqvitalha The RCT_NEW_ARCH_ENABLED isn't actually set anywhere.

Suggested change
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
if fabric_enabled
s.pod_target_xcconfig = {
'OTHER_SWIFT_FLAGS' => '-D RCT_NEW_ARCH_ENABLED',
}
end
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.

And then it would also be required to check if the flag is set at the top level (cannot add suggestion there 😞):

fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED']

# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
if respond_to?(:install_modules_dependencies, true)
install_modules_dependencies(s)
else
s.dependency "React-Core"
end

# Tests spec
s.test_spec 'Tests' do |test_spec|
Expand Down
18 changes: 16 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
apply plugin: 'com.android.library'
def isNewArchitectureEnabled() {
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

if (isNewArchitectureEnabled()) {
apply plugin: 'com.facebook.react'
}

def _ext = rootProject.ext

def _reactNativeVersion = _ext.has('reactNative') ? _ext.reactNative : '+'
Expand Down Expand Up @@ -40,11 +47,18 @@ android {
debug.java.srcDirs += 'src/debug/kotlin'
test.java.srcDirs += 'src/test/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'

if (isNewArchitectureEnabled()) {
main.java.srcDirs += ["src/fabric/java"]
} else {
main.java.srcDirs += ["src/paper/java"]
}
}

defaultConfig {
minSdkVersion _minSdkVersion
targetSdkVersion _targetSdkVersion
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
versionCode 1
versionName "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand All @@ -61,7 +75,7 @@ android {

dependencies {
compileOnly "com.facebook.react:react-native:${_reactNativeVersion}"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${_kotlinVersion}"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${_kotlinVersion}"
testImplementation "junit:junit:${_junitVersion}"
testImplementation "org.mockito.kotlin:mockito-kotlin:${_mockitoVersion}"
testImplementation "org.mockito:mockito-inline:${_mockitoVersion}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.shopify.reactnative.flash_list

import com.facebook.react.bridge.ReactContext
import com.facebook.react.fabric.FabricUIManager
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.common.UIManagerType
import com.facebook.react.bridge.WritableMap

fun ReactContext.dispatchEvent(nativeTag: Int, eventName: String, event: WritableMap) {
val fabricUIManager = UIManagerHelper.getUIManager(this, UIManagerType.FABRIC) as FabricUIManager
fabricUIManager.receiveEvent(nativeTag, eventName, event)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ package com.shopify.reactnative.flash_list

import android.content.Context
import android.graphics.Canvas
import android.util.DisplayMetrics
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.ScrollView
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.RCTEventEmitter
import com.facebook.react.views.view.ReactViewGroup

import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.EventDispatcher
import com.shopify.reactnative.flash_list.events.OnBlankAreaEvent

/** Container for all RecyclerListView children. This will automatically remove all gaps and overlaps for GridLayouts with flexible spans.
* Note: This cannot work for masonry layouts i.e, pinterest like layout */
Expand Down Expand Up @@ -136,15 +136,12 @@ class AutoLayoutView(context: Context) : ReactViewGroup(context) {
return null
}


/** TODO: Check migration to Fabric */
private fun emitBlankAreaEvent() {
val event: WritableMap = Arguments.createMap()
event.putDouble("offsetStart", alShadow.blankOffsetAtStart / pixelDensity)
event.putDouble("offsetEnd", alShadow.blankOffsetAtEnd / pixelDensity)
val reactContext = context as ReactContext
reactContext
.getJSModule(RCTEventEmitter::class.java)
.receiveEvent(id, "onBlankAreaEvent", event)
val eventDispatcher: EventDispatcher? =
UIManagerHelper.getEventDispatcherForReactTag(context as ReactContext, id)
if(eventDispatcher != null) {
val surfaceId = UIManagerHelper.getSurfaceId(context as ReactContext)
eventDispatcher.dispatchEvent(OnBlankAreaEvent(surfaceId, id, alShadow.blankOffsetAtStart / pixelDensity, alShadow.blankOffsetAtEnd / pixelDensity))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ package com.shopify.reactnative.flash_list
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.views.view.ReactViewGroup
import com.facebook.react.views.view.ReactViewManager
import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.viewmanagers.AutoLayoutViewManagerDelegate
import com.facebook.react.viewmanagers.AutoLayoutViewManagerInterface
import com.facebook.react.common.MapBuilder
import kotlin.math.roundToInt

/** ViewManager for AutoLayoutView - Container for all RecyclerListView children. Automatically removes all gaps and overlaps for GridLayouts with flexible spans.
* Note: This cannot work for masonry layouts i.e, pinterest like layout */
@ReactModule(name = AutoLayoutViewManager.REACT_CLASS)
class AutoLayoutViewManager: ReactViewManager() {
class AutoLayoutViewManager: ViewGroupManager<AutoLayoutView>(), AutoLayoutViewManagerInterface<AutoLayoutView> {
private val mDelegate: ViewManagerDelegate<AutoLayoutView>

init {
mDelegate = AutoLayoutViewManagerDelegate(this)
}

companion object {
const val REACT_CLASS = "AutoLayoutView"
Expand All @@ -21,45 +28,42 @@ class AutoLayoutViewManager: ReactViewManager() {
return REACT_CLASS
}

override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
override fun createViewInstance(context: ThemedReactContext): AutoLayoutView {
return AutoLayoutView(context).also { it.pixelDensity = context.resources.displayMetrics.density.toDouble() }
}

override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
return MapBuilder.builder<String, Any>().put(
"onBlankAreaEvent",
MapBuilder.of(
"registrationName", "onBlankAreaEvent")
).build();
}
override fun getExportedCustomDirectEventTypeConstants() = mutableMapOf(
"onBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"),
"topOnBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"),
)

@ReactProp(name = "horizontal")
fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) {
override fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) {
view.alShadow.horizontal = isHorizontal
}

@ReactProp(name = "disableAutoLayout")
fun setDisableAutoLayout(view: AutoLayoutView, disableAutoLayout: Boolean) {
override fun setDisableAutoLayout(view: AutoLayoutView, disableAutoLayout: Boolean) {
view.disableAutoLayout = disableAutoLayout
}

@ReactProp(name = "scrollOffset")
fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) {
override fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) {
view.alShadow.scrollOffset = convertToPixelLayout(scrollOffset, view.pixelDensity)
}

@ReactProp(name = "windowSize")
fun setWindowSize(view: AutoLayoutView, windowSize: Double) {
override fun setWindowSize(view: AutoLayoutView, windowSize: Double) {
view.alShadow.windowSize = convertToPixelLayout(windowSize, view.pixelDensity)
}

@ReactProp(name = "renderAheadOffset")
fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) {
override fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) {
view.alShadow.renderOffset = convertToPixelLayout(renderOffset, view.pixelDensity)
}

@ReactProp(name = "enableInstrumentation")
fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) {
override fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) {
view.enableInstrumentation = enableInstrumentation
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ package com.shopify.reactnative.flash_list

import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.views.view.ReactViewGroup
import com.facebook.react.views.view.ReactViewManager
import com.facebook.react.viewmanagers.CellContainerManagerDelegate
import com.facebook.react.viewmanagers.CellContainerManagerInterface

@ReactModule(name = AutoLayoutViewManager.REACT_CLASS)
class CellContainerManager: ReactViewManager() {
class CellContainerManager: ViewGroupManager<CellContainerImpl>(), CellContainerManagerInterface<CellContainerImpl> {
private val mDelegate: ViewManagerDelegate<CellContainerImpl>

init {
mDelegate = CellContainerManagerDelegate(this);
}

companion object {
const val REACT_CLASS = "CellContainer"
}
Expand All @@ -16,12 +24,12 @@ class CellContainerManager: ReactViewManager() {
return REACT_CLASS
}

override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
override fun createViewInstance(context: ThemedReactContext): CellContainerImpl {
return CellContainerImpl(context)
}

@ReactProp(name = "index")
fun setIndex(view: CellContainerImpl, index: Int) {
override fun setIndex(view: CellContainerImpl, index: Int) {
view.index = index
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@file:Suppress("DEPRECATION") // We want to use RCTEventEmitter for interop purposes
package com.shopify.reactnative.flash_list.events

import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.RCTEventEmitter

class OnBlankAreaEvent(surfaceId: Int, viewId: Int, val offsetStart: Double, val offsetEnd: Double) :
Event<OnBlankAreaEvent>(surfaceId, viewId) {

override fun getEventName(): String {
return EVENT_NAME
}

override fun getEventData(): WritableMap {
val eventData: WritableMap = Arguments.createMap()
eventData.putDouble("offsetStart", offsetStart)
eventData.putDouble("offsetEnd", offsetEnd)
return eventData
}

override fun dispatch(rctEventEmitter: RCTEventEmitter) {
rctEventEmitter.receiveEvent(viewTag, eventName, eventData)
}

companion object {
const val EVENT_NAME: String = "onBlankAreaEvent"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* @generated by codegen project: GeneratePropsJavaDelegate.js
*/

package com.facebook.react.viewmanagers;

import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.uimanager.BaseViewManagerDelegate;
import com.facebook.react.uimanager.BaseViewManagerInterface;

public class AutoLayoutViewManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & AutoLayoutViewManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
public AutoLayoutViewManagerDelegate(U viewManager) {
super(viewManager);
}
@Override
public void setProperty(T view, String propName, @Nullable Object value) {
switch (propName) {
case "horizontal":
mViewManager.setHorizontal(view, value == null ? false : (boolean) value);
break;
case "scrollOffset":
mViewManager.setScrollOffset(view, value == null ? 0f : ((Double) value).doubleValue());
break;
case "windowSize":
mViewManager.setWindowSize(view, value == null ? 0f : ((Double) value).doubleValue());
break;
case "renderAheadOffset":
mViewManager.setRenderAheadOffset(view, value == null ? 0f : ((Double) value).doubleValue());
break;
case "enableInstrumentation":
mViewManager.setEnableInstrumentation(view, value == null ? false : (boolean) value);
break;
case "disableAutoLayout":
mViewManager.setDisableAutoLayout(view, value == null ? false : (boolean) value);
break;
default:
super.setProperty(view, propName, value);
}
}
}
Loading
Loading