Skip to content

[Feat] Support for the new architecture #159

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

Merged
merged 14 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 60 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ![React Native Line](/assets/github-banner.png)
# ![React Native Line](/assets/banner.png)

[![npm version](https://img.shields.io/npm/v/@xmartlabs/react-native-line.svg?style=flat-square)](https://www.npmjs.com/package/@xmartlabs/react-native-line)
[![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
Expand All @@ -14,6 +14,11 @@ Line SDK wrapper for React Native 🚀
- iOS `deploymentTarget` needs to be at least version `15.1`.
- [LINE developer account](https://developers.line.biz/console/) with a channel created.

> [!IMPORTANT]
> @xmartlabs/react-native-line v5 is now a TurboModule and **requires the new architecture to be enabled**.
> - If you want to use @xmartlabs/react-native-line v5, you need to enable the new architecture in your app (see how to [enable the new architecture for apps](https://github.com/reactwg/react-native-new-architecture/blob/main/docs/enable-apps.md))
> - If you cannot enable the new architecture yet, downgrade to @xmartlabs/react-native-line v4 for now.

## Installation

### With Expo
Expand Down Expand Up @@ -62,28 +67,66 @@ Line SDK wrapper for React Native 🚀

#### With Swift

```swift
import RNLine
<details>
<summary>@xmartlabs/react-native-line v4</summary>

...
```swift
import RNLine

override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return LineLogin.application(application, open: url, options: options)
}
```
...

override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return LineLogin.application(application, open: url, options: options)
}
```
</details>

<details>
<summary>@xmartlabs/react-native-line v5</summary>

```swift
import react_native_line

...

override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return LineLogin.application(application, open: url, options: options)
}
```
</details>

#### With Objective-C

```objectivec
#import "RNLine-Swift.h"
<details>
<summary>@xmartlabs/react-native-line v4</summary>

...
```objectivec
#import "RNLine-Swift.h"

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [LineLogin application:application open:url options:options];
}
```
...

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [LineLogin application:application open:url options:options];
}
```
</details>

<details>
<summary>@xmartlabs/react-native-line v4</summary>

```objectivec
#import "react_native_line-Swift.h"

...

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [LineLogin application:application open:url options:options];
}
```
</details>
<br>

4. Insert the following snippet in your `Info.plist` to match the [LINE documentation](https://developers.line.biz/en/docs/line-login-sdks/ios-sdk/swift/setting-up-project/#config-infoplist-file):

Expand Down Expand Up @@ -124,7 +167,7 @@ Line SDK wrapper for React Native 🚀
3. Login with the `login` method:

```typescript
LineLogin.login()
LineLogin.login({})
```

## API
Expand Down
27 changes: 0 additions & 27 deletions RNLine.podspec

This file was deleted.

21 changes: 21 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ buildscript {
}
}

def isNewArchitectureEnabled() {
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
}

def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

apply plugin: "com.android.library"
apply plugin: "com.facebook.react"
apply plugin: "kotlin-android"

android {
Expand All @@ -29,10 +34,26 @@ android {
versionCode 1
versionName "4.1.0"
}

sourceSets {
main {
java.srcDirs += ["${project.buildDir}/generated/source/codegen/java"]
}
}
}

dependencies {
implementation "com.facebook.react:react-native:+"
implementation "com.linecorp.linesdk:linesdk:5.11.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
}

tasks.register("checkNativeLineLoginSpec") {
doLast {
if (!isNewArchitectureEnabled()) {
throw new GradleException("@xmartlabs/react-native-line v5 requires your project to have New Architecture enabled.")
}
}
}

preBuild.dependsOn checkNativeLineLoginSpec
2 changes: 1 addition & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xmartlabs.rnline"></manifest>
package="com.xmartlabs.line"></manifest>
Original file line number Diff line number Diff line change
@@ -1,51 +1,50 @@
package com.xmartlabs.rnline
package com.xmartlabs.line

import android.app.Activity
import android.content.Context
import android.content.Intent

import com.facebook.react.bridge.*
import com.facebook.react.module.annotations.ReactModule

import com.linecorp.linesdk.*
import com.linecorp.linesdk.api.LineApiClient
import com.linecorp.linesdk.api.LineApiClientBuilder
import com.linecorp.linesdk.auth.LineAuthenticationConfig
import com.linecorp.linesdk.auth.LineAuthenticationParams
import com.linecorp.linesdk.auth.LineLoginApi
import com.linecorp.linesdk.auth.LineLoginResult
import com.linecorp.linesdk.LineProfile

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.facebook.react.bridge.WritableMap
import com.linecorp.linesdk.*
import com.linecorp.linesdk.LineProfile

private var LOGIN_REQUEST_CODE: Int = 0

enum class LoginArguments(val key: String) {
BOT_PROMPT("botPrompt"),
ONLY_WEB_LOGIN("onlyWebLogin"),
SCOPES("scopes")
}

class LineLogin(private val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
class LineLoginModule(private val reactContext: ReactApplicationContext) :
NativeLineLoginSpec(reactContext) {

companion object {
private const val MODULE_NAME: String = "LineLogin"
const val NAME = NativeLineLoginSpec.NAME
}

private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)

private lateinit var channelId: String
private lateinit var lineApiClient: LineApiClient
private var LOGIN_REQUEST_CODE: Int = 0
private val uiCoroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)

private var loginResult: Promise? = null

override fun getName() = MODULE_NAME

@ReactMethod
fun setup(args: ReadableMap, promise: Promise) {
val context: Context = reactContext.applicationContext
override fun setup(args: ReadableMap, promise: Promise) {
channelId = args.getString("channelId")!!
lineApiClient = LineApiClientBuilder(context, channelId).build()
lineApiClient = LineApiClientBuilder(reactContext.applicationContext, channelId).build()
reactContext.addActivityEventListener(object : ActivityEventListener {
override fun onNewIntent(intent: Intent?) {}
override fun onActivityResult(
Expand All @@ -58,8 +57,7 @@ class LineLogin(private val reactContext: ReactApplicationContext) :
})
}

@ReactMethod
fun login(args: ReadableMap, promise: Promise) {
override fun login(args: ReadableMap, promise: Promise) {
val scopes =
if (args.hasKey(LoginArguments.SCOPES.key)) args.getArray(LoginArguments.SCOPES.key)!!
.toArrayList() as List<String> else listOf("profile")
Expand Down Expand Up @@ -113,9 +111,8 @@ class LineLogin(private val reactContext: ReactApplicationContext) :
this.loginResult = promise
}

@ReactMethod
fun getProfile(promise: Promise) {
uiCoroutineScope.launch {
override fun getProfile(promise: Promise) {
coroutineScope.launch {
val lineApiResponse = withContext(Dispatchers.IO) { lineApiClient.getProfile() }
if (!lineApiResponse.isSuccess) {
promise.reject(
Expand Down Expand Up @@ -168,9 +165,8 @@ class LineLogin(private val reactContext: ReactApplicationContext) :
loginResult = null
}

@ReactMethod
fun logout(promise: Promise) {
uiCoroutineScope.launch {
override fun logout(promise: Promise) {
coroutineScope.launch {
val lineApiResponse = withContext(Dispatchers.IO) { lineApiClient.logout() }
if (lineApiResponse.isSuccess) {
promise.resolve(null)
Expand All @@ -184,29 +180,25 @@ class LineLogin(private val reactContext: ReactApplicationContext) :
}
}

@ReactMethod
fun getCurrentAccessToken(promise: Promise) = invokeLineServiceMethod(
override fun getCurrentAccessToken(promise: Promise) = invokeLineServiceMethod(
promise = promise,
serviceCallable = { lineApiClient.getCurrentAccessToken() },
parser = { parseAccessToken(it, lineIdToken = null) }
)

@ReactMethod
fun getFriendshipStatus(promise: Promise) = invokeLineServiceMethod(
override fun getFriendshipStatus(promise: Promise) = invokeLineServiceMethod(
promise = promise,
serviceCallable = { lineApiClient.getFriendshipStatus() },
parser = { parseFriendshipStatus(it) }
)

@ReactMethod
fun refreshAccessToken(promise: Promise) = invokeLineServiceMethod(
override fun refreshAccessToken(promise: Promise) = invokeLineServiceMethod(
promise = promise,
serviceCallable = { lineApiClient.refreshAccessToken() },
parser = { parseAccessToken(it, lineIdToken = null) }
)

@ReactMethod
fun verifyAccessToken(promise: Promise) = invokeLineServiceMethod(
override fun verifyAccessToken(promise: Promise) = invokeLineServiceMethod(
promise = promise,
serviceCallable = { lineApiClient.verifyToken() },
parser = { parseVerifyAccessToken(it) }
Expand All @@ -217,7 +209,7 @@ class LineLogin(private val reactContext: ReactApplicationContext) :
serviceCallable: () -> LineApiResponse<T>,
parser: (T) -> WritableMap
) {
uiCoroutineScope.launch {
coroutineScope.launch {
val lineApiResponse = withContext(Dispatchers.IO) { serviceCallable.invoke() }
if (lineApiResponse.isSuccess) {
promise.resolve(parser.invoke(lineApiResponse.responseData))
Expand Down
30 changes: 30 additions & 0 deletions android/src/main/java/com/xmartlabs/line/LineLoginPackage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.xmartlabs.line

import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.TurboReactPackage
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider

class LineLoginPackage : TurboReactPackage() {
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
when (name) {
LineLoginModule.NAME -> LineLoginModule(reactContext)
else -> null
}

override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
ReactModuleInfoProvider {
mapOf(
LineLoginModule.NAME to ReactModuleInfo(
LineLoginModule.NAME,
LineLoginModule.NAME,
canOverrideExistingModule = false,
needsEagerInit = false,
hasConstants = true,
isCxxModule = false,
isTurboModule = true
)
)
}
}
30 changes: 0 additions & 30 deletions android/src/main/java/com/xmartlabs/rnline/RNLinePackage.java

This file was deleted.

3 changes: 0 additions & 3 deletions android/src/main/res/values/strings.xml

This file was deleted.

File renamed without changes
Binary file removed assets/line-button.sketch
Binary file not shown.
Loading