tiktok / tiktok-opensdk-ios

The TikTok OpenSDK features Login Kit and Share Kit which allow your users to log in using their TikTok account and share content from your app to TikTok.
https://developers.tiktok.com
Other
80 stars 31 forks source link

how to add sdk in objective-C project? #2

Closed sunny635533 closed 1 year ago

sunny635533 commented 1 year ago

my project is objective-C project,but sample codes is use swift. So i dont know how integerate it with objective-c. Please provide sample code with objective-c.Thanks

stephen-boyle commented 1 year ago

After installing the SDK using Cocoapods, you can import the SDK into your Objective-C files using the following imports:

#import <TikTokOpenSDKCore/TikTokOpenSDKCore-Swift.h>
#import <TikTokOpenAuthSDK/TikTokOpenAuthSDK-Swift.h>
#import <TikTokOpenShareSDK/TikTokOpenShareSDK-Swift.h>
sunny635533 commented 1 year ago

@stephen-boyle i follow you to import these,but still has error.

image

my pod file codes is:

#source 'https://github.com/CocoaPods/Specs.git'

require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '12.4'

install! 'cocoapods', :deterministic_uuids => false

production = ENV["PRODUCTION"] == "1"

target 'iBooming' do
    project 'iBooming'
  config = use_native_modules!

  flags = get_default_flags()

#tiktok SDK
# pod 'TikTokOpenSDK', '~> 5.0.14'
 pod 'TikTokOpenSDKCore', '~> 2.0.0'
 pod 'TikTokOpenAuthSDK', '~> 2.0.0'

 pod 'SVProgressHUD', '~> 2.2.5'

  use_react_native!(
    :path => config[:reactNativePath],
    # to enable hermes on iOS, change `false` to `true` and then install pods
    :production => production,
        :hermes_enabled => flags[:hermes_enabled],
        :fabric_enabled => flags[:fabric_enabled],
        :flipper_configuration => FlipperConfiguration.enabled,
        # An absolute path to your application root.
        :app_path => "#{Pod::Config.instance.installation_root}/.."
  )

  target 'iBoomingTests' do
    inherit! :complete
    # Pods for testing
  end

  # Enables Flipper.
  #
  # Note that if you have use_frameworks! enabled, Flipper will not work and
  # you should disable the next line.
  use_flipper!()

  post_install do |installer|
    react_native_post_install(installer)
    __apply_Xcode_12_5_M1_post_install_workaround(installer)
  end
end

What else do I need to configure? Maybe it's convenient for you to provide me with a project example.Thanks.

nickdnk commented 1 year ago

@sunny635533 You have commented out TikTokOpenSDK:

# pod 'TikTokOpenSDK', '~> 5.0.14'
pod 'TikTokOpenSDKCore', '~> 2.0.0'
pod 'TikTokOpenAuthSDK', '~> 2.0.0'

Should be:

pod 'TikTokOpenSDKCore'
pod 'TikTokOpenAuthSDK'
pod 'TikTokOpenShareSDK'

If you want to include the project with SPM instead of CocoaPods, you can do that with my fork here: https://github.com/nickdnk/tiktok-opensdk-ios - this would also confirm that the SPM version works with Objective C projects.

sunny635533 commented 1 year ago

@nickdnk i dont want to include with SPM, because my project is Objective-C's project. TikTokOpenSDK is the sdk of Tiktok api V1,but now i want to upgrade to Tiktok api V2 version. So i have commented out TikTokOpenSDK. I found a way to use swift's sdk's method directly with objective-c, First step=> i created "projectName-Bridging-Header.h" file and add these in it,

@import TikTokOpenSDKCore;
@import TikTokOpenAuthSDK;

Second step=> import these class in OC files,

