Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to Share Image to TikTok from Flutter App Due to "Permissions Denied" Error #39

Open
TomCohenDev opened this issue Jun 19, 2024 · 18 comments

Comments

@TomCohenDev
Copy link

I'm trying to share an image from my Flutter app to TikTok using the TikTok Open SDK and share sdk. I've integrated the TikTok SDK into my iOS project.
I'm encountering the following error when attempting to share:

[FirebaseAnalytics][I-ACS023001] Deep Link does not contain valid required params. URL params: {
    "error_code" = "-4";
    "error_description" = "Permissions denied";
    "from_platform" = tiktoksharesdk;
    "request_id" = "B0AC02F2-33CE-4C94-8F47-9ABF3381CB90";
    "response_id" = "40AA3C41-DC79-4026-9D41-D627931EAE1F";
    "share_state" = 20003;
}

I have tried some solutions i found online like adding the Login kit, convert the local file path to PHAsset, checked podfile, made sure the app was accepted on tiktok dev site, added the keys to plist file...

Below are the relevant code snippets from my AppDelegate.swift and Flutter code. Can someone help me identify what might be going wrong?

import UIKit
import Flutter
import TikTokOpenSDKCore
import TikTokOpenShareSDK
import Photos

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)

        let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(name: "com.example.tiktokshare/share",
                                           binaryMessenger: controller.binaryMessenger)

        channel.setMethodCallHandler { [weak self] (call, result) in
            if call.method == "shareImage" {
                if let args = call.arguments as? [String: Any], let localIdentifiers = args["localIdentifiers"] as? [String] {
                    TikTokShare().shareImage(localIdentifiers: localIdentifiers, result: result)
                } else {
                    result(FlutterError(code: "error", message: "Invalid arguments", details: nil))
                }
            } else if call.method == "getLocalIdentifier" {
                if let args = call.arguments as? [String: Any], let filePath = args["filePath"] as? String {
                    self?.getLocalIdentifier(for: filePath, result: result)
                } else {
                    result(FlutterError(code: "error", message: "Invalid arguments", details: nil))
                }
            } else {
                result(FlutterMethodNotImplemented)
            }
        }

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    private func getLocalIdentifier(for filePath: String, result: @escaping FlutterResult) {
        let status = PHPhotoLibrary.authorizationStatus()
        if status == .notDetermined {
            PHPhotoLibrary.requestAuthorization { newStatus in
                if newStatus == .authorized {
                    self.saveToPhotoLibrary(filePath: filePath, result: result)
                } else {
                    result(FlutterError(code: "error", message: "Photos permission denied", details: nil))
                }
            }
        } else if status == .authorized {
            self.saveToPhotoLibrary(filePath: filePath, result: result)
        } else {
            result(FlutterError(code: "error", message: "Photos permission denied", details: nil))
        }
    }

    private func saveToPhotoLibrary(filePath: String, result: @escaping FlutterResult) {
        var localIdentifier: String?

        PHPhotoLibrary.shared().performChanges({
            let request: PHAssetChangeRequest?
            if filePath.hasSuffix(".jpeg") || filePath.hasSuffix(".jpg") || filePath.hasSuffix(".png") {
                request = PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: URL(fileURLWithPath: filePath))
            } else {
                request = nil
            }
            localIdentifier = request?.placeholderForCreatedAsset?.localIdentifier
        }, completionHandler: { success, error in
            if success, let localIdentifier = localIdentifier {
                result([localIdentifier])
            } else {
                result(FlutterError(code: "error", message: "Failed to get local identifier", details: error?.localizedDescription))
            }
        })
    }
}

@objc(TikTokShare)
class TikTokShare: NSObject {
    @objc func shareImage(localIdentifiers: [String], result: @escaping FlutterResult) {
        let shareRequest = TikTokShareRequest(localIdentifiers: localIdentifiers,
                                              mediaType: .image,
                                              redirectURI: "revamped://tiktokshare")
        shareRequest.shareFormat = .normal
        shareRequest.send { response in
            guard let shareResponse = response as? TikTokShareResponse else {
                result(FlutterError(code: "error", message: "Invalid response", details: nil))
                return
            }
            if shareResponse.errorCode == .noError {
                result("Share succeeded!")
            } else {
                result(FlutterError(code: "error", message: "Share failed with error code: \(shareResponse.errorCode.rawValue), state: \(shareResponse.shareState)", details: nil))
            }
        }
    }
}

Flutter code:

import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';

class TikTokShare {
  static const MethodChannel _channel =
      MethodChannel('com.example.tiktokshare/share');

  static Future<List<String>> getLocalIdentifier(String filePath) async {
    try {
      final List<dynamic> result = await _channel
          .invokeMethod('getLocalIdentifier', {'filePath': filePath});
      return result.cast<String>();
    } on PlatformException catch (e) {
      print("Failed to get local identifier: '${e.message}'.");
      return [];
    }
  }

  static Future<void> shareImage(List<String> localIdentifiers) async {
    try {
      final result = await _channel
          .invokeMethod('shareImage', {'localIdentifiers': localIdentifiers});
      print(result);
    } on PlatformException catch (e) {
      print("Failed to share image: '${e.message}'.");
    }
  }
}

void shareToTikTok() async {
  print("Sharing to TikTok...");

  var status = await Permission.photos.request();

  if (status.isGranted) {
    final picker = ImagePicker();
    final pickedFile = await picker.pickImage(source: ImageSource.gallery);

    if (pickedFile != null) {
      final filePath = pickedFile.path;
      final List<String> localIdentifiers =
          await TikTokShare.getLocalIdentifier(filePath);
      if (localIdentifiers.isNotEmpty) {
        TikTokShare.shareImage(localIdentifiers);
      } else {
        print("Failed to get local identifier.");
      }
    } else {
      print("No image selected.");
    }
  } else {
    print("Photos permission denied.");
  }
}
@Dokome
Copy link

