I'm trying to implement the functionality of:
Recording a video (it's automatically saved to the apps memory)
Moving video file from apps memory to gallery (in this step we also save path to newly created video in gallery)
Displaying video from gallery
Steps 1. and 3. are written in Flutter, step 2 was implemented natively in Swift.
For some reason this feature sometimes works and sometimes not. When it doesn't work in step 3. I receive an error PlatformException(VideoError, Failed to load video: The requested URL was not found on this server., null, null).
I've also tried using path retrieved from step 2. to simply create File with it File(path) but then I also receive an error that file was not found (OS Error: No such file or directory, errno = 2).
I suspect, that on iOS it's caused by the whole App Sandbox thing is that correct? But if it is, why it sometimes does work and sometimes doesn't? Maybe there is something in my code which I could fix?
For step 1. I use camera package with basically the same code as in example. Then, after receiving XFile I run native iOS code, to save video to gallery and get it's path:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let videoSavingChannel = FlutterMethodChannel(name: "app.package.name/camera/video_to_gallery",
binaryMessenger: controller.binaryMessenger)
videoSavingChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
// This method is invoked on the UI thread.
guard call.method == "saveVideoToGallery" else {
result(FlutterMethodNotImplemented)
return
}
let args = call.arguments as? Dictionary<String, Any>
guard let fileUrl = args?["fileURL"] else {
result(nil)
return
}
self?.saveVideoToGallery(fileURL: fileUrl as! String, result: result)
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func saveVideoToGallery(fileURL: String, result: #escaping FlutterResult) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: fileURL))
}) { saved, error in
if saved {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject?.getURL { urlFromGallery in
guard let absoluteUrl = urlFromGallery?.absoluteString else {
result(nil)
return
}
result(absoluteUrl)
}
}
}
}
getting a video path:
extension PHAsset {
func getURL(completionHandler : #escaping ((_ responseURL : URL?) -> Void)){
if self.mediaType == .image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustment: PHAdjustmentData) -> Bool in
return true
}
self.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) -> Void in
completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
})
} else if self.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) -> Void in
if let urlAsset = asset as? AVURLAsset {
let localVideoUrl: URL = urlAsset.url as URL
completionHandler(localVideoUrl)
} else {
completionHandler(nil)
}
})
}
}
}
And then in flutter to display a video I use video_player again with pretty basic implementation:
VideoPlayerController controller =
VideoPlayerController.file(File(_videoPathFromGallery));
controller.initialize().then((_) {
//...someStuffHere
}
It's probably more of an iOS question rather than a flutter one.
It turned out that I've updated the video_player library from 2.4.6 to 2.5.1 and after that it stopped working. After downgrading it back to 2.4.6 it started working again.
I've also found how to make it work on 2.5.1 library. Videos from gallery on iOS have path similar to: file:///var/mobile/Media/DCIM/100APPLE/IMG_0103.MP4. To make it work we have to remove file:/// prefix.
Related
I have made a dynamic link in Firebase, which op my iOS app, when I click it.
The problem is, that I can't print information about the link.
I use this function in the AppDelegate file to handle the dynamic link:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("Handeling dynamic link")
if let incomingURL = userActivity.webpageURL {
print("Incoming URL is \(incomingURL)")
let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
guard error == nil else {
print("Found an error! \(error!.localizedDescription)")
return
}
if let dynamicLink = dynamicLink {
self.handleIncomingDynamicLink(dynamicLink)
}
}
if linkHandled {
return true
} else {
// Maybe do other things with our incoming url
return false
}
}
return false
}
When I click on a dynamic link and open the app, none of the print statements is printed to the console.
It seems like this function is never tapped into.
The handleDynamicLink function is:
func handleIncomingDynamicLink(_ dynamicLink: DynamicLink) {
guard let url = dynamicLink.url else {
print("That is weird. My dynamic link object has no url")
return
}
print("Your incoming link parameter is \(url.absoluteString)")
}
I want to print information about the url so I can debug and use the information in the url to redirect to pages in the app.
When I'm testing this, I run the app on an iPhone connected to my mac.
I don't run it on the iOS simulator.
The code in your UIApplication delegate method looks good. The only difference I see with the way you have yours compared to mine is I'm returning true in the last line & you're returning false.
By the way, put breakpoints in that UIApplication delegate method & let me know if you're ever even getting to the first line in that delegate method. Are you able to hit any breakpoints in your handleIncomingDynamicLink() method?
Also, try adding this in applicationDidBecomeActive:
func applicationDidBecomeActive(_ application: UIApplication) {
guard let url = self.launchURL else { return }
self.launchURL = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: { // wait to init. notifs.
DynamicLinks.dynamicLinks().handleUniversalLink(url) { (dynamiclink, error) in
if let dynamiclink = dynamiclink {
self.handleIncomingDynamicLink(dynamiclink)
}
}
})
}
And add this:
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("RECEIVED A URL THROUGH A CUSTOM SCHEME: \(url.absoluteString)")
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
self.handleIncomingDynamicLink(dynamicLink)
return true
} else { // Maybe handle Google or Facebook sign-in here
return false
}
}
My iPhone app sends a custom URL file via AirDrop to my iPad app. The URL has an extension of .fdre. When sent the iPad app opens. However, Application function to handle receiving the custom file in AppDelegate never seems to get hit. Any help would be greatly appreciated.
I have this in my "sending" app:
func exportToURL (data : String) -> URL {
var input : String = ""
let url = self.getDocumentsDirectory().appendingPathComponent("FUNduroResult.fdre")
do {
try data.write(to: url, atomically: true, encoding: .utf8)
input = try String(contentsOf: url)
print(input)
print(url)
} catch {
print(error.localizedDescription)
}
return url
}
#IBAction func airdropButton(_ sender: Any) {
let text = formatDataAsCSV()
let url = exportToURL(data: text)
let activity = UIActivityViewController(
activityItems: ["Check out this result for the FUNduro.", url],
applicationActivities: nil
)
activity.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItem
present(activity, animated: true, completion: nil)
}
I have this in the AppDelegate of my receiving app:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if url.pathExtension == "fdre" {
print("got result")
}
return true
}
I have this in my receiving app for the document types:
Document Types
cheers kbjb
I created the DynamicLink for my firebase project when I am trying to receive the link I am getting "That's weird. My dynamic link object has no url".
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let incomingURL = userActivity.webpageURL{
print("Incoming URL is \(incomingURL)")
let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL)
{(dynamicLink, error) in
guard error == nil else{
print("Found an error! \(error!.localizedDescription)")
return
}
if let dynamicLink = dynamicLink{
self.handleIncomingDynamicLink(dynamicLink)
}
}
if linkHandled{
return true
}
else{
return false
}
}
return false
}
func handleIncomingDynamicLink(_ dynamicLink: DynamicLink){
guard let url = dynamicLink.url else{
print("That's weird. My dynamic link object has no url")
return
}
print("Your incoming link parameter is\(url.absoluteString)")
}
After checking all the blogs and posted this issue on firebase, I didn't find any solution for this but I came up with this concrete solution and it will work definitely
here: dynamicLinkURL is your main dynamic link and shortHandURL is your deeplink URL which is associated with your dynamic link. I hope the below snippet will help you.
func dynamicLinkhandler(_ dynamicLinkURL: URL, onCompletion: #escaping(_ success: Bool) -> Void) {
URLSession.shared.dataTask(with: dynamicLinkURL) { (data, response, error) in
guard error == nil else {
print("Found Error \(String(describing: error?.localizedDescription)))")
return
}
guard let shortHandURL = response?.url, shortHandURL != dynamicLinkURL else {
print("Thats Weird, my dynamic link has no URL")
onCompletion(false)
return
}
onCompletion(true)
}.resume()
}
Double check that the bundle id you set up in the dynamic link wizard creation within the firebase console it's the one you are running the app into.
I have three different bundle ids (dev, enterprise, production) and, for instance, if a set in the link the production bundle id but the app runs the dev bundle id, instead of returning back some error it returned an honest dynamicLink object but with a nil value in the url.
I am using AppAuth on my code.
I manage to authenticate successful , but when the SFSafariViewController gets dismiss from my Controller , the redirect url does not trigger the AppDelegate func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
The redirect URL is my Bundle Identifier name : BundleIdentifier://authenticate
I have setup in info.plist url Schemes and url identifier which they have the same name.
When I run my code setting a break point on this func I can see my redirect url correct for standarizedURL and standarizedRedirectURL
- (BOOL)shouldHandleURL:(NSURL *)URL {
NSURL *standardizedURL = [URL standardizedURL];
NSURL *standardizedRedirectURL = [_request.redirectURL standardizedURL];
return OIDIsEqualIncludingNil(standardizedURL.scheme, standardizedRedirectURL.scheme) &&
OIDIsEqualIncludingNil(standardizedURL.user, standardizedRedirectURL.user) &&
OIDIsEqualIncludingNil(standardizedURL.password, standardizedRedirectURL.password) &&
OIDIsEqualIncludingNil(standardizedURL.host, standardizedRedirectURL.host) &&
OIDIsEqualIncludingNil(standardizedURL.port, standardizedRedirectURL.port) &&
OIDIsEqualIncludingNil(standardizedURL.path, standardizedRedirectURL.path);
But when AppAuth finishes the authentication and I have an access token , func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool doesn't get triggered.
Any idea why?
Here is my code
class func signInAuth(discoveryURLstr: String,presenter : UIViewController,completionHandler: #escaping ( (OIDAuthState?,Error?) -> () )){
guard let discoveruURL = URL(string: discoveryURLstr) else{
completionHandler(nil,AuthErrors.InvalidDiscoveryURL)
return
}
appAuthDiscoverConfiguration(discoveryURL: discoveruURL) { (configurationFile, error) in
guard let configurationFile = configurationFile else {
completionHandler(nil,AuthErrors.InvalidConfigurationFile)
return
}
let authRequest = appAuthRequest(configurationFile: configurationFile)
self.appAuthenticationSession = OIDAuthState.authState(byPresenting: authRequest, presenting: presenter, callback: { (state, error) in
if let error = error {
//self.authState = nil
completionHandler(nil,error)
return
}
if let state = state {
self.authState = state
completionHandler(state,nil)
}else{
completionHandler(nil,AuthErrors.InvalideState)
}
})
}
}
class func appAuthDiscoverConfiguration(discoveryURL : URL, completionHandler: #escaping ((OIDServiceConfiguration?,Error?) -> ())) {
OIDAuthorizationService.discoverConfiguration(forDiscoveryURL: discoveryURL) { (configuration, error) in
if let error = error {
completionHandler(nil,error)
return
}else{
guard let configurationFile = configuration else {
completionHandler(nil,AuthErrors.InvalidConfigurationFile)
return
}
completionHandler(configurationFile,nil)
}
}
}
class func appAuthRequest(configurationFile : OIDServiceConfiguration) -> OIDAuthorizationRequest{
return OIDAuthorizationRequest(configuration: configurationFile, clientId: AppAuthConstants.clientId, clientSecret: nil, scope: AppAuthConstants.scope, redirectURL: AppAuthConstants.redirectURL, responseType: AppAuthConstants.responseType, state: nil, nonce: nil, codeVerifier: nil, codeChallenge: nil, codeChallengeMethod: nil, additionalParameters: AppAuthConstants.additionalParameters)
}
On iOS 12, App-Auth uses ASWebAuthenticationSession, and on iOS 11, it uses the now-deprecated SFAuthenticationSession instead of requiring the app to support handling the redirect manually. To support earlier versions of iOS, you still need your code in the func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool method.
For reference, you can see what AppAuth is doing under the covers here. Also, this is a great answer that explains how to generically get an OAuth token on iOS without using AppAuth.
I want to send parameter through dynamic link and also to receive the same.
I have passed the custom parameter through my short dynamic link. Here is my link: https://pc988.app.goo.gl/vQaV?test=1
And I am using the following code to receive the dynamic link:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let dynamicLink = DynamicLinks.dynamicLinks()?.dynamicLink(fromCustomSchemeURL: url){
self.handleIncomingDynamicLink(dynamicLink: dynamicLink)
return true
}
else{
let handled = FBSDKApplicationDelegate.sharedInstance().application(app, open: url, options: options)
return handled
}
}
#available(iOS 8.0, *)
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
if let incomingURL = userActivity.webpageURL{
let linkHandled = DynamicLinks.dynamicLinks()!.handleUniversalLink(incomingURL, completion:{ [weak self] (dynamiclink, error) in
guard let strongSelf = self else{ return }
if let dynamiclink = dynamiclink, let _ = dynamiclink.url {
strongSelf.handleIncomingDynamicLink(dynamicLink: dynamiclink)
}
})
return linkHandled
}
return false
}
func handleIncomingDynamicLink(dynamicLink: DynamicLink) {
if dynamicLink.matchConfidence == .weak{
}else {
guard let pathComponents = dynamicLink.url?.pathComponents else { return }
for nextPiece in pathComponents{
}
}
print("incoming link \(dynamicLink.url)")
}
And my exact problem was, I cannot get the 'test' parameter that I passed in dynamic short link which I mentioned above.
Help me to get rid off this problem.
To append custom parameter you need to append the parameter to the deep link, not to the dynamic link.
In your example the deep link is https://www.fitview.com/ (you can see this in debug page https://pc988.app.goo.gl/vQaV?d=1).
To accomplish your goal, set the deep link to https://www.fitview.com?test=1 , create dynamic link, and then shorten dynamic link.