#import "projectName-Bridging-Header.h"
RCT_EXPORT_METHOD(getAuthCode:(NSString *)state scopes:(NSString *)scope resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSLog(@"-----------state=%@,scope=%@",state,scope);
  dispatch_async(dispatch_get_main_queue(), ^{
  NSArray *scopesArray = [scope componentsSeparatedByString:@","];
  NSSet<NSString *> *scopeSet = [NSSet setWithArray:scopesArray];
  NSString *redirectUri = @"https://kkk.test.com/app/";
  TTKSDKAuthRequest *authRequest=[[TTKSDKAuthRequest alloc] initWithScopes:scopeSet redirectURI:redirectUri];
  authRequest.isWebAuth = false;
  [authRequest send:^(id<TTKSDKBaseResponse> response) {
    NSLog(@"========== TTKSDKBaseResponse ====%@",response);
    if(response == nil){
      NSError *error=[NSError errorWithDomain:@"error domain" code:-100 userInfo:nil];
      reject(@"getAuthCode fail",@"error",error);
    }else{
      TTKSDKAuthResponse *myresponse = (TTKSDKAuthResponse *)response;
      if(myresponse.errorCode == 0){
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
        [dict setValue:0 forKey:@"errorCode"];
        [dict setValue:myresponse.errorDescription forKey:@"errorMsg"];
        [dict setValue:myresponse.authCode forKey:@"authCode"];
        [dict setValue:scope forKey:@"grantedPermissions"];

        //how to get codeVerifier???
//        TTKSDKCodeVerifier *verifier = authRequest.pkce;
//        [dict setValue:authRequest.pkce.codeVerifier forKey:@"codeVerifier"];
        [dict setValue:redirectUri forKey:@"redirectUri"];
      }else{
        NSError *error=[NSError errorWithDomain:@"error domain" code:-100 userInfo:nil];
        reject(@"getAuthCode fail",@"error",error);
      }
    }
  }];
  });

}

Third step => add these in AppDelege.m,

#import "projectName-Bridging-Header.h"
- (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  if([TTKSDKURLHandler handleOpenURL:url]){
    return true;
  }
    return [RCTLinkingManager application:application openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  if([TTKSDKURLHandler handleOpenURL:url]){
    return true;
  }
  return [RCTLinkingManager application:application openURL:url
                                 sourceApplication:sourceApplication annotation:annotation];
}

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
  if([TTKSDKURLHandler handleOpenURL:url]){
    return true;
  }
    return NO;
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler{
  NSLog(@"=========== restorationHandler  ========%@",userActivity.webpageURL);
  if([TTKSDKURLHandler handleOpenURL:userActivity.webpageURL]){
    return true;
  }
  return false;
}

At last i get an error, [TTKSDKURLHandler handleOpenURL:userActivity.webpageURL] alaway return "null", i hope it can return true. And never come in here, [authRequest send:^(id response) { .... }, I dont know why. If you konw,please tell me.Thanks.

nickdnk commented 1 year ago

SPM works with Objective-C. That's what I just wrote. You just need to add all 3 packages and import them in your bridging header file.

While you don't need CocoaPods at all, the pods I listed are the ones you need to include. They are all version 2.0. Note that what I wrote was TikTokOpenSDKCore while you wrote TikTokOpenSDK. The latter is version 1.

nickdnk commented 1 year ago

I just tested with an Objective-C project, and all you need to do if using SPM is:

@import TikTokOpenAuthSDK; // (or Core/Share; the library you need)

In your header files where you want to use the library, i.e. AppDelegate.h;

sunny635533 commented 1 year ago

@nickdnk I tried add it with SPM, but encountered some problems, can not go to the next step.

image

Tried several times today, still can't download.

nickdnk commented 1 year ago

You added this repo which does not support SPM yet. The link I posted was this: https://github.com/nickdnk/tiktok-opensdk-ios - it is my fork of this repo which supports SPM.

sunny635533 commented 1 year ago

Still has same problem,

image
nickdnk commented 1 year ago

It's not the same problem. I can't read the error message. You'll have to post the full error.

nickdnk commented 1 year ago

Try using Branch: main instead of "up to next major".

nickdnk commented 1 year ago

And also remember to check all 3 packages when it asks you which you want to add to your project.

sunny635533 commented 1 year ago

First step,choose=>Branch: main,

image

Sceond Step:

image

Still has error. But thank you for your reply.

nickdnk commented 1 year ago

You are not posting the full error, so I cannot help you debug it. It works in my Objective C project.

nickdnk commented 1 year ago

After adding the package, you can change its parameters here:

Screenshot 2023-07-06 at 18 05 55

And it should be linked as a framework in your app's target as well here:

Screenshot 2023-07-06 at 18 04 37

chenqi777 commented 1 year ago

@sunny635533 我也遇到这个问题, handleOpenURL一直返回false, 查不出原因. 然后我自己解析url, 把结果返回给RN了, 代码给你参考下

@objc
  public static func handleOpenURL(_ url: URL) -> Bool {
    // TIkTok 官方代码
    // return TikTokURLHandler.handleOpenURL(url);

    // 由于TikTok SDK一直返回false, 查不出原因, 所以自己解析url返回结果
    do {
      guard let comps = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
          throw TikTokResponseError.failToParseURL
      }

      guard let comps = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
          throw TikTokResponseError.failToParseURL
      }
      guard let dict = comps.queryItems?.reduce(into: [String: String](), {
          $0[$1.name] = $1.value
      }) else {
          throw TikTokResponseError.failToParseURL
      }

      if (dict["from_platform"] == "tiktokopensdk") {
        let errorDescription = dict["error_description"]
        let errorCode            = dict["error_code"]
        let authCode         = dict["code"]

        if (errorCode == "0") {
          resolve?([
            "code": authCode,
            "codeVerifier": codeVerifier,
            "redirectUri": redirectUri
          ]);
        } else {
          reject?(errorCode, errorDescription, nil);
        }
        return true
      }
    } catch {
    }
    return false;
  }
sunny635533 commented 1 year ago

@chenqi777 你好,resolve 和 reject 怎么弄成全局静态调用?android集成使用没问题,但ios我不是很熟悉怎么写,你方便把代码贴完整出来给我参考下么?谢谢!

chenqi777 commented 1 year ago

@sunny635533 iOS我也不熟, 我是写了个Swift桥接代码, 然后用OC来掉Swift

import Foundation
import TikTokOpenSDKCore
import TikTokOpenAuthSDK

@objc
final public class TikTokBridge: NSObject {
  static var resolve : RCTPromiseResolveBlock? = nil;
  static var reject : RCTPromiseRejectBlock? = nil;
  static var codeVerifier : String = "";
  static var redirectUri : String = RNCConfig.env(for: "TIKTOK_IOS_REDIRECT_URI");

  @objc
  public static func handleOpenURL(_ url: URL) -> Bool {
    // TIkTok 官方代码
    // return TikTokURLHandler.handleOpenURL(url);

    // 由于TikTok SDK一直返回false, 查不出原因, 所以自己解析url返回结果
    do {
      guard let comps = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
          throw TikTokResponseError.failToParseURL
      }

      guard let comps = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
          throw TikTokResponseError.failToParseURL
      }
      guard let dict = comps.queryItems?.reduce(into: [String: String](), {
          $0[$1.name] = $1.value
      }) else {
          throw TikTokResponseError.failToParseURL
      }

      if (dict["from_platform"] == "tiktokopensdk") {
        let errorDescription = dict["error_description"]
        let errorCode            = dict["error_code"]
        let authCode         = dict["code"]

        if (errorCode == "0") {
          resolve?([
            "code": authCode,
            "codeVerifier": codeVerifier,
            "redirectUri": redirectUri
          ]);
        } else {
          reject?(errorCode, errorDescription, nil);
        }
        return true
      }
    } catch {
    }
    return false;
  }

  @objc
  public static func login(_ resolve1: @escaping RCTPromiseResolveBlock, reject1: @escaping RCTPromiseRejectBlock) {
    let authRequest = TikTokAuthRequest(scopes: ["user.info.basic", "user.info.profile", "user.info.stats", "video.list", "video.publish", "video.upload"],
                                        redirectURI: redirectUri);

    /**
     TIkTok 官方代码
     由于TikTok SDK一直返回false, 查不出原因, 所以自己解析url返回结果
     authRequest.send { response in
           /* Step 3 */
           guard let authResponse = response as? TikTokAuthResponse else { return }
           if authResponse.errorCode == .noError {
             print("Auth code: \(authResponse.authCode)")
           } else {
               print("Authorization Failed!")
           }
       }
     */

    codeVerifier = authRequest.pkce.codeVerifier;
    resolve = resolve1;
    reject = reject1;
    authRequest.send();

  }
}

可以在RN的module把resolve 和 reject传过去

RCT_REMAP_METHOD(login, findEventsWithResolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
  [TikTokBridge login:resolve reject1:reject];
}
sunny635533 commented 1 year ago

@chenqi777 通过你的代码,我之前遇到的问题找到解决方法了,[TTKURLHandler handleOpenURL:url] 是返回true,完整代码: TikokUtils 类(swift)=>

//
//  TikTokUtils.swift
//  iBooming
//
//  Created by sunny on 2023/6/29.
//

import Foundation
import TikTokOpenSDKCore
import TikTokOpenAuthSDK

//@objcMembers public class TikokUtils : NSObject{
@objc
final public class TikokUtils: NSObject {
  static var resolve : RCTPromiseResolveBlock? = nil;
  static var reject : RCTPromiseRejectBlock? = nil;

//  public static var instance = TikokUtils()