Dokome commented Jul 4, 2024

I meet this problem too,have you solved it? I try to use the sandbox client key, but still not work.

@TomCohenDev
Copy link
Author

Yes i have,
What i have done that worked:

  • Add the share sdk, integrate it fully.
  • Make sure your app is approved on tiktok developer.
  • dont share image and text, share just images. (Seems like text automatically removes some options)
  • don't use any of the methods of thw share sdk, use the share_plus package methods.

Its odd but only after integration of the sdk share_plus added tiktok, which before it hadn't

@Dokome
Copy link

Dokome commented Jul 4, 2024

Yes i have, What i have done that worked:

  • Add the share sdk, integrate it fully.
  • Make sure your app is approved on tiktok developer.
  • dont share image and text, share just images. (Seems like text automatically removes some options)
  • don't use any of the methods of thw share sdk, use the share_plus package methods.

Its odd but only after integration of the sdk share_plus added tiktok, which before it hadn't

Thanks, our app is build on react-native,I will learn how the share_plus library worked

@Nishannb
Copy link

Nishannb commented Jul 7, 2024

@Dokome Are you using Tiktok Share Kit in react native? I was looking for some help in integrating it, if you can help me.

@Dokome
Copy link

Dokome commented Jul 7, 2024

@Dokome Are you using Tiktok Share Kit in react native? I was looking for some help in integrating it, if you can help me.

I integrated the legacy sdk and always get the Permission Denied (errCode -4) 😂,what about you

@Nishannb
Copy link

Nishannb commented Jul 7, 2024

I am also not able to integrate it. Do you mean https://developers.tiktok.com/doc/getting-started-ios-quickstart-objective-c as legacy sdk.
I think this is the new sdk https://developers.tiktok.com/doc/mobile-sdk-ios-quickstart right?

@Dokome
Copy link

Dokome commented Jul 7, 2024

I am also not able to integrate it. Do you mean https://developers.tiktok.com/doc/getting-started-ios-quickstart-objective-c as legacy sdk. I think this is the new sdk https://developers.tiktok.com/doc/mobile-sdk-ios-quickstart right?

Yes, I use this repo and react-native-tiktok before, but the swift code always compile failed in my project, so I finally integrate the
legacy sdk with ObjectC.

@Nishannb
Copy link

Nishannb commented Jul 8, 2024

I cannot integrate with my project. Using Legacy SDK, I get this error:

Showing All Errors Only
/Users/admin/Library/Developer/Xcode/DerivedData/Take-eauqcvjjwjjiehdqnbdofghuenhl/Build/Products/Debug-iphoneos/XCFrameworkIntermediates/TikTokOpenSDK/TikTokOpenSDK.framework/Headers/TikTokOpenSDKShare.h:74:40: 'TikTokOpenSDKShareRequest' has different definitions in different modules; first difference is definition in module 'TikTokOpenSDK.TikTokOpenSDKShare' found property

I followed this doc.
https://developers.tiktok.com/doc/getting-started-ios-quickstart-objective-c
Can you share how you managed to do it. If you have example project, I can look into it.

@Dokome
Copy link

Dokome commented Jul 8, 2024

@Nishannb
For the errors maybe you can reinstall your pods files and clear cache.I paste part of my code here, you can have a try.

@Nishannb
Copy link

Nishannb commented Jul 8, 2024

@Dokome I did delete pod file and cleared the cache. Same error. Thanks for the part of the code. I still am out of context with the code. Is there a share kit implemented RN project I can check in your repo, I can learn from there.

@Nishannb
Copy link

Nishannb commented Jul 8, 2024

When I share, it open the tiktok app and immediately comes back.

@Dokome
Copy link

Dokome commented Jul 8, 2024

When I share, it open the tiktok app and immediately comes back.

It also happened to my project,is your app published or use sandbox app client key?

@Nishannb
Copy link

Nishannb commented Jul 8, 2024

It's not a published app. I am trying in sandbox with its client key. What was the problem in your project and how were you able to debug it ?

@Dokome
Copy link

Dokome commented Jul 8, 2024

@Nishannb My problem just as this issue say, and I print the errCode from share response (You can check the Xcode terminal or resolve the errCode print with javascript).

NSInteger code = response.errCode;
NSLog(@"Recevied value: %ld", code);

@enestatli
Copy link

@Nishannb @Dokome I don't know if you are still looking for a solution, I just published an npm module, currently only supports share sdk.

https://github.com/rnheroes/tiktok-opensdk-react-native

@Dokome
Copy link

Dokome commented Oct 15, 2024

@Nishannb @Dokome I don't know if you are still looking for a solution, I just published an npm module, currently only supports share sdk.

https://github.com/rnheroes/tiktok-opensdk-react-native

Thanks bro, i solved it after published my app

@Nishannb
Copy link

@Nishannb @Dokome I don't know if you are still looking for a solution, I just published an npm module, currently only supports share sdk.

https://github.com/rnheroes/tiktok-opensdk-react-native

I tested the package, it crashes with a single line error Could not cast value of type '__NSCFNumber' (0x1fcd08808) to 'NSString' (0x1fcd07108).

@enestatli
Copy link

@Nishannb @Dokome I don't know if you are still looking for a solution, I just published an npm module, currently only supports share sdk.

https://github.com/rnheroes/tiktok-opensdk-react-native

I tested the package, it crashes with a single line error Could not cast value of type '__NSCFNumber' (0x1fcd08808) to 'NSString' (0x1fcd07108).

@Nishannb Open an issue please, include your environment, react native version etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants