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.");
}
}
1