From 900be84c7f9ab9aa943245dd5fc74b84f509e7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Wed, 4 Dec 2024 09:48:45 +0100 Subject: [PATCH] feat: macos ci --- .github/workflows/ci.yml | 46 ++++++++++++++ package.json | 1 + .../ios/RCTTabViewViewManager.mm | 3 - .../ios/TabViewImpl.swift | 60 +++++++++---------- .../react-native-bottom-tabs.podspec | 6 +- turbo.json | 15 +++++ 6 files changed, 97 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0141b8d..9d1b189 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -242,3 +242,49 @@ jobs: - name: Build example for iOS run: | yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" + + build-macos: + runs-on: macos-15 + env: + TURBO_CACHE_DIR: .turbo/macos + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build package + run: yarn build + + - name: Install xcbeautify + run: | + brew install xcbeautify + + - name: Cache turborepo for macOS + uses: actions/cache@v3 + with: + path: ${{ env.TURBO_CACHE_DIR }} + key: ${{ runner.os }}-turborepo-macos-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-turborepo-macos- + + - name: Check turborepo cache for macOS + run: | + TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:macos --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:macos').cache.status") + + if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then + echo "turbo_cache_hit=1" >> $GITHUB_ENV + fi + + - name: Install cocoapods + if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' + run: | + cd apps/example + pod install --project-directory=macos + env: + NO_FLIPPER: 1 + + - name: Build example for macOS + run: | + yarn turbo run build:macos --cache-dir="${{ env.TURBO_CACHE_DIR }}" diff --git a/package.json b/package.json index 69df5c3..b0ccdfc 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "build:android": "turbo run build:android", "build:android:fabric": "turbo run build:android:fabric", "build:ios": "turbo run build:ios", + "build:macos": "turbo run build:macos", "build:ios-expo": "turbo run build:ios-expo", "build:android-expo": "turbo run build:android-expo", "publish-packages": "turbo run build lint && changeset version && changeset publish" diff --git a/packages/react-native-bottom-tabs/ios/RCTTabViewViewManager.mm b/packages/react-native-bottom-tabs/ios/RCTTabViewViewManager.mm index d4bde13..2c1194b 100644 --- a/packages/react-native-bottom-tabs/ios/RCTTabViewViewManager.mm +++ b/packages/react-native-bottom-tabs/ios/RCTTabViewViewManager.mm @@ -2,9 +2,6 @@ #import #import #import -#if TARGET_OS_OSX -#import -#endif #if TARGET_OS_OSX #import diff --git a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift index 07461e7..636cf72 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift @@ -14,12 +14,12 @@ struct TabViewImpl: View { #else @Weak var tabBar: UITabBar? #endif - + var onSelect: (_ key: String) -> Void var onLongPress: (_ key: String) -> Void var onLayout: (_ size: CGSize) -> Void var onTabBarMeasured: (_ height: Int) -> Void - + var body: some View { TabView(selection: $props.selectedPage) { ForEach(props.children.indices, id: \.self) { index in @@ -34,7 +34,7 @@ struct TabViewImpl: View { guard let key = props.items.filter({ !$0.hidden || $0.key == props.selectedPage })[safe: index]?.key else { return } - + if isLongPress { onLongPress(key) emitHapticFeedback(longPress: true) @@ -73,17 +73,17 @@ struct TabViewImpl: View { #endif } } - + @ViewBuilder private func renderTabItem(at index: Int) -> some View { let tabData = props.items[safe: index] let isHidden = tabData?.hidden ?? false let isFocused = props.selectedPage == tabData?.key - + if !isHidden || isFocused { let child = props.children[safe: index] ?? PlatformView() let icon = props.icons[index] - + RepresentableView(view: child) .ignoresTopSafeArea( props.ignoresTopSafeArea, @@ -103,7 +103,7 @@ struct TabViewImpl: View { #if !os(macOS) updateTabBarAppearance(props: props, tabBar: tabBar) #endif - + #if os(iOS) guard index >= 4, let key = tabData?.key, @@ -113,13 +113,13 @@ struct TabViewImpl: View { } } } - + func emitHapticFeedback(longPress: Bool = false) { #if os(iOS) if !props.hapticFeedbackEnabled { return } - + if longPress { UINotificationFeedbackGenerator().notificationOccurred(.success) } else { @@ -132,12 +132,12 @@ struct TabViewImpl: View { #if !os(macOS) private func updateTabBarAppearance(props: TabViewProps, tabBar: UITabBar?) { guard let tabBar else { return } - + if props.scrollEdgeAppearance == "transparent" { configureTransparentAppearance(tabBar: tabBar, props: props) return } - + configureStandardAppearance(tabBar: tabBar, props: props) } #endif @@ -149,8 +149,8 @@ private func createFontAttributes( inactiveTintColor: PlatformColor? ) -> [NSAttributedString.Key: Any] { var attributes: [NSAttributedString.Key: Any] = [:] - - + + if family != nil || weight != nil { attributes[.font] = RCTFont.update( nil, @@ -164,7 +164,7 @@ private func createFontAttributes( } else { attributes[.font] = UIFont.boldSystemFont(ofSize: size) } - + return attributes } @@ -180,9 +180,9 @@ private func configureTransparentAppearance(tabBar: UITabBar, props: TabViewProp tabBar.barTintColor = props.barTintColor tabBar.isTranslucent = props.translucent tabBar.unselectedItemTintColor = props.inactiveTintColor - + guard let items = tabBar.items else { return } - + let fontSize = props.fontSize != nil ? CGFloat(props.fontSize!) : tabBarDefaultFontSize let attributes = createFontAttributes( size: fontSize, @@ -190,7 +190,7 @@ private func configureTransparentAppearance(tabBar: UITabBar, props: TabViewProp weight: props.fontWeight, inactiveTintColor: nil ) - + items.forEach { item in item.setTitleTextAttributes(attributes, for: .normal) } @@ -198,7 +198,7 @@ private func configureTransparentAppearance(tabBar: UITabBar, props: TabViewProp private func configureStandardAppearance(tabBar: UITabBar, props: TabViewProps) { let appearance = UITabBarAppearance() - + // Configure background switch props.scrollEdgeAppearance { case "opaque": @@ -207,33 +207,33 @@ private func configureStandardAppearance(tabBar: UITabBar, props: TabViewProps) appearance.configureWithDefaultBackground() } appearance.backgroundColor = props.barTintColor - + // Configure item appearance let itemAppearance = UITabBarItemAppearance() let fontSize = props.fontSize != nil ? CGFloat(props.fontSize!) : tabBarDefaultFontSize - + var attributes = createFontAttributes( size: fontSize, family: props.fontFamily, weight: props.fontWeight, inactiveTintColor: props.inactiveTintColor ) - + if let inactiveTintColor = props.inactiveTintColor { attributes[.foregroundColor] = inactiveTintColor } - + if let inactiveTintColor = props.inactiveTintColor { itemAppearance.normal.iconColor = inactiveTintColor } - + itemAppearance.normal.titleTextAttributes = attributes - + // Apply item appearance to all layouts appearance.stackedLayoutAppearance = itemAppearance appearance.inlineLayoutAppearance = itemAppearance appearance.compactInlineLayoutAppearance = itemAppearance - + // Apply final appearance tabBar.standardAppearance = appearance if #available(iOS 15.0, *) { @@ -259,7 +259,7 @@ extension View { self } } - + @ViewBuilder func tabBadge(_ data: String?) -> some View { if #available(iOS 15.0, macOS 15.0, visionOS 2.0, tvOS 15.0, *) { @@ -276,7 +276,7 @@ extension View { self } } - + @ViewBuilder func ignoresTopSafeArea( _ flag: Bool, @@ -290,7 +290,7 @@ extension View { .ignoresSafeArea(.container, edges: .bottom) } } - + #if !os(macOS) @ViewBuilder func configureAppearance(props: TabViewProps, tabBar: UITabBar?) -> some View { @@ -321,7 +321,7 @@ extension View { } } #endif - + @ViewBuilder func tintColor(_ color: PlatformColor?) -> some View { if let color { @@ -335,7 +335,7 @@ extension View { self } } - + // Allows TabView to use unfilled SFSymbols. // By default they are always filled. @ViewBuilder diff --git a/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec b/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec index 54a307c..cafd8e5 100644 --- a/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec +++ b/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec @@ -12,7 +12,11 @@ Pod::Spec.new do |s| s.license = package["license"] s.authors = package["author"] - s.platforms = { :ios => "14.0", :visionos => "1.0", :tvos => "15.1", :macos => "11.0" } + s.ios.deployment_target = "14.0" + s.visionos.deployment_target = "1.0" + s.tvos.deployment_target = "15.1" + s.osx.deployment_target = "11.0" + s.source = { :git => "https://github.com/okwasniewski/react-native-bottom-tabs.git", :tag => "#{s.version}" } s.source_files = "ios/**/*.{h,m,mm,cpp,swift}" diff --git a/turbo.json b/turbo.json index 9275846..23cc65c 100644 --- a/turbo.json +++ b/turbo.json @@ -73,6 +73,21 @@ ], "outputs": [] }, + "build:macos": { + "env": ["RCT_NEW_ARCH_ENABLED"], + "inputs": [ + "packages/*/package.json", + "packages/*/*.podspec", + "packages/*/ios", + "packages/*/src/*.ts", + "packages/*/src/*.tsx", + "apps/example/package.json", + "apps/example/macos", + "!apps/example/macos/build", + "!apps/example/macos/Pods" + ], + "outputs": [] + }, "build:ios-expo": { "env": ["RCT_NEW_ARCH_ENABLED"], "inputs": [