  public func handleOpenURL(url: URL)-> Bool{
    if TikTokURLHandler.handleOpenURL(url) {
      print("======= handleOpenURL true =====",url);
        return true
    }
    print("======= handleOpenURL false =====")
    return false;
  }

//  //获取code
  @objc
  public static func getAuthCode(scopes: NSString,resolver:@escaping RCTPromiseResolveBlock,rejecter:@escaping RCTPromiseRejectBlock)->Void{
    resolve = resolver;
    reject = rejecter;

    let scopesArray:Array = scopes.components(separatedBy: ",");
    var setScopes: Set<String> = [];
    let redirectUri = "https://m.iboomingglobal.com/app/"
    let dict = NSMutableDictionary();

    for index in 0 ..< scopesArray.count {
      setScopes.insert(scopesArray[index])
    }
    print("===getAuthCode 1111111==== ",setScopes);

    let authRequest = TikTokAuthRequest(scopes: setScopes, redirectURI:redirectUri)
    authRequest.isWebAuth = false
    DispatchQueue.main.sync {
      authRequest.send { response in
        guard  let myresponse = response as? TikTokAuthResponse else {
          let errorObj = NSError();
          reject?("getAuthCode fail","error",errorObj);
          return }

        if myresponse.errorCode != .noError {
            let message = "Error: \(myresponse.error ?? "") \n Error Description: \(myresponse.errorDescription ?? "")"
          let errorObj = NSError();
          reject?("getAuthCode fail","error",errorObj);
        }else{
          dict.setValue(myresponse.errorCode.rawValue, forKey:"errorCode")
          dict.setValue(myresponse.errorDescription, forKey:"errorMsg")
          dict.setValue(myresponse.authCode, forKey:"authCode")
          dict.setValue(scopes, forKey:"grantedPermissions")
          dict.setValue(authRequest.pkce.codeVerifier, forKey:"codeVerifier")
          dict.setValue(redirectUri, forKey:"redirectUri")
          resolve?(dict)
        }
        print("===getAuthCode 33333==== ", dict);
      }
    }

  }

}

OC和swift的桥接文件: 项目名称-Bridging-Header.h


@import TikTokOpenSDKCore;
@import TikTokOpenAuthSDK;

#import <React/RCTBridgeModule.h>

AppDelegate.m 类

#import "项目名称-Bridging-Header.h"

- (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    if([TTKSDKURLHandler handleOpenURL:url]){
      return true;
    }
    return [RCTLinkingManager application:application openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  if([TTKSDKURLHandler handleOpenURL:url]){
    return true;
  }
    return [RCTLinkingManager application:application openURL:url
                                 sourceApplication:sourceApplication annotation:annotation];
}

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
  if([TTKSDKURLHandler handleOpenURL:url]){
    return true;
  }
    return NO;
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler{
  BOOL result = [TTKSDKURLHandler handleOpenURL:userActivity.webpageURL];
  NSLog(@"=========== restorationHandler  ========%@  result=%d",userActivity.webpageURL,result);
  if(result){
    return true;
  }
  return false;
}

最后OC桥接给JS调用的方法:

#import "项目名称-Bridging-Header.h"
#import "项目名称-Swift.h"

RCT_EXPORT_METHOD(getNewAuthCode:(NSString *)state scopes:(NSString *)scope resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSLog(@"----getNewAuthCode-------state=%@,scope=%@",state,scope);
  [TikokUtils getAuthCodeWithScopes:scope resolver:resolve rejecter:reject];

}

js 代码:

 NativeModules.TikTokHelper.getNewAuthCode("1687919509192","user.info.basic,video.list").then(async result => {})
chenqi777 commented 1 year ago

@sunny635533 所以问题是出在哪? 是有些参数没传对吗?

sunny635533 commented 1 year ago

@chenqi777 AppDelegate类的这个方法你有重写么? (BOOL)application:(UIApplication )application continueUserActivity:(NSUserActivity )userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler{

chenqi777 commented 1 year ago

@sunny635533 有重写的

sunny635533 commented 1 year ago

@sunny635533 iOS配置Universal Links,这个配置测试成功了?如果成功的话,tiktok开发平台ios配置检查下:

image
chenqi777 commented 1 year ago

@sunny635533 算了不纠结了, 现在不影响使用, 将就着用了, 有空再看看

zhsuperman commented 1 year ago

在oc代码中 调用authRequest.pkce.codeVerifier 报错Property 'codeVerifier' not found on object of type 'TTKSDKCodeVerifier *',看起来是因为swift代码中的pkce 没有讲codeVerifier 开发给oc代码 应该如何解决啊

stephen-boyle commented 1 year ago

@ZhangChao0613 We recently made a fix for this for, please upgrade your SDK version to v2.2.0