Close Menu
  • Home
  • AI
  • Big Data
  • Cloud Computing
  • iOS Development
  • IoT
  • IT/ Cybersecurity
  • Tech
    • Nanotechnology
    • Green Technology
    • Apple
    • Software Development
    • Software Engineering

Subscribe to Updates

Get the latest technology news from Bigteetechhub about IT, Cybersecurity and Big Data.

    What's Hot

    Google’s Plan to Fix a Broken System

    November 11, 2025

    swift – IOS app not opened or prompted to open when Universal Link used

    November 10, 2025

    A new era and new features in Azure Ultra Disk

    November 10, 2025
    Facebook X (Twitter) Instagram
    Facebook X (Twitter) Instagram
    Big Tee Tech Hub
    • Home
    • AI
    • Big Data
    • Cloud Computing
    • iOS Development
    • IoT
    • IT/ Cybersecurity
    • Tech
      • Nanotechnology
      • Green Technology
      • Apple
      • Software Development
      • Software Engineering
    Big Tee Tech Hub
    Home»iOS Development»I can not link IOS module to React Native CLI
    iOS Development

    I can not link IOS module to React Native CLI

    big tee tech hubBy big tee tech hubSeptember 7, 2025009 Mins Read
    Share Facebook Twitter Pinterest Copy Link LinkedIn Tumblr Email Telegram WhatsApp
    Follow Us
    Google News Flipboard
    I can not link IOS module to React Native CLI
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link


    I am creating widget app in react native cli. It is a countdown widget that has 5 backgrounds which the user can pick. And the user can also upload custom image to the background of the widget. When i tried to use NativeModules.RNWidgetModule, it is being null. How can i solve this?

    (I am not IOS developer. I wrote ios codes with AI.)

    SkinPicker.ts

    /* eslint-disable react-native/no-inline-styles */
    import React from 'react';
    import {
      View,
      TouchableOpacity,
      StyleSheet,
      Text,
      BackHandler,
      Platform,
    } from 'react-native';
    import LinearGradient from 'react-native-linear-gradient';
    import { NativeModules } from 'react-native';
    const { WidgetModule } = NativeModules;
    
    // Helper to apply skin
    const setWidgetSkin = (skinName: string) => {
      if (Platform.OS === 'android' && WidgetModule) {
        WidgetModule.setSkin(skinName);
      }
    
      if (Platform.OS === 'ios' && NativeModules.RNWidgetModule) {
        NativeModules.RNWidgetModule.setSkin(skinName);
        NativeModules.RNWidgetModule.reloadWidgets();
      }
    
      if (Platform.OS === 'android') {
        BackHandler.exitApp();
      }
    };
    
    // Skin definitions
    const skins = [
      {
        name: 'FIDESZ',
        type: 'solid',
        color: '#FF6A13',
      },
      {
        name: 'TISZA',
        type: 'gradient',
        colors: ['#24B573', '#ED4551'],
      },
      {
        name: 'KUTYAPART',
        type: 'dots', // two-color split + two red dots
        colors: ['#FFFFFF', '#000000'],
        dotColor: '#DA0000',
      },
      {
        name: 'DK',
        type: 'gradient',
        colors: ['#0062A7', '#C50067', '#FFD500', '#2DAAE1'],
      },
      {
        name: 'MI_HAZANK',
        type: 'solid',
        color: '#678B1D',
      },
    ];
    
    export default function SkinPicker() {
      return (
        
          Háttér kiválasztása
          
            {skins.map(skin => {
              if (skin.type === 'solid') {
                return (
                   setWidgetSkin(skin.name)}
                  />
                );
              } else if (skin.type === 'gradient') {
                return (
                   setWidgetSkin(skin.name)}
                  >
                    
                  
                );
              } else if (skin.type === 'dots') {
                // Split box + 2 red dots
                return (
                   setWidgetSkin(skin.name)}
                  >
                    
                      
                      
                      {/* Red dots */}
                      
                      
                    
                  
                );
              }
            })}
          
        
      );
    }
    
    const styles = StyleSheet.create({
      headerText: {
        textAlign: 'center',
        fontSize: 20,
        marginBottom: 10,
      },
      row: {
        flexDirection: 'row',
        justifyContent: 'space-around',
        gap: 10,
      },
      box: {
        width: 50,
        height: 50,
        borderRadius: 8,
        borderWidth: 1,
        borderColor: '#333',
        overflow: 'hidden',
      },
      dot: {
        position: 'absolute',
        width: 8,
        height: 8,
        borderRadius: 4,
      },
    });
    

    RNWidgetModule.m

    #import 
    
    @interface RCT_EXTERN_MODULE(RNWidgetModule, NSObject)
    
    RCT_EXTERN_METHOD(setSkin:(NSString *)skinName)
    
    RCT_EXTERN_METHOD(setCustomBackground:(NSString *)imageUri
                      resolver:(RCTPromiseResolveBlock)resolve
                      rejecter:(RCTPromiseRejectBlock)reject)
    
    RCT_EXTERN_METHOD(removeCustomBackground)
    
    RCT_EXTERN_METHOD(reloadWidgets)
    
    @end
    

    RNWidgetModule.swift

    import Foundation
    import React
    import WidgetKit
    import UIKit
    
    @objc(RNWidgetModule)
    class RNWidgetModule: NSObject {
        
        @objc
        static func requiresMainQueueSetup() -> Bool {
            return false
        }
        
        @objc
        func setSkin(_ skinName: String) {
            DispatchQueue.main.async {
                guard let userDefaults = UserDefaults(suiteName: "group.ittazido") else {
                    print("Failed to get UserDefaults with suite name")
                    return
                }
                
                userDefaults.set(skinName, forKey: "selectedSkin")
                userDefaults.set(false, forKey: "isCustomBackground")
                userDefaults.removeObject(forKey: "customBackgroundData")
                WidgetCenter.shared.reloadAllTimelines()
                userDefaults.synchronize()
                
                print("Skin set to: \(skinName)")
                self.reloadWidgets()
            }
        }
        
        @objc
        func setCustomBackground(_ imageUri: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
            DispatchQueue.main.async {
                guard let url = URL(string: imageUri) else {
                    rejecter("INVALID_URI", "Invalid image URI", nil)
                    return
                }
                
                // Handle different URI schemes
                var imageData: Data?
                
                if imageUri.hasPrefix("ph://") {
                    // Photo library asset
                    self.loadPhotoLibraryAsset(url: url) { data in
                        if let data = data {
                            self.saveCustomBackground(data: data, resolver: resolver, rejecter: rejecter)
                        } else {
                            rejecter("LOAD_FAILED", "Failed to load image from photo library", nil)
                        }
                    }
                    return
                } else if imageUri.hasPrefix("file://") {
                    // File system
                    let filePath = url.path
                    imageData = NSData(contentsOfFile: filePath) as Data?
                } else if imageUri.hasPrefix("data:") {
                    // Base64 data URI
                    if let range = imageUri.range(of: ",") {
                        let base64String = String(imageUri[range.upperBound...])
                        imageData = Data(base64Encoded: base64String)
                    }
                }
                
                guard let data = imageData else {
                    rejecter("LOAD_FAILED", "Failed to load image data", nil)
                    return
                }
                
                self.saveCustomBackground(data: data, resolver: resolver, rejecter: rejecter)
            }
        }
        
        private func loadPhotoLibraryAsset(url: URL, completion: @escaping (Data?) -> Void) {
            import Photos
            
            let assetId = url.absoluteString.replacingOccurrences(of: "ph://", with: "")
            let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil)
            
            guard let asset = fetchResult.firstObject else {
                completion(nil)
                return
            }
            
            let imageManager = PHImageManager.default()
            let options = PHImageRequestOptions()
            options.isSynchronous = false
            options.deliveryMode = .highQualityFormat
            
            imageManager.requestImage(for: asset, targetSize: CGSize(width: 800, height: 400), contentMode: .aspectFill, options: options) { image, _ in
                guard let image = image else {
                    completion(nil)
                    return
                }
                
                completion(image.jpegData(compressionQuality: 0.8))
            }
        }
        
        private func saveCustomBackground(data: Data, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
            guard let userDefaults = UserDefaults(suiteName: "group.ittazido") else {
                rejecter("USERDEFAULTS_ERROR", "Failed to get UserDefaults", nil)
                return
            }
            
            // Resize image if needed
            guard let image = UIImage(data: data) else {
                rejecter("INVALID_IMAGE", "Invalid image data", nil)
                return
            }
            
            let resizedImage = self.resizeImage(image: image, maxWidth: 800, maxHeight: 400)
            guard let resizedData = resizedImage.jpegData(compressionQuality: 0.8) else {
                rejecter("RESIZE_FAILED", "Failed to resize image", nil)
                return
            }
            
            userDefaults.set(true, forKey: "isCustomBackground")
            userDefaults.set(resizedData, forKey: "customBackgroundData")
            userDefaults.synchronize()
            
            self.reloadWidgets()
            resolver("Custom background set successfully")
        }
        
        @objc
        func removeCustomBackground() {
            DispatchQueue.main.async {
                guard let userDefaults = UserDefaults(suiteName: "group.ittazido") else {
                    return
                }
                
                userDefaults.set(false, forKey: "isCustomBackground")
                userDefaults.removeObject(forKey: "customBackgroundData")
                userDefaults.synchronize()
                
                self.reloadWidgets()
            }
        }
        
        @objc
        func reloadWidgets() {
            DispatchQueue.main.async {
                if #available(iOS 14.0, *) {
                    WidgetCenter.shared.reloadAllTimelines()
                    print("Widgets reloaded")
                }
            }
        }
        
        private func resizeImage(image: UIImage, maxWidth: CGFloat, maxHeight: CGFloat) -> UIImage {
            let size = image.size
            
            if size.width <= maxWidth && size.height <= maxHeight {
                return image
            }
            
            let widthRatio = maxWidth / size.width
            let heightRatio = maxHeight / size.height
            let ratio = min(widthRatio, heightRatio)
            
            let newSize = CGSize(width: size.width * ratio, height: size.height * ratio)
            
            UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
            image.draw(in: CGRect(origin: .zero, size: newSize))
            let newImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            
            return newImage ?? image
        }
    }
    
    // MARK: - React Native Bridge
    extension RNWidgetModule: RCTBridgeModule {
        static func moduleName() -> String! {
            return "RNWidgetModule"
        }
    }
    

    CountdownWidget.swift

    import WidgetKit
    import SwiftUI
    import Foundation
    
    struct CountdownData {
        let targetDate: Date
        let lastFetchTime: Date
    }
    
    enum WidgetSkin: String, CaseIterable {
        case fidesz = "FIDESZ"
        case tisza = "TISZA"
        case kutyapart = "KUTYAPART"
        case dk = "DK"
        case miHazank = "MI_HAZANK"
    }
    
    struct CountdownTimelineProvider: TimelineProvider {
        typealias Entry = CountdownEntry
        
        func placeholder(in context: Context) -> CountdownEntry {
            CountdownEntry(date: Date(), targetDate: getDefaultTargetDate())
        }
        
        func getSnapshot(in context: Context, completion: @escaping (CountdownEntry) -> Void) {
            let entry = CountdownEntry(date: Date(), targetDate: getTargetDate())
            completion(entry)
        }
        
        func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {
            fetchTargetDateFromAPI { targetDate in
                var entries: [CountdownEntry] = []
                let currentDate = Date()
                
                // Create entries for every second for the next minute
                for secondOffset in 0..<60 {
                    let entryDate = Calendar.current.date(byAdding: .second, value: secondOffset, to: currentDate)!
                    let entry = CountdownEntry(date: entryDate, targetDate: targetDate)
                    entries.append(entry)
                }
                
                // Update every minute
                let timeline = Timeline(entries: entries, policy: .after(Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!))
                completion(timeline)
            }
        }
        
        private func fetchTargetDateFromAPI(completion: @escaping (Date) -> Void) {
            guard let url = URL(string: " else {
                completion(getDefaultTargetDate())
                return
            }
            
            URLSession.shared.dataTask(with: url) { data, response, error in
                guard let data = data,
                      let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
                      let timeString = json["time"] as? String else {
                    completion(getDefaultTargetDate())
                    return
                }
                
                let formatter = DateFormatter()
                formatter.dateFormat = "yyyy-MM-dd H:mm:ss"
                formatter.timeZone = TimeZone(identifier: "Europe/Budapest")
                
                if let date = formatter.date(from: timeString) {
                    // Cache the result
                    UserDefaults(suiteName: "group.ittazido")?.set(date, forKey: "targetDate")
                    UserDefaults(suiteName: "group.ittazido")?.set(Date(), forKey: "lastFetchTime")
                    completion(date)
                } else {
                    completion(getDefaultTargetDate())
                }
            }.resume()
        }
        
        private func getTargetDate() -> Date {
            guard let userDefaults = UserDefaults(suiteName: "group.ittazido"),
                  let cachedDate = userDefaults.object(forKey: "targetDate") as? Date,
                  let lastFetch = userDefaults.object(forKey: "lastFetchTime") as? Date,
                  Date().timeIntervalSince(lastFetch) < 600 else {
                return getDefaultTargetDate()
            }
            return cachedDate
        }
        
        private func getDefaultTargetDate() -> Date {
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd H:mm:ss"
            formatter.timeZone = TimeZone(identifier: "Europe/Budapest")
            return formatter.date(from: "2026-04-12 06:00:00") ?? Date()
        }
    }
    
    struct CountdownEntry: TimelineEntry {
        let date: Date
        let targetDate: Date
    }
    
    struct CountdownWidgetEntryView: View {
        var entry: CountdownEntry
    
        @State private var now = Date()
        
        @AppStorage("selectedSkin", store: UserDefaults(suiteName: "group.ittazido"))
        private var selectedSkin: String = "DK"
        
        @AppStorage("isCustomBackground", store: UserDefaults(suiteName: "group.ittazido"))
        private var isCustomBackground: Bool = false
        
        private var currentSkin: WidgetSkin {
            WidgetSkin(rawValue: selectedSkin) ?? .dk
        }
        
        private var customBackgroundData: Data? {
            UserDefaults(suiteName: "group.ittazido")?.data(forKey: "customBackgroundData")
        }
        
        var body: some View {
            ZStack {
                // Background
                if isCustomBackground, let imageData = customBackgroundData, let uiImage = UIImage(data: imageData) {
                    Image(uiImage: uiImage)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                } else {
                    getSkinBackground(for: currentSkin)
                }
                
                // Overlay red dots for KUTYAPART
                if currentSkin == .kutyapart {
                    VStack {
                        HStack {
                            Circle()
                                .fill(Color.red)
                                .frame(width: 8, height: 8)
                            Spacer()
                        }
                        Spacer()
                        HStack {
                            Spacer()
                            Circle()
                                .fill(Color.red)
                                .frame(width: 8, height: 8)
                        }
                    }
                    .padding(5)
                }
                
                // Countdown text
                Text(getCountdownText())
                    .font(.system(size: 24, weight: .bold))
                    .foregroundColor(getTextColor(for: currentSkin))
                    .shadow(color: .black.opacity(0.5), radius: 2, x: 1, y: 1)
                    .multilineTextAlignment(.center)
        
            }
            .containerBackground(for: .widget) {
                Color.clear
            }
        }
        
        private func getCountdownText() -> String {
            let now = entry.date
            let target = entry.targetDate
            let diff = target.timeIntervalSince(now)
            
            if diff > 0 {
                let days = Int(diff) / (24 * 60 * 60)
                let hours = (Int(diff) % (24 * 60 * 60)) / (60 * 60)
                let minutes = (Int(diff) % (60 * 60)) / 60
                let seconds = Int(diff) % 60
                return String(format: "%d nap %02d:%02d:%02d", days, hours, minutes, seconds)
            } else if diff >= -24 * 60 * 60 {
                return "ITT AZ IDŐ!"
            } else {
                return "4 év múlva újra találkozunk!"
            }
        }
        
        private func getSkinBackground(for skin: WidgetSkin) -> some View {
            Group {
                switch skin {
                case .fidesz:
                    RoundedRectangle(cornerRadius: 8)
                        .fill(Color(red: 1.0, green: 0.416, blue: 0.075))
                        .overlay(
                            RoundedRectangle(cornerRadius: 8)
                                .stroke(Color.black.opacity(0.2), lineWidth: 1)
                        )
                    
                case .tisza:
                    RoundedRectangle(cornerRadius: 8)
                        .fill(
                            LinearGradient(
                                colors: [
                                    Color(red: 0.141, green: 0.710, blue: 0.451),
                                    Color(red: 0.929, green: 0.271, blue: 0.318)
                                ],
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                            )
                        )
                        .overlay(
                            RoundedRectangle(cornerRadius: 8)
                                .stroke(Color.black.opacity(0.2), lineWidth: 1)
                        )
                    
                case .kutyapart:
                    RoundedRectangle(cornerRadius: 8)
                        .fill(
                            LinearGradient(
                                colors: [Color.white, Color.black],
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                            )
                        )
                        .overlay(
                            RoundedRectangle(cornerRadius: 8)
                                .stroke(Color.black.opacity(0.2), lineWidth: 1)
                        )
                    
                case .dk:
                    RoundedRectangle(cornerRadius: 8)
                        .fill(
                            LinearGradient(
                                colors: [
                                    Color(red: 0.004, green: 0.384, blue: 0.655),
                                    Color(red: 0.773, green: 0.000, blue: 0.404),
                                    Color(red: 1.0, green: 0.835, blue: 0.0)
                                ],
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                            )
                        )
                        .overlay(
                            RoundedRectangle(cornerRadius: 8)
                                .stroke(Color.black.opacity(0.2), lineWidth: 1)
                        )
                    
                case .miHazank:
                    RoundedRectangle(cornerRadius: 8)
                        .fill(Color(red: 0.404, green: 0.545, blue: 0.114))
                        .overlay(
                            RoundedRectangle(cornerRadius: 8)
                                .stroke(Color.black.opacity(0.2), lineWidth: 1)
                        )
                }
            }
        }
        
        private func getTextColor(for skin: WidgetSkin) -> Color {
            switch skin {
            case .kutyapart:
                return .black
            default:
                return .white
            }
        }
    }
    
    struct CountdownWidget: Widget {
        let kind: String = "CountdownWidget"
        
        var body: some WidgetConfiguration {
            StaticConfiguration(kind: kind, provider: CountdownTimelineProvider()) { entry in
                CountdownWidgetEntryView(entry: entry)
            }
            .configurationDisplayName("Countdown Widget")
            .description("Displays a countdown to the target date.")
            .supportedFamilies([.systemSmall, .systemMedium])
        }
    }
    
    #Preview(as: .systemSmall) {
        CountdownWidget()
    } timeline: {
        let targetDate = Calendar.current.date(byAdding: .day, value: 30, to: Date()) ?? Date()
        CountdownEntry(date: .now, targetDate: targetDate)
    }
    



    Source link

    CLI iOS Link Module native React
    Follow on Google News Follow on Flipboard
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    tonirufai
    big tee tech hub
    • Website

    Related Posts

    swift – IOS app not opened or prompted to open when Universal Link used

    November 10, 2025

    android – MissingPluginException(No implementation found for method OneSignal#initialize on channel OneSignal

    November 9, 2025

    How to access and modify Swift class properties using Frida on iOS?

    November 8, 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Editors Picks

    Google’s Plan to Fix a Broken System

    November 11, 2025

    swift – IOS app not opened or prompted to open when Universal Link used

    November 10, 2025

    A new era and new features in Azure Ultra Disk

    November 10, 2025

    The EPA Is in Chaos

    November 10, 2025
    About Us
    About Us

    Welcome To big tee tech hub. Big tee tech hub is a Professional seo tools Platform. Here we will provide you only interesting content, which you will like very much. We’re dedicated to providing you the best of seo tools, with a focus on dependability and tools. We’re working to turn our passion for seo tools into a booming online website. We hope you enjoy our seo tools as much as we enjoy offering them to you.

    Don't Miss!

    Google’s Plan to Fix a Broken System

    November 11, 2025

    swift – IOS app not opened or prompted to open when Universal Link used

    November 10, 2025

    Subscribe to Updates

    Get the latest technology news from Bigteetechhub about IT, Cybersecurity and Big Data.

      • About Us
      • Contact Us
      • Disclaimer
      • Privacy Policy
      • Terms and Conditions
      © 2025 bigteetechhub.All Right Reserved

      Type above and press Enter to search. Press Esc to cancel.