├── .babelrc.js
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .prettierrc.js
├── @types
└── index.d.ts
├── LICENSE
├── README.md
├── RNAKakaoSDK.podspec
├── android
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── io
│ └── actbase
│ └── kakaosdk
│ ├── RNAKakaoSDK.java
│ └── RNAKakaoSDKPackage.java
├── assets
└── xcode_0501.png
├── ios
├── RNAKakaoSDK-Bridging-Header.h
├── RNAKakaoSDK.swift
├── RNAKakaoSDK.xcodeproj
│ └── project.pbxproj
├── RNAKakaoSDKModule.h
├── RNAKakaoSDKModule.m
├── WithKakaoSDK.h
└── WithKakaoSDK.m
├── package.json
├── src
├── app.native.ts
├── app.ts
├── index.ts
└── types.ts
├── src_bin
└── install
└── tsconfig.json
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = api => {
2 | api.cache(true);
3 | return {
4 | presets: [['@babel/preset-env']],
5 | plugins: [
6 | '@babel/plugin-transform-react-jsx',
7 | '@babel/plugin-proposal-object-rest-spread',
8 | '@babel/plugin-proposal-optional-chaining',
9 | "inline-import-data-uri"
10 | ],
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | example/
2 | dist/
3 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "parserOptions": {
3 | "ecmaVersion": 2018,
4 | "sourceType": "module",
5 | "ecmaFeatures": {
6 | "jsx": true,
7 | "modules": true,
8 | "experimentalObjectRestSpread": true
9 | }
10 | },
11 | "env": {
12 | "browser" : true,
13 | "node": true
14 | },
15 | "parser": "@typescript-eslint/parser",
16 | "plugins": ["react", "@typescript-eslint/eslint-plugin", "babel"],
17 | "extends": ["eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended"],
18 | "globals": { "Promise": true, "setTimeout": true, "FormData": true, "global": true, "document": true },
19 | "rules": {
20 | "@typescript-eslint/camelcase": 0
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | /node_modules
4 | /dist
5 | /lib
6 | /modules
7 | /bin
8 |
9 | .idea
10 | .watchmanconfig
11 | package-lock.json
12 | .next
13 | android/local.properties
14 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 |
2 | .idea/
3 | .git*
4 | src/
5 | src_modules/
6 | example/
7 | docs/
8 | assets/
9 |
10 | tsconfig.json
11 | .babelrc.js
12 | .gitignore
13 | .npmignore
14 | .prettierrc.js
15 | yarn.lock
16 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | semi: true,
4 | useTabs: false,
5 | tabWidth: 2,
6 | trailingComma: 'all',
7 | printWidth: 120,
8 | };
9 |
--------------------------------------------------------------------------------
/@types/index.d.ts:
--------------------------------------------------------------------------------
1 | export declare global {
2 | interface Window {
3 | Kakao: any;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Creamcookie
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # KakaoSDK for React, React-Native
2 |
3 | 
4 | [](https://www.npmjs.com/package/@actbase/react-kakaosdk)
5 | [](https://www.npmjs.com/package/@actbase/react-kakaosdk)
6 | [](https://github.com/actbase/react-kakaosdk/issues)
7 | [](https://github.com/actbase/react-kakaosdk/issues?q=is%3Aissue+is%3Aclosed)
8 | [](http://github.com/actbase/react-kakaosdk/issues)
9 |
10 | ## Use Dependencies
11 |
12 | | iOS | Android | Web |
13 | | --------------------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
14 | | [2.5.1](https://developers.kakao.com/docs/latest/ko/sdk-download/ios) | [2.5.0](https://developers.kakao.com/docs/latest/ko/sdk-download/android) | [1.39.14](https://developers.kakao.com/docs/latest/ko/sdk-download/js) |
15 |
16 | ### 처음 설치 시 주의 사항 (React-Native 만)
17 |
18 | 해당 모듈은 Swift로 되어있어서
19 | 그냥 가동 시 작동이 안될 수 있습니다.
20 |
21 | Xcode에서 프로젝트 내 비어있는 Swift File를 새로 만들고 Headers 생성을 누르면
22 | 스위프트 모드로 잡히면서 정상적으로 돌게 됩니다.
23 |
24 | 이후 해결방안 나오면 별도로 공지하겠습니다.
25 |
26 | ### Xcode 12.5 업뎃 후 빌드 실패 해결법 (React-Native 만)
27 |
28 | 갑자기 Xcode 업데이트 후 디버그로는 빌드가 안되는 문제가 있습니다.
29 | 원인은 Alamofire에서 나오는 부분인데 프로젝트 설정으로 해결할 수 있습니다.
30 |
31 | 해당 프로젝트 설정을 접근 후 Target에 프로젝트에서
32 | Build Settings 접근 후 Library Search Path를 검색하면 그 안에 Debug쪽에 있는 부분을 수정해야합니다.
33 |
34 |
35 |
36 | \$(inherited)를 제외한 2개를 삭제 후 저장하고 빌드하면 정상적으로 돌아갑니다.
37 |
38 | ## 사용 환경
39 |
40 | - CRA (create-react-app)
41 | - Next.js
42 | - React-Native 0.61 이상
43 | - React-Native-Web
44 |
45 | ## 시작하기
46 |
47 | ```bash
48 | $ npx @actbase/react-kakaosdk
49 | ```
50 |
51 | 웹이나 앱을 구분 한 뒤 알아서 wizard가 실행됩니다.
52 |
53 | ### iOS
54 |
55 | [공식문서 - 개발 프로젝트 설정](https://developers.kakao.com/docs/latest/ko/getting-started/sdk-ios-v1) 을 참고하여 `info.plist` 의 아래`NATIVE_APP_KEY` 문구를 잘 확인하시여 본인의 Kakao App Key로 변경해주세요.
56 |
57 | ```
58 | + KAKAO_APP_KEY
59 | + {NATIVE_APP_KEY}
60 | ```
61 |
62 | AppDelegate.m (++ 된 부분 추가)
63 |
64 | ```
65 | #import "WithKakaoSDK.h"
66 |
67 | - (BOOL)application:(UIApplication *)app
68 | openURL:(NSURL *)url
69 | options:(NSDictionary *)options
70 | {
71 | ...
72 |
73 | ++ NSString *appKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"KAKAO_APP_KEY"];
74 | [WithKakaoSDK initSDK: appKey];
75 | if ([WithKakaoSDK isKakaoTalkLoginUrl:url]) return [WithKakaoSDK handleOpenUrl:url];
76 | [[FBSDKApplicationDelegate sharedInstance] application:app
77 | openURL:url
78 | options:options];
79 |
80 | ...
81 |
82 | return NO;
83 | }
84 | ```
85 |
86 | 다른 수정사항은 npx를 이용해 kakaosdk모듈을 설치 진행 하고 kakaoApiKey 입력을 하시면 자동으로 추가가됩니다.
87 |
88 |
89 |
90 | ### Android
91 |
92 | ## 사용방법
93 |
94 | Project build.gradle 안에 다음과 같이 android sdk repository를 추가해주세요.
95 |
96 | ```
97 | allprojects {
98 | repositories {
99 |
100 | maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }
101 | }
102 | }
103 | ```
104 |
105 | ```js
106 | import KakaoSDK from '@actbase/react-kakaosdk';
107 |
108 | // 카카오 로그인 시 처리부문
109 | await KakaoSDK.init(NATIVE_APP_KEY);
110 | const tokens = await KakaoSDK.login();
111 | ```
112 |
113 | | 변수명 | 설명 |
114 | | ------------------------ | ---------------------------------- |
115 | | access_token | 카카오의 access_token |
116 | | refresh_token | 카카오의 refresh_token |
117 | | expires_in | 카카오의 accessToken 만료 남은 초 |
118 | | refresh_token_expires_in | 카카오의 refreshToken 만료 남은 초 |
119 | | scopes | 사용권한 |
120 |
121 | ```js
122 | import KakaoSDK from '@actbase/react-kakaosdk';
123 |
124 | // 카카오 로그아웃시 처리
125 | await KakaoSDK.logout();
126 |
127 | // 카카오 회원정보 가져오기
128 | const profile = await KakaoSDK.getProfile();
129 | ```
130 |
131 | # Contacts
132 |
133 | 해당 모듈은 액트베이스(유)에서 개발 및 관리를 진행하고 있습니다.
134 | 프로젝트 문의 혹은 제휴가 필요한 경우 project@actbase.io로 연락주세요.
135 |
136 | # Changes
137 | - 0.9.24
138 | - use_framework! 시 사용가능하도록 변경
139 | - 0.9.20
140 | - 카카오 최신 SDK 반영
141 | - 0.9.19
142 | - 카카오 iOS SDK 장애로 버전 fixed 처리
143 | - 0.9.18
144 | - 액트베이스(유) 오픈소스 기준에 맞춰서 수정
145 | - 0.9.17
146 | - 카카오 로그인 시 keyhash 없는경우 클립보드에 넣어주는 기능 장애 처리
147 | - 0.9.16
148 | - 적폐 제거 (원인제공자는 살림)
149 | - 0.9.14
150 | - 사용하지 않는 명령 제거
151 | - typing 업데이트
152 | - 0.9.12
153 | - 웹버전 scope없을때 장애 처리
154 | - 0.9.11
155 | - 안드로이드 간혈적 에러 처리
156 | - 0.9.10
157 | - 카카오 웹 버전에서도 openChannel 사용가능.
158 | - openChannelChat으로 즉시 채팅 열수있음.
159 | - 0.9.8
160 | - iOS Kakao Login 버그수정
161 | - 0.9.7
162 | - 카카오 채널 기능 버그수정
163 | - 0.9.6
164 | - 카카오 채널 기능 추가
165 | - 0.9.4
166 | - 안드로이드 간혈적 kakaoAccount null 일 경우 처리
167 | - 0.9.3
168 | - 첫 배포
169 |
--------------------------------------------------------------------------------
/RNAKakaoSDK.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNAKakaoSDK"
7 | s.version = package['version']
8 | s.summary = package['description']
9 |
10 | s.authors = { "Actbase LLC" => "project@actbase.io" }
11 | s.homepage = package['homepage']
12 | s.license = package['license']
13 |
14 | s.platform = :ios, "11.0"
15 | s.framework = 'MobileCoreServices'
16 | s.requires_arc = true
17 |
18 | s.source = { :git => package['repository']['url'] }
19 | s.source_files = "ios/*.{h,m,swift}"
20 |
21 | s.swift_version = '5.1'
22 |
23 | s.ios.deployment_target = '11.0'
24 | s.ios.framework = 'MobileCoreServices'
25 |
26 | s.dependency 'React'
27 | s.dependency 'React-Core'
28 | s.dependency 'KakaoSDKCommon', '2.11.1'
29 | s.dependency 'KakaoSDKAuth', '2.11.1'
30 | s.dependency 'KakaoSDKUser', '2.11.1'
31 | s.dependency 'KakaoSDKTalk', '2.11.1'
32 | s.dependency 'KakaoSDKShare', '2.11.1'
33 | s.dependency 'KakaoSDKTemplate', '2.11.1'
34 |
35 | s.xcconfig = {
36 | "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES" => "YES",
37 | "EMBEDDED_CONTENT_CONTAINS_SWIFT" => "YES",
38 | "BUILD_LIBRARY_FOR_DISTRIBUTION" => "YES"
39 | }
40 | s.pod_target_xcconfig = {
41 | "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES" => "YES",
42 | "EMBEDDED_CONTENT_CONTAINS_SWIFT" => "YES",
43 | "BUILD_LIBRARY_FOR_DISTRIBUTION" => "YES"
44 | }
45 | # s.pod_target_xcconfig = { 'SWIFT_OBJC_BRIDGING_HEADER' => '${PODS_TARGET_SRCROOT}/ios/RNAKakaoSDK-Bridging-Header.h' }
46 | end
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 | .settings
3 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.4.2'
9 | }
10 | }
11 |
12 |
13 | def safeExtGet(prop, fallback) {
14 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
15 | }
16 |
17 | apply plugin: 'com.android.library'
18 |
19 | android {
20 | compileSdkVersion safeExtGet('compileSdkVersion', 29)
21 |
22 | defaultConfig {
23 | minSdkVersion safeExtGet('minSdkVersion', 19)
24 | targetSdkVersion safeExtGet('targetSdkVersion', 29)
25 | versionCode 1
26 | versionName "1.0"
27 | }
28 |
29 | lintOptions {
30 | abortOnError false
31 | }
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 | }
37 |
38 | repositories {
39 | maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }
40 | }
41 |
42 | dependencies {
43 | implementation 'com.facebook.react:react-native:+'
44 | implementation "com.kakao.sdk:v2-user:2.6.0"
45 | implementation "com.kakao.sdk:v2-talk:2.6.0"
46 | implementation "com.kakao.sdk:v2-story:2.6.0"
47 | implementation "com.kakao.sdk:v2-link:2.6.0"
48 | implementation "com.kakao.sdk:v2-navi:2.6.0"
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/actbase/react-kakaosdk/a4f56c93db34a8b0e087e4c2107861b4ec277acd/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Nov 18 17:58:04 KST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/src/main/java/io/actbase/kakaosdk/RNAKakaoSDK.java:
--------------------------------------------------------------------------------
1 | package io.actbase.kakaosdk;
2 |
3 | import android.content.ClipData;
4 | import android.content.ClipboardManager;
5 | import android.content.Context;
6 | import android.content.pm.PackageInfo;
7 | import android.content.pm.PackageManager;
8 | import android.content.pm.Signature;
9 | import android.net.Uri;
10 | import android.util.Base64;
11 | import android.util.Log;
12 | import android.widget.Toast;
13 |
14 | import com.facebook.react.bridge.Arguments;
15 | import com.facebook.react.bridge.Promise;
16 | import com.facebook.react.bridge.ReactApplicationContext;
17 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
18 | import com.facebook.react.bridge.ReactMethod;
19 | import com.facebook.react.bridge.ReadableArray;
20 | import com.facebook.react.bridge.ReadableMap;
21 | import com.facebook.react.bridge.WritableArray;
22 | import com.facebook.react.bridge.WritableMap;
23 | import com.kakao.sdk.common.KakaoSdk;
24 | import com.kakao.sdk.common.model.AuthError;
25 | import com.kakao.sdk.common.util.KakaoCustomTabsClient;
26 | import com.kakao.sdk.talk.TalkApiClient;
27 | import com.kakao.sdk.user.UserApiClient;
28 | import com.kakao.sdk.user.model.Account;
29 |
30 | import java.security.MessageDigest;
31 | import java.security.NoSuchAlgorithmException;
32 | import java.text.SimpleDateFormat;
33 | import java.util.ArrayList;
34 | import java.util.Date;
35 | import java.util.List;
36 | import java.util.Map;
37 |
38 | public class RNAKakaoSDK extends ReactContextBaseJavaModule {
39 |
40 | private ReactApplicationContext context;
41 | private boolean isInit = false;
42 |
43 | public RNAKakaoSDK(ReactApplicationContext context) {
44 | super(context);
45 | this.context = context;
46 | }
47 |
48 | public String getKeyHash() {
49 | String keyHash = null;
50 | PackageInfo packageInfo = null;
51 | try {
52 | packageInfo = context.getPackageManager()
53 | .getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
54 | } catch (PackageManager.NameNotFoundException e) {
55 | e.printStackTrace();
56 | }
57 |
58 | for (Signature signature : packageInfo.signatures) {
59 | try {
60 | MessageDigest md = MessageDigest.getInstance("SHA");
61 | md.update(signature.toByteArray());
62 | keyHash = Base64.encodeToString(md.digest(), Base64.DEFAULT);
63 | } catch (NoSuchAlgorithmException e) {
64 | Log.e("KeyHash", "Unable to get MessageDigest. signature=" + signature, e);
65 | }
66 | }
67 |
68 | return keyHash;
69 | }
70 |
71 | @Override
72 | public String getName() {
73 | return "RNAKakaoSDK";
74 | }
75 |
76 | private String format(Date date) {
77 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
78 | return sdf.format(date);
79 | }
80 |
81 | private boolean toBool(Boolean b) {
82 | return b == null ? false : b;
83 | }
84 |
85 | private void loginWithKakaoAccount(Promise promise) {
86 | // change class into user api at kakao sdk 2.4
87 | UserApiClient.getInstance().loginWithKakaoAccount(context, (token, error) -> {
88 | try {
89 | if (error != null) {
90 | throw error;
91 | }
92 | WritableMap map = Arguments.createMap();
93 | map.putString("accessToken", token.getAccessToken());
94 | map.putString("refreshToken", token.getRefreshToken());
95 | map.putString("accessTokenExpiresAt", format(token.getAccessTokenExpiresAt()));
96 | map.putString("refreshTokenExpiresAt", format(token.getRefreshTokenExpiresAt()));
97 |
98 | WritableArray scopes = Arguments.createArray();
99 | if (token.getScopes() != null) {
100 | for (String scope : token.getScopes()) {
101 | scopes.pushString(scope);
102 | }
103 | }
104 | map.putArray("scopes", scopes);
105 |
106 | promise.resolve(map);
107 | } catch (Throwable ex) {
108 | if (ex instanceof AuthError) {
109 | AuthError authError = (AuthError) ex;
110 | if (authError.getStatusCode() == 401) {
111 | // invalid android_key_hash or ios_bundle_id or web_site_url.
112 | try {
113 | ClipboardManager clipboard = (ClipboardManager) context
114 | .getSystemService(Context.CLIPBOARD_SERVICE);
115 | ClipData clip = ClipData.newPlainText("Android Key Hash", getKeyHash());
116 | clipboard.setPrimaryClip(clip);
117 | Toast.makeText(context, "Copy to Keyhash (clipboard)", Toast.LENGTH_SHORT).show();
118 | } catch (Exception ex2) {
119 | ex2.printStackTrace();
120 | }
121 | }
122 | promise.reject(String.valueOf(authError.getStatusCode()), authError.getLocalizedMessage(),
123 | ex);
124 | } else {
125 | ex.printStackTrace();
126 | promise.reject(ex);
127 | }
128 | }
129 | return null;
130 | });
131 | }
132 |
133 | @ReactMethod
134 | public void init(String appKey) {
135 | KakaoSdk.init(context, appKey);
136 | isInit = true;
137 | }
138 |
139 | @ReactMethod
140 | public void isInitialized(final Promise promise) {
141 | promise.resolve(isInit);
142 | }
143 |
144 | @ReactMethod
145 | public void login(final Promise promise) {
146 | if (!UserApiClient.getInstance().isKakaoTalkLoginAvailable(context)) {
147 | loginWithKakaoAccount(promise);
148 | return;
149 | }
150 |
151 | UserApiClient.getInstance().loginWithKakaoTalk(context.getCurrentActivity(), (token, error) -> {
152 | try {
153 | if (error != null) {
154 | throw error;
155 | }
156 |
157 | WritableMap map = Arguments.createMap();
158 | map.putString("accessToken", token.getAccessToken());
159 | map.putString("refreshToken", token.getRefreshToken());
160 | map.putString("accessTokenExpiresAt", format(token.getAccessTokenExpiresAt()));
161 | map.putString("refreshTokenExpiresAt", format(token.getRefreshTokenExpiresAt()));
162 |
163 | WritableArray scopes = Arguments.createArray();
164 |
165 | List givenScopes = token.getScopes();
166 |
167 | if (givenScopes != null) {
168 | for (String scope : givenScopes) {
169 | scopes.pushString(scope);
170 | }
171 | }
172 |
173 | map.putArray("scopes", scopes);
174 |
175 | promise.resolve(map);
176 | } catch (Throwable ex) {
177 | if (ex instanceof AuthError) {
178 | AuthError authError = (AuthError) ex;
179 | if (authError.getStatusCode() == 401) {
180 | // invalid android_key_hash or ios_bundle_id or web_site_url.
181 | try {
182 | ClipboardManager clipboard = (ClipboardManager) context
183 | .getSystemService(Context.CLIPBOARD_SERVICE);
184 | ClipData clip = ClipData.newPlainText("Android Key Hash", getKeyHash());
185 | clipboard.setPrimaryClip(clip);
186 | Toast.makeText(context, "Copy to Keyhash (clipboard)", Toast.LENGTH_SHORT).show();
187 | } catch (Exception ex2) {
188 | ex2.printStackTrace();
189 | }
190 | promise.reject(String.valueOf(authError.getStatusCode()), getKeyHash(), ex);
191 | } else if (authError.getStatusCode() == 302) {
192 | // KakaoTalk is installed but not connected to Kakao account.
193 | loginWithKakaoAccount(promise);
194 | } else {
195 | promise
196 | .reject(String.valueOf(authError.getStatusCode()), authError.getLocalizedMessage(),
197 | ex);
198 | }
199 | } else {
200 | ex.printStackTrace();
201 | promise.reject(ex);
202 | }
203 | }
204 | return null;
205 | });
206 | }
207 |
208 | @ReactMethod
209 | public void manualLogin(final Promise promise) {
210 | loginWithKakaoAccount(promise);
211 | }
212 |
213 | @ReactMethod
214 | public void loginWithNewScopes(ReadableArray permissions, final Promise promise) {
215 | List perms = new ArrayList();
216 | for (int i = 0; i < permissions.size(); i++) {
217 | perms.add(permissions.getString(i));
218 | }
219 | // change class into user api at kakao sdk 2.4
220 | UserApiClient.getInstance().loginWithNewScopes(context, perms, (token, error) -> {
221 | try {
222 | if (error != null) {
223 | throw new Exception(error.getMessage());
224 | }
225 | WritableMap map = Arguments.createMap();
226 | map.putString("accessToken", token.getAccessToken());
227 | map.putString("refreshToken", token.getRefreshToken());
228 | map.putString("accessTokenExpiresAt", format(token.getAccessTokenExpiresAt()));
229 | map.putString("refreshTokenExpiresAt", format(token.getRefreshTokenExpiresAt()));
230 |
231 | WritableArray scopes = Arguments.createArray();
232 |
233 | List givenScopes = token.getScopes();
234 |
235 | if (givenScopes != null) {
236 | for (String scope : givenScopes) {
237 | scopes.pushString(scope);
238 | }
239 | }
240 |
241 | map.putArray("scopes", scopes);
242 |
243 | promise.resolve(map);
244 | } catch (Throwable ex) {
245 | promise.reject(ex);
246 | }
247 | return null;
248 | });
249 | }
250 |
251 | @ReactMethod
252 | public void logout(final Promise promise) {
253 | UserApiClient.getInstance().logout((error) -> {
254 | if (error != null) {
255 | promise.reject(error);
256 | } else {
257 | promise.resolve("SUCCESS");
258 | }
259 | return null;
260 | });
261 | }
262 |
263 | @ReactMethod
264 | public void unlink(final Promise promise) {
265 | UserApiClient.getInstance().unlink((error) -> {
266 | if (error != null) {
267 | promise.reject(error);
268 | } else {
269 | promise.resolve("SUCCESS");
270 | }
271 | return null;
272 | });
273 | }
274 |
275 | @ReactMethod
276 | public void getAccessToken(final Promise promise) {
277 | UserApiClient.getInstance().accessTokenInfo((tokenInfo, error) -> {
278 | try {
279 | if (error != null) {
280 | throw new Exception(error.getMessage());
281 | }
282 |
283 | WritableMap map = Arguments.createMap();
284 | map.putDouble("id", tokenInfo.getId());
285 | map.putDouble("expiresIn", tokenInfo.getExpiresIn());
286 | promise.resolve(map);
287 |
288 | } catch (Throwable ex) {
289 | promise.reject(ex);
290 | }
291 | return null;
292 | });
293 | }
294 |
295 | @ReactMethod
296 | public void getProfile(final Promise promise) {
297 | UserApiClient.getInstance().me((user, error) -> {
298 | try {
299 | if (error != null) {
300 | error.printStackTrace();
301 | throw new Exception(error.getMessage());
302 | }
303 |
304 | WritableMap map = Arguments.createMap();
305 | map.putDouble("id", user.getId());
306 | map.putString("connectedAt", format(user.getConnectedAt()));
307 |
308 | {
309 | WritableMap kakaoAccount = Arguments.createMap();
310 | Account origin = user.getKakaoAccount();
311 | if (origin != null) {
312 | if (origin.getEmailNeedsAgreement() != null) {
313 | if (origin.getEmailNeedsAgreement() == Boolean.FALSE) {
314 | kakaoAccount.putString("email", origin.getEmail());
315 | }
316 | kakaoAccount.putBoolean("emailNeedsAgreement",
317 | toBool(origin.getEmailNeedsAgreement()));
318 | kakaoAccount.putBoolean("isEmailValid", toBool(origin.isEmailValid()));
319 | kakaoAccount.putBoolean("isEmailVerified", toBool(origin.isEmailVerified()));
320 | }
321 |
322 | if (origin.getBirthdayNeedsAgreement() != null) {
323 | if (origin.getBirthdayNeedsAgreement() == Boolean.FALSE) {
324 | kakaoAccount.putString("birthday", origin.getBirthday());
325 | }
326 | kakaoAccount
327 | .putBoolean("birthdayNeedsAgreement", toBool(origin.getBirthdayNeedsAgreement()));
328 | }
329 |
330 | if (origin.getBirthyearNeedsAgreement() != null) {
331 | if (origin.getBirthyearNeedsAgreement() == Boolean.FALSE) {
332 | kakaoAccount.putString("birthyear", origin.getBirthyear());
333 | }
334 | kakaoAccount
335 | .putBoolean("birthyearNeedsAgreement",
336 | toBool(origin.getBirthyearNeedsAgreement()));
337 | }
338 |
339 | if (origin.getGenderNeedsAgreement() != null) {
340 | if (origin.getGenderNeedsAgreement() == Boolean.FALSE && origin.getGender() != null) {
341 | kakaoAccount.putString("gender", origin.getGender().toString());
342 | }
343 | kakaoAccount
344 | .putBoolean("genderNeedsAgreement", toBool(origin.getGenderNeedsAgreement()));
345 | }
346 |
347 | if (origin.getCiNeedsAgreement() != null) {
348 | if (origin.getCiNeedsAgreement() == Boolean.FALSE && origin.getCi() != null) {
349 | kakaoAccount.putString("ci", origin.getCi().toString());
350 | }
351 | kakaoAccount.putString("ciAuthenticatedAt", format(origin.getCiAuthenticatedAt()));
352 | kakaoAccount.putBoolean("ciNeedsAgreement", toBool(origin.getCiNeedsAgreement()));
353 | }
354 |
355 | if (origin.getLegalBirthDateNeedsAgreement() != null) {
356 | if (origin.getLegalBirthDateNeedsAgreement() == Boolean.FALSE) {
357 | kakaoAccount.putString("legalBirthDate", origin.getLegalBirthDate());
358 | }
359 | kakaoAccount.putBoolean("legalBirthDateNeedsAgreement",
360 | toBool(origin.getLegalBirthDateNeedsAgreement()));
361 | }
362 |
363 | if (origin.getLegalGenderNeedsAgreement() != null) {
364 | if (origin.getLegalGenderNeedsAgreement() == Boolean.FALSE
365 | && origin.getLegalGender() != null) {
366 | kakaoAccount.putString("legalGender", origin.getLegalGender().toString());
367 | }
368 | kakaoAccount
369 | .putBoolean("legalGenderNeedsAgreement",
370 | toBool(origin.getLegalGenderNeedsAgreement()));
371 | }
372 |
373 | if (origin.getLegalNameNeedsAgreement() != null) {
374 | if (origin.getLegalNameNeedsAgreement() == Boolean.FALSE) {
375 | kakaoAccount.putString("legalName", origin.getLegalName());
376 | }
377 | kakaoAccount
378 | .putBoolean("legalNameNeedsAgreement",
379 | toBool(origin.getLegalNameNeedsAgreement()));
380 | }
381 |
382 | if (origin.getAgeRangeNeedsAgreement() != null) {
383 | if (origin.getAgeRangeNeedsAgreement() == Boolean.FALSE
384 | && origin.getAgeRange() != null) {
385 | kakaoAccount.putString("ageRange", origin.getAgeRange().toString());
386 | }
387 | kakaoAccount
388 | .putBoolean("ageRangeNeedsAgreement", toBool(origin.getAgeRangeNeedsAgreement()));
389 | }
390 |
391 | if (origin.getPhoneNumberNeedsAgreement() != null) {
392 | if (origin.getPhoneNumberNeedsAgreement() == Boolean.FALSE) {
393 | kakaoAccount.putString("phoneNumber", origin.getPhoneNumber());
394 | }
395 | kakaoAccount
396 | .putBoolean("phoneNumberNeedsAgreement",
397 | toBool(origin.getPhoneNumberNeedsAgreement()));
398 | }
399 |
400 | if (origin.getProfileNeedsAgreement() != null) {
401 | if (origin.getProfileNeedsAgreement() == Boolean.FALSE) {
402 | WritableMap profile = Arguments.createMap();
403 | profile.putString("nickname", origin.getProfile().getNickname());
404 | profile.putString("profileImageUrl", origin.getProfile().getProfileImageUrl());
405 | profile.putString("thumbnailImageUrl", origin.getProfile().getThumbnailImageUrl());
406 | kakaoAccount.putMap("profile", profile);
407 | }
408 | kakaoAccount
409 | .putBoolean("profileNeedsAgreement", toBool(origin.getProfileNeedsAgreement()));
410 | }
411 | }
412 | map.putMap("kakaoAccount", kakaoAccount);
413 | }
414 |
415 | {
416 | WritableMap properties = Arguments.createMap();
417 | Map origin = user.getProperties();
418 | if (origin != null) {
419 | for (String key : origin.keySet()) {
420 | if (origin.get(key) != null) {
421 | properties.putString(key, origin.get(key));
422 | }
423 | }
424 | }
425 | map.putMap("properties", properties);
426 | }
427 | promise.resolve(map);
428 |
429 | } catch (Throwable ex) {
430 | promise.reject(ex);
431 | }
432 | return null;
433 | });
434 | }
435 |
436 | @ReactMethod
437 | public void openChannel(String url, final Promise promise) {
438 | Uri talkUrl = TalkApiClient.getInstance().addChannelUrl(url);
439 | KakaoCustomTabsClient.INSTANCE.openWithDefault(
440 | getReactApplicationContext().getCurrentActivity(), talkUrl);
441 | promise.resolve(true);
442 | }
443 |
444 | @ReactMethod
445 | public void openChannelChat(String url, final Promise promise) {
446 | Uri talkUrl = TalkApiClient.getInstance().channelChatUrl(url);
447 | KakaoCustomTabsClient.INSTANCE.openWithDefault(
448 | getReactApplicationContext().getCurrentActivity(), talkUrl);
449 | promise.resolve(true);
450 | }
451 |
452 | public String getKeyHash(final Context context) {
453 | PackageInfo packageInfo = null;
454 | try {
455 | packageInfo = context.getPackageManager()
456 | .getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
457 | } catch (PackageManager.NameNotFoundException e) {
458 | e.printStackTrace();
459 | }
460 |
461 | if (packageInfo == null) {
462 | return null;
463 | }
464 | for (Signature signature : packageInfo.signatures) {
465 | try {
466 | MessageDigest md = MessageDigest.getInstance("SHA");
467 | md.update(signature.toByteArray());
468 | return Base64.encodeToString(md.digest(), Base64.NO_WRAP);
469 | } catch (NoSuchAlgorithmException e) {
470 | e.printStackTrace();
471 | }
472 | }
473 | return null;
474 | }
475 |
476 | }
477 |
478 |
--------------------------------------------------------------------------------
/android/src/main/java/io/actbase/kakaosdk/RNAKakaoSDKPackage.java:
--------------------------------------------------------------------------------
1 | package io.actbase.kakaosdk;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.bridge.NativeModule;
9 | import com.facebook.react.bridge.ReactApplicationContext;
10 | import com.facebook.react.uimanager.ViewManager;
11 | import com.facebook.react.bridge.JavaScriptModule;
12 |
13 | public class RNAKakaoSDKPackage implements ReactPackage {
14 |
15 | @Override
16 | public List createNativeModules(ReactApplicationContext reactContext) {
17 | return Arrays.asList(
18 | new RNAKakaoSDK(reactContext)
19 | );
20 | }
21 |
22 | // Deprecated from RN 0.47
23 | public List> createJSModules() {
24 | return Collections.emptyList();
25 | }
26 |
27 | @Override
28 | public List createViewManagers(ReactApplicationContext reactContext) {
29 | return Collections.emptyList();
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/assets/xcode_0501.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/actbase/react-kakaosdk/a4f56c93db34a8b0e087e4c2107861b4ec277acd/assets/xcode_0501.png
--------------------------------------------------------------------------------
/ios/RNAKakaoSDK-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // ARNKakaoLogin-Bridging-Header.h
3 | // ARNKakaoLogin
4 | //
5 | // Created by Suhan Moon on 2020/08/27.
6 | //
7 |
8 | #import
9 | #import
10 | #import
11 |
12 |
--------------------------------------------------------------------------------
/ios/RNAKakaoSDK.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RNAKakaoSDK.swift
3 | // RNAKakaoSDK
4 | //
5 | // Created by Suhan Moon on 2020/08/27.
6 | //
7 |
8 | import Foundation
9 | import KakaoSDKCommon
10 | import KakaoSDKAuth
11 | import KakaoSDKUser
12 | import SafariServices
13 | import KakaoSDKTalk
14 |
15 | @objc(RNAKakaoSDK)
16 | public class RNAKakaoSDK: NSObject {
17 |
18 | fileprivate var inited = false;
19 |
20 | @objc
21 | static func requiresMainQueueSetup() -> Bool {
22 | return true
23 | }
24 |
25 | @objc(isKakaoTalkLoginUrl:)
26 | public static func isKakaoTalkLoginUrl(url:URL) -> Bool {
27 | return AuthApi.isKakaoTalkLoginUrl(url)
28 | }
29 |
30 | @objc(handleOpenUrl:)
31 | public static func handleOpenUrl(url:URL) -> Bool {
32 | return AuthController.handleOpenUrl(url: url)
33 | }
34 |
35 | func objectToDic(_ value: T) throws -> Any where T: Encodable {
36 | let json = try JSONEncoder().encode(value)
37 | let dict = try JSONSerialization.jsonObject(with: json, options: .allowFragments);
38 | return dict;
39 | }
40 |
41 | @objc(init:)
42 | func sdkinit(_ appKey: String) -> Void {
43 | KakaoSDK.initSDK(appKey: appKey)
44 | inited = true
45 | }
46 |
47 | @objc(isInitialized:rejecter:)
48 | func isInitialized(_ resolve: @escaping RCTPromiseResolveBlock,
49 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
50 | resolve(inited);
51 | }
52 |
53 | @objc(login:rejecter:)
54 | func login(_ resolve: @escaping RCTPromiseResolveBlock,
55 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
56 |
57 | DispatchQueue.main.async {
58 | let dateFormatter = DateFormatter()
59 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
60 |
61 | if (UserApi.isKakaoTalkLoginAvailable()) {
62 | UserApi.shared.loginWithKakaoTalk {(oauthToken, error) in
63 | do {
64 | if let error = error {
65 | throw error;
66 | }
67 | resolve([
68 | "accessToken": oauthToken!.accessToken,
69 | "refreshToken": oauthToken!.refreshToken,
70 | "accessTokenExpiresAt": dateFormatter.string(from: oauthToken!.expiredAt),
71 | "refreshTokenExpiresAt": dateFormatter.string(from: oauthToken!.refreshTokenExpiredAt),
72 | "scopes": oauthToken?.scopes,
73 | ])
74 | } catch let e {
75 | print(e);
76 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
77 | }
78 | }
79 | }
80 | else {
81 | UserApi.shared.loginWithKakaoAccount {(oauthToken, error) in
82 | do {
83 | if let error = error {
84 | throw error;
85 | }
86 |
87 | resolve([
88 | "accessToken": oauthToken!.accessToken,
89 | "refreshToken": oauthToken!.refreshToken,
90 | "accessTokenExpiresAt": dateFormatter.string(from: oauthToken!.expiredAt),
91 | "refreshTokenExpiresAt": dateFormatter.string(from: oauthToken!.refreshTokenExpiredAt),
92 | "scopes": oauthToken?.scopes,
93 | ])
94 | } catch let e {
95 | print(e);
96 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
97 | }
98 | }
99 | }
100 | }
101 | }
102 |
103 | @objc(manualLogin:rejecter:)
104 | func manualLogin(_ resolve: @escaping RCTPromiseResolveBlock,
105 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
106 |
107 | DispatchQueue.main.async {
108 | let dateFormatter = DateFormatter()
109 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
110 | UserApi.shared.loginWithKakaoAccount {(oauthToken, error) in
111 | do {
112 | if let error = error {
113 | throw error;
114 | }
115 |
116 | resolve([
117 | "accessToken": oauthToken!.accessToken,
118 | "refreshToken": oauthToken!.refreshToken,
119 | "accessTokenExpiresAt": dateFormatter.string(from: oauthToken!.expiredAt),
120 | "refreshTokenExpiresAt": dateFormatter.string(from: oauthToken!.refreshTokenExpiredAt),
121 | "scopes": oauthToken?.scopes,
122 | ])
123 | } catch let e {
124 | print(e);
125 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
126 | }
127 | }
128 | }
129 | }
130 |
131 | @objc(loginWithNewScopes:resolver:rejecter:)
132 | func loginWithNewScopes(_ scopedata: NSArray,
133 | resolver resolve: @escaping RCTPromiseResolveBlock,
134 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
135 | DispatchQueue.main.async {
136 | let dateFormatter = DateFormatter()
137 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
138 |
139 | let scopes = scopedata as? [String]
140 | UserApi.shared.loginWithKakaoAccount(scopes: scopes!) { (oauthToken, error) in
141 | if let error = error {
142 | reject("RCTKakaoSDK", error.asAFError?.errorDescription, nil)
143 | return
144 | }
145 | else {
146 | do {
147 | if let error = error {
148 | throw error;
149 | }
150 |
151 | resolve([
152 | "accessToken": oauthToken!.accessToken,
153 | "refreshToken": oauthToken!.refreshToken,
154 | "accessTokenExpiresAt": dateFormatter.string(from: oauthToken!.expiredAt),
155 | "refreshTokenExpiresAt": dateFormatter.string(from: oauthToken!.refreshTokenExpiredAt),
156 | "scopes": oauthToken?.scopes!,
157 | ])
158 |
159 | } catch let e {
160 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
161 | }
162 | }
163 | }
164 | }
165 | }
166 |
167 | @objc(logout:rejecter:)
168 | func logout(_ resolve: @escaping RCTPromiseResolveBlock,
169 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
170 |
171 | DispatchQueue.main.async {
172 | UserApi.shared.logout {(error) in
173 | do {
174 | if let error = error {
175 | throw error;
176 | }
177 | resolve("SUCCESS")
178 | } catch let e {
179 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
180 | }
181 | }
182 | }
183 | }
184 |
185 | @objc(unlink:rejecter:)
186 | func unlink(_ resolve: @escaping RCTPromiseResolveBlock,
187 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
188 |
189 | DispatchQueue.main.async {
190 | UserApi.shared.unlink {(error) in
191 | do {
192 | if let error = error {
193 | throw error;
194 | }
195 | resolve("SUCCESS")
196 | } catch let e {
197 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
198 | }
199 | }
200 | }
201 | }
202 |
203 | @objc(getAccessToken:rejecter:)
204 | func getAccessToken(_ resolve: @escaping RCTPromiseResolveBlock,
205 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
206 |
207 | DispatchQueue.main.async {
208 | UserApi.shared.accessTokenInfo {(accessTokenInfo, error) in
209 | do {
210 | if let error = error {
211 | throw error;
212 | }
213 | resolve([
214 | "id": accessTokenInfo?.id,
215 | "expiresIn": accessTokenInfo?.expiresIn
216 | ])
217 | } catch let e {
218 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
219 | }
220 | }
221 | }
222 | }
223 |
224 | @objc(getProfile:rejecter:)
225 | func getProfile(_ resolve: @escaping RCTPromiseResolveBlock,
226 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
227 |
228 | DispatchQueue.main.async {
229 | let dateFormatter = DateFormatter()
230 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
231 | UserApi.shared.me() {(user, error) in
232 | do {
233 | if let error = error {
234 | throw error;
235 | }
236 |
237 | var map: [String: Any] = [
238 | "id": user!.id as Any,
239 | "connectedAt": dateFormatter.string(from: user!.connectedAt!)
240 | ];
241 |
242 | var kakaoAccount: [String: Any] = [:]
243 | let origin: Account = user!.kakaoAccount!
244 |
245 | if (origin.emailNeedsAgreement != nil) {
246 | if (origin.emailNeedsAgreement == false) {
247 | kakaoAccount.updateValue(origin.email ?? "", forKey: "email")
248 | }
249 | kakaoAccount.updateValue(origin.emailNeedsAgreement ?? false, forKey: "emailNeedsAgreement")
250 | kakaoAccount.updateValue(origin.isEmailValid ?? false, forKey: "isEmailValid")
251 | kakaoAccount.updateValue(origin.isEmailVerified ?? false, forKey: "isEmailVerified")
252 | }
253 |
254 | if (origin.birthdayNeedsAgreement != nil) {
255 | if (origin.birthdayNeedsAgreement == false) {
256 | kakaoAccount.updateValue(origin.birthday ?? "", forKey: "birthday")
257 | }
258 | kakaoAccount.updateValue(origin.birthdayNeedsAgreement ?? false, forKey: "birthdayNeedsAgreement")
259 | }
260 |
261 | if (origin.birthyearNeedsAgreement != nil) {
262 | if (origin.birthyearNeedsAgreement == false) {
263 | kakaoAccount.updateValue(origin.birthyear, forKey: "birthyear")
264 | }
265 | kakaoAccount.updateValue(origin.birthyearNeedsAgreement, forKey: "birthyearNeedsAgreement")
266 | }
267 |
268 | if (origin.genderNeedsAgreement != nil) {
269 | if (origin.genderNeedsAgreement == false) {
270 | kakaoAccount.updateValue(origin.gender?.rawValue, forKey: "gender")
271 | }
272 | kakaoAccount.updateValue(origin.genderNeedsAgreement, forKey: "genderNeedsAgreement")
273 | }
274 |
275 | if (origin.ciNeedsAgreement != nil) {
276 | if (origin.ciNeedsAgreement == false) {
277 | kakaoAccount.updateValue(origin.ci, forKey: "ci")
278 | }
279 | kakaoAccount.updateValue(dateFormatter.string(from: origin.ciAuthenticatedAt!), forKey: "ciAuthenticatedAt")
280 | kakaoAccount.updateValue(origin.ciNeedsAgreement, forKey: "ciNeedsAgreement")
281 | }
282 |
283 | if (origin.legalBirthDateNeedsAgreement != nil) {
284 | if (origin.legalBirthDateNeedsAgreement == false) {
285 | kakaoAccount.updateValue(origin.legalBirthDate, forKey: "legalBirthDate")
286 | }
287 | kakaoAccount.updateValue(origin.legalBirthDateNeedsAgreement, forKey: "legalBirthDateNeedsAgreement")
288 | }
289 |
290 | if (origin.legalGenderNeedsAgreement != nil) {
291 | if (origin.legalGenderNeedsAgreement == false) {
292 | kakaoAccount.updateValue(origin.legalGender?.rawValue, forKey: "legalGender")
293 | }
294 | kakaoAccount.updateValue(origin.legalGenderNeedsAgreement, forKey: "legalGenderNeedsAgreement")
295 | }
296 |
297 | if (origin.legalNameNeedsAgreement != nil) {
298 | if (origin.legalNameNeedsAgreement == false) {
299 | kakaoAccount.updateValue(origin.legalName, forKey: "legalName")
300 | }
301 | kakaoAccount.updateValue(origin.legalNameNeedsAgreement, forKey: "legalNameNeedsAgreement")
302 | }
303 |
304 | if (origin.ageRangeNeedsAgreement != nil) {
305 | if (origin.ageRangeNeedsAgreement == false) {
306 | kakaoAccount.updateValue(origin.ageRange?.rawValue, forKey: "ageRange")
307 | }
308 | kakaoAccount.updateValue(origin.ageRangeNeedsAgreement, forKey: "ageRangeNeedsAgreement")
309 | }
310 |
311 | if (origin.phoneNumberNeedsAgreement != nil) {
312 | if (origin.phoneNumberNeedsAgreement == false) {
313 | kakaoAccount.updateValue(origin.phoneNumber, forKey: "phoneNumber")
314 | }
315 | kakaoAccount.updateValue(origin.phoneNumberNeedsAgreement, forKey: "phoneNumberNeedsAgreement")
316 | }
317 |
318 | if (origin.profileNeedsAgreement != nil) {
319 | if (origin.profileNeedsAgreement == false) {
320 | kakaoAccount.updateValue([
321 | "nickname": origin.profile?.nickname,
322 | "profileImageUrl": origin.profile?.profileImageUrl,
323 | "thumbnailImageUrl": origin.profile?.thumbnailImageUrl,
324 | ], forKey: "profile")
325 | }
326 | kakaoAccount.updateValue(origin.profileNeedsAgreement, forKey: "profileNeedsAgreement")
327 | }
328 | map.updateValue(kakaoAccount, forKey: "kakaoAccount")
329 | map.updateValue(user?.properties, forKey: "properties");
330 | resolve(map)
331 | } catch let e {
332 | reject("actbase_kakao_sdk", e.localizedDescription, nil)
333 | }
334 | }
335 | }
336 | }
337 |
338 | @objc(openChannel:resolver:rejecter:)
339 | func openChannel(_ channelId: String,
340 | resolver resolve: @escaping RCTPromiseResolveBlock,
341 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
342 | var safariViewController : SFSafariViewController = SFSafariViewController(url: TalkApi.shared.makeUrlForAddChannel(channelPublicId:channelId)!)
343 | guard (safariViewController != nil) else { return }
344 |
345 | DispatchQueue.main.async {
346 | safariViewController.modalTransitionStyle = .crossDissolve
347 | safariViewController.modalPresentationStyle = .overCurrentContext
348 | UIApplication.shared.keyWindow?.rootViewController?.present(safariViewController, animated: true) {
349 | print("카카오톡 채널 추가 연결 페이지 실행 성공")
350 | }
351 | resolve(true)
352 | }
353 | }
354 |
355 | @objc(openChannelChat:resolver:rejecter:)
356 | func openChannelChat(_ channelId: String,
357 | resolver resolve: @escaping RCTPromiseResolveBlock,
358 | rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
359 | var safariViewController : SFSafariViewController = SFSafariViewController(url: TalkApi.shared.makeUrlForChannelChat(channelPublicId:channelId)!)
360 | guard (safariViewController != nil) else { return }
361 |
362 | DispatchQueue.main.async {
363 | safariViewController.modalTransitionStyle = .crossDissolve
364 | safariViewController.modalPresentationStyle = .overCurrentContext
365 | UIApplication.shared.keyWindow?.rootViewController?.present(safariViewController, animated: true) {
366 | print("Kakao Talk Channel chat 연결 페이지 실행 성공")
367 | }
368 | resolve(true)
369 | }
370 | }
371 |
372 | }
373 |
--------------------------------------------------------------------------------
/ios/RNAKakaoSDK.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXCopyFilesBuildPhase section */
10 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
11 | isa = PBXCopyFilesBuildPhase;
12 | buildActionMask = 2147483647;
13 | dstPath = "include/$(PRODUCT_NAME)";
14 | dstSubfolderSpec = 16;
15 | files = (
16 | );
17 | runOnlyForDeploymentPostprocessing = 0;
18 | };
19 | /* End PBXCopyFilesBuildPhase section */
20 |
21 | /* Begin PBXFrameworksBuildPhase section */
22 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
23 | isa = PBXFrameworksBuildPhase;
24 | buildActionMask = 2147483647;
25 | files = (
26 | BA67EA73238522010094C010 /* KakaoOpenSDK.framework in Frameworks */,
27 | );
28 | runOnlyForDeploymentPostprocessing = 0;
29 | };
30 | /* End PBXFrameworksBuildPhase section */
31 |
32 |
33 | /* Begin PBXNativeTarget section */
34 | 58B511DA1A9E6C8500147676 /* RNCKakaoSDK */ = {
35 | isa = PBXNativeTarget;
36 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCKakaoSDK" */;
37 | buildPhases = (
38 | 58B511D71A9E6C8500147676 /* Sources */,
39 | 58B511D81A9E6C8500147676 /* Frameworks */,
40 | 58B511D91A9E6C8500147676 /* CopyFiles */,
41 | );
42 | buildRules = (
43 | );
44 | dependencies = (
45 | );
46 | name = RNCKakaoSDK;
47 | productName = RCTDataManager;
48 | productReference = 134814201AA4EA6300B7C361 /* libRNCcsKakaosdk.a */;
49 | productType = "com.apple.product-type.library.static";
50 | };
51 | /* End PBXNativeTarget section */
52 |
53 | /* Begin XCBuildConfiguration section */
54 | 58B511ED1A9E6C8500147676 /* Debug */ = {
55 | isa = XCBuildConfiguration;
56 | buildSettings = {
57 | ALWAYS_SEARCH_USER_PATHS = NO;
58 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
59 | CLANG_CXX_LIBRARY = "libc++";
60 | CLANG_ENABLE_MODULES = YES;
61 | CLANG_ENABLE_OBJC_ARC = YES;
62 | CLANG_WARN_BOOL_CONVERSION = YES;
63 | CLANG_WARN_CONSTANT_CONVERSION = YES;
64 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
65 | CLANG_WARN_EMPTY_BODY = YES;
66 | CLANG_WARN_ENUM_CONVERSION = YES;
67 | CLANG_WARN_INFINITE_RECURSION = YES;
68 | CLANG_WARN_INT_CONVERSION = YES;
69 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
70 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
71 | CLANG_WARN_UNREACHABLE_CODE = YES;
72 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
73 | COPY_PHASE_STRIP = NO;
74 | ENABLE_STRICT_OBJC_MSGSEND = YES;
75 | ENABLE_TESTABILITY = YES;
76 | GCC_C_LANGUAGE_STANDARD = gnu99;
77 | GCC_DYNAMIC_NO_PIC = NO;
78 | GCC_NO_COMMON_BLOCKS = YES;
79 | GCC_OPTIMIZATION_LEVEL = 0;
80 | GCC_PREPROCESSOR_DEFINITIONS = (
81 | "DEBUG=1",
82 | "$(inherited)",
83 | );
84 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
85 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
86 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
87 | GCC_WARN_UNDECLARED_SELECTOR = YES;
88 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
89 | GCC_WARN_UNUSED_FUNCTION = YES;
90 | GCC_WARN_UNUSED_VARIABLE = YES;
91 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
92 | MTL_ENABLE_DEBUG_INFO = YES;
93 | ONLY_ACTIVE_ARCH = YES;
94 | OTHER_LDFLAGS = "-all_load";
95 | SDKROOT = iphoneos;
96 | };
97 | name = Debug;
98 | };
99 | 58B511EE1A9E6C8500147676 /* Release */ = {
100 | isa = XCBuildConfiguration;
101 | buildSettings = {
102 | ALWAYS_SEARCH_USER_PATHS = NO;
103 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
104 | CLANG_CXX_LIBRARY = "libc++";
105 | CLANG_ENABLE_MODULES = YES;
106 | CLANG_ENABLE_OBJC_ARC = YES;
107 | CLANG_WARN_BOOL_CONVERSION = YES;
108 | CLANG_WARN_CONSTANT_CONVERSION = YES;
109 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
110 | CLANG_WARN_EMPTY_BODY = YES;
111 | CLANG_WARN_ENUM_CONVERSION = YES;
112 | CLANG_WARN_INFINITE_RECURSION = YES;
113 | CLANG_WARN_INT_CONVERSION = YES;
114 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
115 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
116 | CLANG_WARN_UNREACHABLE_CODE = YES;
117 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
118 | COPY_PHASE_STRIP = YES;
119 | ENABLE_NS_ASSERTIONS = NO;
120 | ENABLE_STRICT_OBJC_MSGSEND = YES;
121 | GCC_C_LANGUAGE_STANDARD = gnu99;
122 | GCC_NO_COMMON_BLOCKS = YES;
123 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
124 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
125 | GCC_WARN_UNDECLARED_SELECTOR = YES;
126 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
127 | GCC_WARN_UNUSED_FUNCTION = YES;
128 | GCC_WARN_UNUSED_VARIABLE = YES;
129 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
130 | MTL_ENABLE_DEBUG_INFO = NO;
131 | OTHER_LDFLAGS = "-all_load";
132 | SDKROOT = iphoneos;
133 | VALIDATE_PRODUCT = YES;
134 | };
135 | name = Release;
136 | };
137 | 58B511F01A9E6C8500147676 /* Debug */ = {
138 | isa = XCBuildConfiguration;
139 | buildSettings = {
140 | FRAMEWORK_SEARCH_PATHS = (
141 | "$(inherited)",
142 | "$(PROJECT_DIR)",
143 | );
144 | HEADER_SEARCH_PATHS = (
145 | "$(inherited)",
146 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
147 | "$(SRCROOT)/../../../React/**",
148 | "$(SRCROOT)/../../react-native/React/**",
149 | "$(SRCROOT)/../../../ios/Pods/Headers/Public/**",
150 | );
151 | LIBRARY_SEARCH_PATHS = "$(inherited)";
152 | OTHER_LDFLAGS = "-ObjC";
153 | PRODUCT_NAME = RNCKakaoSDK;
154 | SKIP_INSTALL = YES;
155 | };
156 | name = Debug;
157 | };
158 | 58B511F11A9E6C8500147676 /* Release */ = {
159 | isa = XCBuildConfiguration;
160 | buildSettings = {
161 | FRAMEWORK_SEARCH_PATHS = (
162 | "$(inherited)",
163 | "$(PROJECT_DIR)",
164 | );
165 | HEADER_SEARCH_PATHS = (
166 | "$(inherited)",
167 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
168 | "$(SRCROOT)/../../../React/**",
169 | "$(SRCROOT)/../../react-native/React/**",
170 | "$(SRCROOT)/../../../ios/Pods/Headers/Public/**",
171 | );
172 | LIBRARY_SEARCH_PATHS = "$(inherited)";
173 | OTHER_LDFLAGS = "-ObjC";
174 | PRODUCT_NAME = RNCKakaoSDK;
175 | SKIP_INSTALL = YES;
176 | };
177 | name = Release;
178 | };
179 | /* End XCBuildConfiguration section */
180 |
181 | /* Begin XCConfigurationList section */
182 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "ARNKakaoLogin" */ = {
183 | isa = XCConfigurationList;
184 | buildConfigurations = (
185 | 58B511ED1A9E6C8500147676 /* Debug */,
186 | 58B511EE1A9E6C8500147676 /* Release */,
187 | );
188 | defaultConfigurationIsVisible = 0;
189 | defaultConfigurationName = Release;
190 | };
191 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCKakaoSDK" */ = {
192 | isa = XCConfigurationList;
193 | buildConfigurations = (
194 | 58B511F01A9E6C8500147676 /* Debug */,
195 | 58B511F11A9E6C8500147676 /* Release */,
196 | );
197 | defaultConfigurationIsVisible = 0;
198 | defaultConfigurationName = Release;
199 | };
200 | /* End XCConfigurationList section */
201 | };
202 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
203 | }
204 |
--------------------------------------------------------------------------------
/ios/RNAKakaoSDKModule.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface RNAKakaoSDKModule: NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/RNAKakaoSDKModule.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import "RNAKakaoSDKModule.h"
3 |
4 | @interface RCT_EXTERN_MODULE(RNAKakaoSDK, NSObject)
5 | RCT_EXTERN_METHOD(initSDK:(NSString *)appKey);
6 | RCT_EXTERN_METHOD(init:(NSString *)appKey);
7 | RCT_EXTERN_METHOD(isInitialized:(RCTPromiseResolveBlock *)resolve rejecter:(RCTPromiseRejectBlock *)reject);
8 | RCT_EXTERN_METHOD(login:(RCTPromiseResolveBlock *)resolve rejecter:(RCTPromiseRejectBlock *)reject);
9 | RCT_EXTERN_METHOD(manualLogin:(RCTPromiseResolveBlock *)resolve rejecter:(RCTPromiseRejectBlock *)reject);
10 | RCT_EXTERN_METHOD(logout:(RCTPromiseResolveBlock *)resolve rejecter:(RCTPromiseRejectBlock *)reject);
11 | RCT_EXTERN_METHOD(unlink:(RCTPromiseResolveBlock *)resolve rejecter:(RCTPromiseRejectBlock *)reject);
12 | RCT_EXTERN_METHOD(getAccessToken:(RCTPromiseResolveBlock *)resolve rejecter:(RCTPromiseRejectBlock *)reject);
13 | RCT_EXTERN_METHOD(getProfile:(RCTPromiseResolveBlock *)resolve rejecter:(RCTPromiseRejectBlock *)reject);
14 | RCT_EXTERN_METHOD(loginWithNewScopes:(NSArray *)scopes
15 | resolver:(RCTPromiseResolveBlock *)resolve
16 | rejecter:(RCTPromiseRejectBlock *)reject);
17 | RCT_EXTERN_METHOD(openChannel:(NSString *)channelId
18 | resolver:(RCTPromiseResolveBlock *)resolve
19 | rejecter:(RCTPromiseRejectBlock *)reject);
20 | RCT_EXTERN_METHOD(openChannelChat:(NSString *)channelId
21 | resolver:(RCTPromiseResolveBlock *)resolve
22 | rejecter:(RCTPromiseRejectBlock *)reject);
23 | @end
24 |
--------------------------------------------------------------------------------
/ios/WithKakaoSDK.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface WithKakaoSDK : NSObject
6 |
7 | + (BOOL)isKakaoTalkLoginUrl:(NSURL *)url;
8 | + (BOOL)handleOpenUrl:(NSURL *)url;
9 |
10 | @end
11 |
12 | NS_ASSUME_NONNULL_END
13 |
14 |
--------------------------------------------------------------------------------
/ios/WithKakaoSDK.m:
--------------------------------------------------------------------------------
1 | #import "WithKakaoSDK.h"
2 | #import
3 |
4 | @implementation WithKakaoSDK
5 |
6 | + (BOOL)isKakaoTalkLoginUrl:(NSURL *)url {
7 | return [RNAKakaoSDK isKakaoTalkLoginUrl:url];
8 | }
9 |
10 | + (BOOL)handleOpenUrl:(NSURL *)url {
11 | return [RNAKakaoSDK handleOpenUrl:url];
12 |
13 | }
14 |
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@actbase/react-kakaosdk",
3 | "bin": {
4 | "install": "./bin/install"
5 | },
6 | "version": "0.9.26",
7 | "description": "KakaoSDK for React, ReactNative ",
8 | "main": "lib/index.js",
9 | "module": "lib/index.js",
10 | "typings": "lib/index.d.ts",
11 | "scripts": {
12 | "build:src": "rm -rf ./lib && tsc -p ./tsconfig.json && babel lib --out-dir lib",
13 | "build:bin": "rm -rf ./bin && babel src_bin --out-dir bin --copy-files",
14 | "build": "npm run build:src && npm run build:bin",
15 | "prepublish": "npm run build",
16 | "test": "echo \"Error: no test specified\" && exit 1"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/actbase/react-kakaosdk.git"
21 | },
22 | "keywords": [
23 | "react",
24 | "react-native",
25 | "reactnative",
26 | "kakaosdk",
27 | "react kakao",
28 | "react native kakao",
29 | "react-native kakao",
30 | "react kakao login",
31 | "react native kakao login",
32 | "react-native kakao login"
33 | ],
34 | "author": {
35 | "name": "Actbase LLC",
36 | "email": "project@actbase.io",
37 | "url": "https://da.actbase.io"
38 | },
39 | "license": "MIT",
40 | "bugs": {
41 | "url": "https://github.com/actbase/react-kakaosdk/issues"
42 | },
43 | "homepage": "https://github.com/actbase/react-kakaosdk#readme",
44 | "devDependencies": {
45 | "@babel/cli": "^7.6.2",
46 | "@babel/core": "^7.6.2",
47 | "@babel/node": "^7.7.7",
48 | "@babel/plugin-proposal-class-properties": "^7.7.4",
49 | "@babel/plugin-proposal-optional-chaining": "^7.9.0",
50 | "@babel/plugin-transform-modules-commonjs": "^7.7.5",
51 | "@babel/plugin-transform-react-jsx": "^7.8.3",
52 | "@babel/preset-env": "^7.7.7",
53 | "@babel/preset-react": "^7.7.4",
54 | "@types/lodash": "^4.14.149",
55 | "@types/react": "^16.9.31",
56 | "@types/react-dom": "^16.9.5",
57 | "@types/react-native": "0.61.12",
58 | "@typescript-eslint/eslint-plugin": "^4.29.3",
59 | "@typescript-eslint/parser": "^4.29.3",
60 | "babel-eslint": "^10.0.3",
61 | "babel-plugin-inline-import-data-uri": "^1.0.1",
62 | "commander": "^4.1.1",
63 | "esifycss": "^1.4.12",
64 | "eslint": "^7.32.0",
65 | "eslint-config-prettier": "^6.10.0",
66 | "eslint-plugin-babel": "^5.3.1",
67 | "eslint-plugin-import": "^2.20.1",
68 | "eslint-plugin-json": "^2.1.0",
69 | "eslint-plugin-jsx-a11y": "^6.2.3",
70 | "eslint-plugin-react": "^7.18.3",
71 | "eslint-plugin-react-hooks": "^4.2.0",
72 | "eslint-plugin-react-native": "^3.8.1",
73 | "inquirer": "^7.3.3",
74 | "jest": "^25.1.0",
75 | "prettier": "^1.19.1",
76 | "react": "*",
77 | "react-native": "*",
78 | "tslib": "^2.3.1",
79 | "typescript": "^4.4.2"
80 | },
81 | "dependencies": {
82 | "plist": "^3.0.2"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/app.native.ts:
--------------------------------------------------------------------------------
1 | import { NativeModules } from 'react-native';
2 | import { KakaoSDK, ProfileType } from './types';
3 |
4 | const { RNAKakaoSDK } = NativeModules;
5 |
6 | const dateToSeconds = (str: string): number => {
7 | const v = new Date(str?.replace(' ', 'T')).getTime();
8 | const n = new Date().getTime();
9 | return Math.floor((v - n) / 1000);
10 | };
11 |
12 | const valueToSnakeCase = (data: { [key: string]: any }) => {
13 | const args: { [key: string]: any } = {};
14 | for (const key of Object.keys(data)) {
15 | const nkey: string = key.replace(/(?:^|\.?)([A-Z])/g, (_x, y) => '_' + y.toLowerCase()).replace(/^_/, '');
16 | if (data[key] && typeof data[key] === 'object' && !data[key]?.push && Object.keys(data[key])?.length > 0) {
17 | args[nkey] = valueToSnakeCase(data[key]);
18 | } else {
19 | args[nkey] = data[key];
20 | }
21 | }
22 | return args;
23 | };
24 |
25 | export const init = async (appKey: string) => {
26 | RNAKakaoSDK.init(appKey);
27 | };
28 |
29 | export const isInitialized = async () => {
30 | return await RNAKakaoSDK.isInitialized();
31 | };
32 |
33 | export const login = async () => {
34 | const result = await RNAKakaoSDK.login();
35 | return {
36 | access_token: result?.accessToken,
37 | refresh_token: result?.refreshToken,
38 | scopes: result?.scopes,
39 | expires_in: dateToSeconds(result?.accessTokenExpiresAt),
40 | refresh_token_expires_in: dateToSeconds(result?.refreshTokenExpiresAt),
41 | };
42 | };
43 |
44 | export const manualLogin = async () => {
45 | const result = await RNAKakaoSDK.manualLogin();
46 | return {
47 | access_token: result?.accessToken,
48 | refresh_token: result?.refreshToken,
49 | scopes: result?.scopes,
50 | expires_in: dateToSeconds(result?.accessTokenExpiresAt),
51 | refresh_token_expires_in: dateToSeconds(result?.refreshTokenExpiresAt),
52 | };
53 | };
54 |
55 | export const loginWithNewScopes = async (scopes: string[]) => {
56 | const result = await RNAKakaoSDK.loginWithNewScopes(scopes);
57 | return {
58 | access_token: result?.accessToken,
59 | refresh_token: result?.refreshToken,
60 | scopes: result?.scopes,
61 | expires_in: dateToSeconds(result?.accessTokenExpiresAt),
62 | refresh_token_expires_in: dateToSeconds(result?.refreshTokenExpiresAt),
63 | };
64 | };
65 |
66 | export const logout = async () => {
67 | await RNAKakaoSDK.logout();
68 | };
69 |
70 | export const unlink = async () => {
71 | await RNAKakaoSDK.unlink();
72 | };
73 |
74 | export const getAccessToken = async () => {
75 | const result = await RNAKakaoSDK.getAccessToken();
76 | return {
77 | id: result.id,
78 | expires_in: dateToSeconds(result?.expiresIn),
79 | };
80 | };
81 |
82 | export const getProfile: () => Promise = async () => {
83 | const result = await RNAKakaoSDK.getProfile();
84 | return valueToSnakeCase(result) as ProfileType;
85 | };
86 |
87 | export const openChannel: (id: string) => Promise = async (id: string) =>{
88 | return await RNAKakaoSDK.openChannel(id);
89 | };
90 |
91 | export const openChannelChat: (id: string) => Promise = async (id: string) =>{
92 | return await RNAKakaoSDK.openChannelChat(id);
93 | };
94 |
95 | const app: KakaoSDK = {
96 | init,
97 | isInitialized,
98 | getAccessToken,
99 | getProfile,
100 | login,
101 | manualLogin,
102 | loginWithNewScopes,
103 | logout,
104 | unlink,
105 | openChannel,
106 | openChannelChat,
107 | };
108 |
109 | export default app;
110 |
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 | import {KakaoSDK, ProfileType} from './types';
2 |
3 | declare const global: Window & typeof globalThis;
4 |
5 | const getKakaoSDK = (): Promise => {
6 | return new Promise((resolve, reject) => {
7 | if (typeof window === 'undefined') reject({message: 'unsupported platform'});
8 | const kakaoSDK = global.Kakao;
9 | if (kakaoSDK) {
10 | resolve(kakaoSDK);
11 | return;
12 | }
13 |
14 | const jsapi = document.createElement('script');
15 | jsapi.type = 'text/javascript';
16 | jsapi.src = 'https://developers.kakao.com/sdk/js/kakao.min.js';
17 | const s = document.getElementsByTagName('script')[0];
18 | s?.parentNode?.insertBefore(jsapi, s);
19 | jsapi.onload = () => resolve(global.Kakao);
20 | jsapi.onabort = jsapi.onerror = reject;
21 | });
22 | };
23 |
24 | export const init = async (appKey: string) => {
25 | const kakao = await getKakaoSDK();
26 | if (!kakao.isInitialized()) kakao.init(appKey);
27 | };
28 |
29 | export const isInitialized = async () => {
30 | const Kakao = await getKakaoSDK();
31 | return Kakao.isInitialized();
32 | };
33 |
34 | export const login = async () => {
35 | const Kakao = await getKakaoSDK();
36 | const exec = () => new Promise((success, fail) => Kakao.Auth.login({scope: '', success, fail}));
37 | const output: any = (await exec()) || {};
38 | return {
39 | access_token: output?.access_token,
40 | expires_in: output?.expires_in,
41 | refresh_token: output?.refresh_token,
42 | refresh_token_expires_in: output?.refresh_token_expires_in,
43 | scopes: output?.scope?.split(' '),
44 | token_type: output.token_type,
45 | };
46 | };
47 |
48 | export const loginWithNewScopes = async (scopes: string[]) => {
49 | const Kakao = await getKakaoSDK();
50 | const scope = scopes?.join(',');
51 | const exec = () => new Promise((success, fail) => Kakao.Auth.login({scope, success, fail}));
52 | const output: any = (await exec()) || {};
53 | return {
54 | access_token: output?.access_token,
55 | expires_in: output?.expires_in,
56 | refresh_token: output?.refresh_token,
57 | refresh_token_expires_in: output?.refresh_token_expires_in,
58 | scopes: output?.scope?.split(' '),
59 | token_type: output.token_type,
60 | };
61 | };
62 |
63 | export const logout = async () => {
64 | const Kakao = await getKakaoSDK();
65 | if (!Kakao.Auth.getAccessToken()) throw {message: 'Not logged in.'};
66 |
67 | const exec = () => new Promise(success => Kakao.Auth.logout(success));
68 | await exec();
69 | };
70 |
71 | export const unlink = async () => {
72 | const Kakao = await getKakaoSDK();
73 | const url = '/v1/user/unlink';
74 | const exec = () => new Promise((success, fail) => Kakao.API.request({url, success, fail}));
75 | await exec();
76 | };
77 |
78 | export const getAccessToken = async () => {
79 | const Kakao = await getKakaoSDK();
80 | const output: any = Kakao.Auth.getAccessToken();
81 | return {
82 | id: output?.id,
83 | expires_in: output?.expires_in,
84 | };
85 | };
86 |
87 | export const getProfile = async (): Promise => {
88 | const Kakao = await getKakaoSDK();
89 | const url = '/v2/user/me';
90 | const exec = () => new Promise((success, fail) => Kakao.API.request({url, success, fail}));
91 | return await exec() as ProfileType;
92 | };
93 |
94 | export const openChannel: (id: string) => Promise = async (channelPublicId: string) => {
95 | const Kakao = await getKakaoSDK();
96 | return Kakao.Channel.addChannel({channelPublicId});
97 | }
98 |
99 | export const openChannelChat: (id: string) => Promise = async (channelPublicId: string) => {
100 | const Kakao = await getKakaoSDK();
101 | return Kakao.Channel.chat({channelPublicId});
102 | }
103 |
104 | const app: KakaoSDK = {
105 | init,
106 | isInitialized,
107 | getAccessToken,
108 | getProfile,
109 | login,
110 | loginWithNewScopes,
111 | logout,
112 | unlink,
113 | openChannel,
114 | openChannelChat,
115 | };
116 |
117 | export default app;
118 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import app from './app';
2 |
3 | const { name, version, homepage } = require('../package.json');
4 | console.log(`
5 | ___ ______________ ___ ________
6 | / _ |/ ___/_ __/ _ )/ _ | / __/ __/
7 | / __ / /__ / / / _ / __ |_\\ \\/ _/
8 | /_/ |_\\___/ /_/ /____/_/ |_/___/___/
9 |
10 | ${name} ${version} :: Actbase Opensources.
11 | Contact us -> project@actbase.io
12 | > https://actbase.io/opensource
13 | > ${homepage}
14 | `);
15 |
16 | export const KakaoSDK = app;
17 |
18 | export default KakaoSDK;
19 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface ProfileType {
2 | connected_at: string;
3 | id: number;
4 | kakao_account: {
5 | has_age_range?: boolean;
6 | age_range_needs_agreement?: boolean;
7 | age_range?:
8 | | '0~9'
9 | | '10~14'
10 | | '15~19'
11 | | '20~29'
12 | | '30~39'
13 | | '40~49'
14 | | '50~59'
15 | | '60~69'
16 | | '70~79'
17 | | '80~89'
18 | | '90~'
19 | | null;
20 |
21 | has_birthday?: boolean;
22 | birthday_needs_agreement?: boolean;
23 | birthday?: string;
24 | birthday_type?: string;
25 |
26 | has_birthyear?: boolean;
27 | birthyear_needs_agreement?: boolean;
28 | birthyear?: string;
29 |
30 | has_email?: boolean;
31 | email_needs_agreement?: boolean;
32 | email?: string;
33 |
34 | is_email_valid?: boolean;
35 | is_email_verified?: boolean;
36 |
37 | has_gender?: boolean;
38 | gender_needs_agreement?: boolean;
39 | gender?: 'male' | 'female' | null;
40 |
41 | profile_needs_agreement?: boolean;
42 | profile?: {
43 | is_default_image?: boolean;
44 | nickname?: string;
45 | profile_image_url?: string;
46 | thumbnail_image_url?: string;
47 | };
48 |
49 | ci_needs_agreement?: boolean;
50 | ci?: string;
51 | ci_authenticated_at?: string;
52 |
53 | legal_birth_date_needs_agreement?: boolean;
54 | legal_birth_date?: string;
55 |
56 | legal_gender_needs_agreement?: boolean;
57 | legal_gender?: 'male' | 'female' | null;
58 |
59 | legal_name_needs_agreement?: boolean;
60 | legal_name?: string;
61 |
62 | phone_number_needs_agreement?: boolean;
63 | phone_number?: string;
64 | };
65 | properties: {
66 | [key: string]: any;
67 | };
68 | }
69 |
70 | export interface AccessTokenType {
71 | access_token: string;
72 | expires_in: number;
73 | refresh_token: string;
74 | refresh_token_expires_in: number;
75 | scopes: string[];
76 | token_type?: string;
77 | }
78 |
79 | export interface AccessTokenInfo {
80 | id: number;
81 | expires_in: number;
82 | }
83 |
84 | export interface KakaoSDK {
85 | init: (appKey: string) => Promise;
86 | isInitialized: () => Promise;
87 | getAccessToken: () => Promise;
88 | getProfile: () => Promise;
89 | login: () => Promise;
90 | manualLogin?: () => Promise;
91 | loginWithNewScopes: (scopes: string[]) => Promise;
92 | logout: () => Promise;
93 | unlink: () => Promise;
94 | openChannel: (id: string) => Promise;
95 | openChannelChat: (id: string) => Promise;
96 | }
97 |
--------------------------------------------------------------------------------
/src_bin/install:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs');
4 | const { exec } = require('child_process');
5 | const readline = require('readline');
6 | const plist = require('plist');
7 |
8 | const readFile = path => {
9 | return new Promise((resolve, reject) => {
10 | fs.readFile(path, 'utf8', (err, data) => {
11 | if (err) {
12 | reject(err);
13 | } else {
14 | resolve(data);
15 | }
16 | });
17 | });
18 | };
19 |
20 | const writeFile = (path, content) => {
21 | return new Promise((resolve, reject) => {
22 | fs.writeFile(path, content, 'utf8', (err, data) => {
23 | if (err) {
24 | reject(err);
25 | } else {
26 | resolve(data);
27 | }
28 | });
29 | });
30 | };
31 |
32 | const readDir = path => {
33 | return new Promise((resolve, reject) => {
34 | fs.readdir(path, 'utf8', (err, data) => {
35 | if (err) {
36 | reject(err);
37 | } else {
38 | resolve(data);
39 | }
40 | });
41 | });
42 | };
43 |
44 | const execute = cmd => {
45 | return new Promise((resolve, reject) => {
46 | exec(cmd?.replace(/\n/g, ' '), (err, stdout, stderr) => {
47 | if (err) {
48 | console.log(err);
49 | reject(err);
50 | } else {
51 | resolve({ stdout, stderr });
52 | }
53 | });
54 | });
55 | };
56 |
57 | const question = query => {
58 | return new Promise(resolve => {
59 | const rl = readline.createInterface({
60 | input: process.stdin,
61 | output: process.stdout,
62 | });
63 |
64 | rl.question(query, data => {
65 | resolve(data);
66 | rl.close();
67 | });
68 | });
69 | };
70 |
71 | const start = async () => {
72 | let path = process.env.PWD;
73 | while (true) {
74 | if (fs.existsSync(path + '/package.json')) break;
75 | if (path.length < 10) {
76 | throw { message: 'not found package.json' };
77 | }
78 | path = path.substring(0, path.lastIndexOf('/'));
79 | }
80 |
81 | const pkg = JSON.parse(await readFile(path + '/package.json'));
82 | if (!pkg.dependencies['@actbase/react-kakaosdk']) {
83 | await exec(`npm i @actbase/react-kakaosdk`);
84 | // throw {
85 | // message: 'no installed @actbase/react-kakaosdk',
86 | // };
87 | }
88 |
89 | if (pkg.dependencies['react-native']) {
90 | console.log('React Native에 맞춰서 설정을 시작합니다.');
91 | const input = await question(
92 | '네이티브 앱 키를 입력하세요. (빈칸이면 기본설치 / TEST 키도 추가하시려면 ; 로 구분해서 넣어주세요) : ',
93 | );
94 | const keys = input ? input.split(';') : [];
95 | console.log('iOS 설치 시작');
96 |
97 | let name = (await readDir(path + '/ios'))?.filter(v => v.endsWith('.xcodeproj'))?.[0];
98 | name = name.substring(0, name.indexOf('.xcode'));
99 |
100 | {
101 | console.log('1. Pod Install...');
102 | let podfile = (await readFile(path + '/ios/PodFile')).split('\n');
103 | const ix = podfile.findIndex(x => x.indexOf('platform :ios') >= 0);
104 | if (ix >= 0) {
105 | podfile[ix] = podfile[ix].replace("'9.", "'11.").replace("'10.", "'11.");
106 | }
107 | await writeFile(path + '/ios/PodFile', podfile.join('\n'));
108 | await execute(`pod install --repo-update --project-directory=${path}/ios`);
109 | }
110 |
111 | {
112 | console.log('2. Info.plist 설정');
113 | const plists = [];
114 | let pbx = (await readFile(path + `/ios/${name}.xcodeproj/project.pbxproj`)).split('\n');
115 | const pbxoutput = pbx
116 | .map(v => {
117 | if (v.indexOf('INFOPLIST_FILE') >= 0) {
118 | plists.push(v.substring(v.indexOf('=') + 1).trim());
119 | }
120 | if (v.indexOf('IPHONEOS_DEPLOYMENT_TARGET') >= 0) {
121 | return v.replace('9.', '11.').replace('10.', '11.');
122 | }
123 | return v;
124 | })
125 | .join('\n');
126 | console.log(' > Deployment 버전 확인 후 낮을 시 올립니다.');
127 |
128 | await writeFile(path + `/ios/${name}.xcodeproj/project.pbxproj`, pbxoutput);
129 | for (const plistPath of plists?.filter((v, ix) => plists.indexOf(v) === ix)) {
130 | let r = plistPath.substring(0, plistPath.length - 1);
131 | r = r.replace(/\$\(SRCROOT\)/g, '');
132 | r = r.replace(/\"/g, '');
133 |
134 | const appPlist = plist.parse(await readFile(path + `/ios/${r}`));
135 | if (!appPlist.LSApplicationQueriesSchemes) {
136 | appPlist.LSApplicationQueriesSchemes = [];
137 | }
138 | if (!appPlist.LSApplicationQueriesSchemes.includes('kakaokompassauth')) {
139 | appPlist.LSApplicationQueriesSchemes.push('kakaokompassauth');
140 | }
141 | if (!appPlist.LSApplicationQueriesSchemes.includes('kakaolink')) {
142 | appPlist.LSApplicationQueriesSchemes.push('kakaolink');
143 | }
144 |
145 | if (!appPlist.CFBundleURLTypes) {
146 | appPlist.CFBundleURLTypes = [];
147 | }
148 |
149 | for (const key of keys) {
150 | if (!appPlist.LSApplicationQueriesSchemes.includes(key)) {
151 | appPlist.LSApplicationQueriesSchemes.push(key);
152 | }
153 | if (!appPlist.LSApplicationQueriesSchemes.includes(`kakao${key}`)) {
154 | appPlist.LSApplicationQueriesSchemes.push(`kakao${key}`);
155 | }
156 |
157 | if (!appPlist.CFBundleURLTypes.find(v => v.CFBundleURLSchemes.includes(`kakao${key}`))) {
158 | appPlist.CFBundleURLTypes.push({
159 | CFBundleTypeRole: 'Editor',
160 | CFBundleURLSchemes: [`kakao${key}`],
161 | });
162 | }
163 | }
164 | await writeFile(path + `/ios/${r}`, plist.build(appPlist));
165 | }
166 | console.log(' > Plist 내 설정값을 지정합니다.');
167 | }
168 |
169 | {
170 | console.log('3. AppDelegate.m 설정');
171 | let adf;
172 | let extension = 'm';
173 |
174 | try {
175 | adf = await readFile(path + `/ios/${name}/AppDelegate.m`);
176 | } catch {
177 | adf = await readFile(path + `/ios/${name}/AppDelegate.mm`);
178 | extension = 'mm';
179 | }
180 |
181 | if (adf.indexOf('[WithKakaoSDK isKakaoTalkLoginUrl:url]') < 0) {
182 | let adfsplit = adf.split('\n');
183 | const ix = [];
184 | adfsplit.forEach((x, n) => {
185 | if (x.indexOf('application:') > 0 && x.indexOf('penURL') > 0 && !x.trim().startsWith('//')) {
186 | ix.push(n);
187 | }
188 | });
189 |
190 | if (ix.length > 0) {
191 | console.log(' > openURL시 이벤트가 삽입되야 합니다.');
192 | console.log(
193 | ' > "if ([WithKakaoSDK isKakaoTalkLoginUrl:url]) return [WithKakaoSDK handleOpenUrl:url];" 를 openURL 옵션에 넣어주세요',
194 | );
195 | console.log(' > 자세한 내용은 github install manual 참고하세요.');
196 | } else {
197 | const pf = adf.substring(0, adf.lastIndexOf('@'));
198 | const cx = `
199 | - (BOOL) application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
200 | if ([WithKakaoSDK isKakaoTalkLoginUrl:url]) return [WithKakaoSDK handleOpenUrl:url];
201 | return YES;
202 | }
203 | `;
204 | const sf = adf.substring(adf.lastIndexOf('@'));
205 | adf = pf + cx + sf;
206 | await writeFile(path + `/ios/${name}/AppDelegate.${extension}`, adf);
207 | }
208 | }
209 |
210 | console.log(' > 저장 완료.');
211 | }
212 |
213 | console.log('Android 설치 시작');
214 | console.log('1. AndroidManifest.xml 수정');
215 | if (keys.length > 0) {
216 | let am = await readFile(path + '/android/app/src/main/AndroidManifest.xml');
217 | if (am.indexOf('com.kakao.sdk.auth.AuthCodeHandlerActivity') === -1) {
218 | const prefix = am.substring(0, am.indexOf('') + 11);
219 | const dataList = keys.map(key => ``).join('\n');
220 | const content = `\n\n
221 |
222 |
223 |
224 |
225 |
226 | ${dataList}
227 |
228 |
229 | `;
230 | const suffix = am.substring(am.indexOf('') + 11);
231 | am = prefix + content + suffix;
232 | await writeFile(path + '/android/app/src/main/AndroidManifest.xml', am);
233 | console.log(' > AndroidManifest.xml 수정완료.');
234 | } else {
235 | console.log(' > AuthCodeHandlerActivity 가 존재합니다. 키를 확인해주세요.');
236 | }
237 | } else {
238 | console.log(' > Key가 없는 관계로 우선 패스합니다.');
239 | }
240 | }
241 |
242 | console.log(``);
243 | console.log(`================================================`);
244 | console.log(`설치완료..`);
245 | console.log(`자세한 부분은 github를 참고하세요.`);
246 | console.log(`https://github.com/actbase/react-kakaosdk`);
247 | console.log(``);
248 | };
249 |
250 | start().catch(e => {
251 | console.warn(e);
252 | process.exit(1);
253 | });
254 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "target": "es3",
5 | "module": "esnext",
6 | "lib": ["dom", "es2015", "es2016", "es2017", "es2018", "es2019", "es2020", "esnext"],
7 | "jsx": "react",
8 | "preserveConstEnums": true,
9 | "skipLibCheck": true,
10 | "strict": true,
11 | "outDir": "./lib",
12 | "baseUrl": "./src",
13 | "sourceMap": true,
14 | "resolveJsonModule": true,
15 | "removeComments": true,
16 | "declaration": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "allowJs": true,
19 | "noImplicitThis": true,
20 | "moduleResolution": "node",
21 | "typeRoots": ["node_modules/@types", "@types"]
22 | },
23 | "include": ["src/**/*", "@types"],
24 | "exclude": ["node_modules"]
25 | }
26 |
--------------------------------------------------------------------------------