Reputation: 1
i am trying share screenshot or screen record of my app, i have implemented instagram also that is works fine, but for facebook it open the open my screenshot or screen record in pasteboard then again open my app, follow in instructions from there official website, but could not fine the solution
import Foundation import UIKit import AVFoundation
class FacebookSharingManager { static let shared = FacebookSharingManager()
private let appID: String = "1337677113908275"
/// Shares media (video or screenshot) to Facebook Stories
func shareToFacebookStories(videoURL: URL? = nil, screenshot: UIImage? = nil) {
if let videoURL = videoURL {
shareBackgroundVideo(videoURL: videoURL, appID: appID)
} else if let screenshot = screenshot {
shareImage(screenshot: screenshot, appID: appID)
} else {
print("⚠️ No valid media provided.")
}
}
/// Shares an image to Facebook Stories
private func shareImage(screenshot: UIImage, appID: String) {
let resizedImage = resizeImageToAspectRatio(image: screenshot, targetRatio: 9.0/16.0)
if let imageData = resizedImage.pngData() {
backgroundImage(imageData: imageData, appID: appID)
}
}
private func backgroundImage(imageData: Data, appID: String) {
guard let urlScheme = URL(string: "facebook-stories://share?source_appliation=\(appID)"),
UIApplication.shared.canOpenURL(urlScheme) else {
print("❌ Facebook app is not installed or does not support story sharing.")
return
}
let pasteboardItems: [[String: Any]] = [
["com.facebook.sharedSticker.backgroundImage": imageData,
"com.facebook.sharedSticker.appID": appID]
]
let pasteboardOptions: [UIPasteboard.OptionsKey: Any] = [
.expirationDate: Date().addingTimeInterval(300) // 5 minutes expiration
]
// Assign background image to pasteboard
UIPasteboard.general.setItems(pasteboardItems, options: pasteboardOptions)
// Open Facebook Stories
UIApplication.shared.open(urlScheme, options: [:], completionHandler: nil)
}
func shareBackgroundVideo(videoURL: URL, appID: String) {
// ✅ Process video before sharing
processVideoForFacebook(inputURL: videoURL) { processedURL in
guard let finalURL = processedURL else {
print("❌ Failed to process video.")
return
}
Task {
if await self.isValidVideo(videoURL: finalURL) {
do {
let videoData = try Data(contentsOf: finalURL)
self.backgroundVideo(videoData: videoData, appID: appID)
} catch {
print("❌ Failed to load video data: \(error.localizedDescription)")
}
} else {
print("⚠️ Video still does not meet Facebook's requirements!")
}
}
}
}
func backgroundVideo(videoData: Data, appID: String) {
guard let urlScheme = URL(string: "facebook-stories://share?source_application\(appID)"),
UIApplication.shared.canOpenURL(urlScheme) else {
print("❌ Facebook app is not installed or does not support story sharing.")
return
}
let pasteboardItems: [[String: Any]] = [
["com.facebook.sharedSticker.backgroundVideo": videoData,
"com.facebook.sharedSticker.appID": appID]
]
let pasteboardOptions: [UIPasteboard.OptionsKey: Any] = [
.expirationDate: Date().addingTimeInterval(300) // 5 minutes expiration
]
// Assign background video to pasteboard
UIPasteboard.general.setItems(pasteboardItems, options: pasteboardOptions)
DispatchQueue.main.async {
UIApplication.shared.open(urlScheme, options: [:], completionHandler: nil)
}
}
private func showAlert(title: String, message: String) {
DispatchQueue.main.async {
if let topVC = self.getTopViewController() {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
topVC.present(alert, animated: true)
} else {
print("❌ Failed to find top view controller.")
}
}
}
// Helper function to get the top-most view controller
private func getTopViewController() -> UIViewController? {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first,
let rootViewController = window.rootViewController {
var topController: UIViewController? = rootViewController
while let presentedViewController = topController?.presentedViewController {
topController = presentedViewController
}
return topController
}
return nil
}
func resizeImageToAspectRatio(image: UIImage, targetRatio: CGFloat) -> UIImage {
let originalSize = image.size
let originalRatio = originalSize.width / originalSize.height
var newSize: CGSize
if originalRatio > targetRatio {
// Crop width to fit 9:16
newSize = CGSize(width: originalSize.height * targetRatio, height: originalSize.height)
} else {
// Crop height to fit 9:16
newSize = CGSize(width: originalSize.width, height: originalSize.width / targetRatio)
}
let format = UIGraphicsImageRendererFormat()
format.scale = image.scale
let renderer = UIGraphicsImageRenderer(size: newSize, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: newSize))
}
}
/// ✅ **Validates if video meets Facebook's requirements**
private func isValidVideo(videoURL: URL) async -> Bool {
let asset = AVURLAsset(url: videoURL)
do {
// Load required properties asynchronously
let duration = try await asset.load(.duration)
let tracks = try await asset.loadTracks(withMediaType: .video)
guard let track = tracks.first else {
print("❌ No video track found.")
return false
}
let size = try await track.load(.naturalSize)
let bitrate = try await track.load(.estimatedDataRate)
let durationSeconds = CMTimeGetSeconds(duration)
print("📹 Video Info: Duration = \(durationSeconds)s, Resolution = \(size.width)x\(size.height), Bitrate = \(bitrate)")
// ✅ Ensure video meets Facebook’s recommended criteria
return durationSeconds <= 20 &&
size.width >= 1080 &&
bitrate <= 50_000_000 // 50 Mbps max recommended
} catch {
print("❌ Error loading video properties: \(error.localizedDescription)")
return false
}
}
func processVideoForFacebook(inputURL: URL, completion: @escaping (URL?) -> Void) {
Task {
let asset = AVURLAsset(url: inputURL)
// ✅ Ensure export session is created outside the Task
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else {
print("❌ Failed to create AVAssetExportSession")
completion(nil)
return
}
do {
// ✅ Load required properties asynchronously
let duration = try await asset.load(.duration)
let tracks = try await asset.loadTracks(withMediaType: .video)
guard let videoTrack = tracks.first else {
print("❌ No video track found.")
completion(nil)
return
}
let preferredTransform = try await videoTrack.load(.preferredTransform)
// ✅ Define output file path
let outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("processedVideo.mp4")
try? FileManager.default.removeItem(at: outputURL)
// ✅ Force video to 1080x1920 resolution
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(width: 1080, height: 1920)
videoComposition.frameDuration = CMTime(value: 1, timescale: 30) // 30 FPS
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(start: .zero, duration: duration)
let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
transformer.setTransform(preferredTransform, at: .zero)
instruction.layerInstructions = [transformer]
videoComposition.instructions = [instruction]
exportSession.outputURL = outputURL
exportSession.outputFileType = .mp4
exportSession.shouldOptimizeForNetworkUse = true
exportSession.videoComposition = videoComposition
// ✅ Start exporting asynchronously
exportSession.exportAsynchronously {
DispatchQueue.main.async { // ✅ Ensure completion runs on the main thread
if exportSession.status == .completed {
print("✅ Video processed successfully!")
completion(outputURL)
} else {
print("❌ Video processing failed: \ (exportSession.error?.localizedDescription ?? "Unknown error")")
completion(nil)
}
}
}
} catch {
print("❌ Failed to load video properties: \. (error.localizedDescription)")
completion(nil)
}
this is my complete code i am expecting to open facebook and my provided content should be appear in pasteboard and i have general option to post that on stories
Upvotes: -1
Views: 21