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

iOS Support #10

Closed
hacker1024 opened this issue Jan 2, 2019 · 99 comments
Closed

iOS Support #10

hacker1024 opened this issue Jan 2, 2019 · 99 comments
Assignees
Labels
help wanted Extra attention is needed iOS

Comments

@hacker1024
Copy link
Contributor

I'm creating this issue to keep track of an iOS implementation. I myself am an Android developer, and I'd love to see an iOS developer contribute.

@ryanheise ryanheise added the help wanted Extra attention is needed label Jan 2, 2019
@ryanheise
Copy link
Owner

ryanheise commented Jan 5, 2019

To potential iOS contributors

This post describes the work to be done on the iOS side and what might be the best way to break it down.

Android and iOS differences

The architecture of this plugin was really dictated by the nature of Android's component system, so first let's try to understand why things must be the way they are if the plugin is to work on both Android and iOS. In Android, an app is made up of a set of individual components which may be dynamically spawned and destroyed over the lifetime of the app. An audio playing app needs an "Activity" component which is only allowed to run foreground code (i.e. the UI), and a "Service" component which is only allowed to run background code (i.e. playing audio). The idea is that the Activity could be swiped away by the user, i.e. destroyed and freed from memory, while the Service component is allowed to live on and play audio in the background. Flutter automatically sets up an isolate inside the activity to run your Dart code, but since this activity could be swiped away at any time, we can't put the audio playing code here. We therefore use a relatively new Flutter API to create our own isolate attached to the Service for running Dart code, and this is where the audio playing code needs to go. Flutter's API for background execution of Dart code is described in this Medium article.

iOS developers in the thread discussion below have pointed out that things fortunately may be a lot simpler on iOS since there is no such separation of foreground and background code. This may mean that the iOS implementation of the plugin would not need to use Flutter's background execution API. However, to main compatibility with Android's behaviour, I would suggest that the iOS implementation should still spawn a plain old isolate for running the background audio code. This would ensure call-by-value semantics is maintained on both platforms.

Architecture overview

This plugin sets up a Dart isolate for running audio playback code in a way that continues to run when the app is not in the foreground. The dart code that runs in this isolate is called the "background task". The architecture allows one or more "clients" to connect to the background task to control audio playback and be notified of state changes to update the UI. One of those clients is the Flutter user interface, but other clients may include a Bluetooth headset or a smart watch. There are distinct APIs for the Flutter client side and for the background task:

  • The AudioService API is for the Flutter client and allows for starting, stopping and sending/receiving messages to/from the background isolate.
  • The AudioServiceBackground API is for the background isolate and it dictates a set of callbacks that must be implemented to handle messages from the main isolate, and also provides methods to notify state updates back to all clients.

For example, AudioService.play() sends a message to the background task which is handled by its onPlay callback. But the message is not sent directly from point A to point B. Rather, it must go through a platform channel to the native plugin so that the plugin has a chance to do all of the things that a native app must typically do before playing audio. On Android, this involves acquiring audio focus and calling various APIs to allow the app to keep running while the app is not in the foreground and while the screen is turned off (acquiring a wake lock and starting a service) as well as showing a notification to indicate that the app is playing audio in the background. Once all of these things are done, the plugin THEN passes the message on to the Dart background task via its onPlay callback. Regardless of which client the play command originated from, the plugin should handle the command in the same way by performing any required native rituals to prepare for audio playback and then finally pass control to onPlay.

Stage 0: Foundation

A good starting point would be to implement the method call to start the background isolate, and to implement all other method calls either to directly pass through from one isolate to the other, or even implement them as no-ops. The only methods that absolutely need to work in this stage are:

  • start - tell the plugin to start the background isolate.
  • stopped - tell the plugin that the background isolate has completed.

With this much, the demo example app should start playing audio and will stop once the audio has run out by itself, but will not allow clients to control playback and will not notify the clients of state changes.

Stage 1: Basic functionality

The basic functionality provides the ability to control playback with the basic play/pause operations (which can also be controlled with a headset button click), and also the ability to stop the background task on request. The relevant method calls to implement are: connect, disconnect, ready, isRunning, setState/onPlaybackStateChanged, stop/onStop, pause/onPause, play/onPlay, setMediaItem/onMediaChanged and click/onClick. I'll be happy to answer questions about any of these in the comments below.

There are also a set of method calls that allow jumping to different positions and tracks that don't require any special set up on the native side so all they do is forward the messages on to the background task. They are: seekTo/onSeekTo, skipToNext/onSkipToNext, skipToPrevious/onSkipToPrevious, fastForward/onFastForward and rewind/onRewind.

The plugin should also have a default case for custom actions (whose method names begin with "custom_". These are also just forwarded directly to the background task without any special setup on the native side.

Stage 2: Queue functionality

This functionality adds the ability to manipulate the playlist/queue and jump to an arbitrary media items. The relevant method calls are: addQueueItem/onAddQueueItem, addQueueItemAt/onAddQueueItemAt, removeQueueItem/onRemoveQueueItem, skipToQueueItem/onSkipToQueueItem and playFromMediaId/onPlayFromMediaId.

Stage 3: Browsing functionality

This adds the ability for clients to browse media items offered by the background task. The relevant methods are: setBrowseMediaParent, notifyChildrenChanged, onChildrenLoaded and onLoadChildren.

Addressing API differences between Android and iOS

When there are equivalent features on iOS and Android, the preference is to implement both sides with the same API. If things would be easier on the iOS side if the API were changed, please make the suggestion below! I would definitely prefer to change the API if it means that the Android and iOS APIs can be harmonised.

In cases where a purely iOS-specific feature is desirable, it can be added as long as it is named with an ios prefix.

@hacker1024
Copy link
Contributor Author

Stage 3: Browsing functionality
This adds the ability for clients to browse media items offered by the background task. The relevant methods are: setBrowseMediaParent, notifyChildrenChanged, onChildrenLoaded and onLoadChildren.

Read the Android docs to see what these methods should actually do: https://developer.android.com/guide/topics/media-apps/audio-app/building-a-mediabrowserservice

@alexelisenko
Copy link

alexelisenko commented Mar 18, 2019

I started working on the iOS implementation, but running into several issues.

The flutter background isolate example does not run on iOS.

https://medium.com/flutter-io/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124

13:57:40.809 1 info flutter.tools Launching lib/main.dart on iPhone XS Max in debug mode...
13:57:49.494 2 info flutter.tools Running pod install...
13:57:50.791 3 info flutter.tools Running Xcode build...
13:57:51.973 4 info flutter.tools Xcode build done.                                            1.2s
13:57:52.914 5 info flutter.tools Failed to build iOS app
13:57:52.914 6 info flutter.tools Error output from Xcode build:
13:57:52.914 7 info flutter.tools ↳
13:57:52.914 8 info flutter.tools Could not build the application for the simulator.
13:57:52.914 9 info flutter.tools Error launching application on iPhone XS Max.
13:57:52.914 10 info flutter.tools     ** BUILD FAILED **
13:57:52.914 11 info flutter.tools 
13:57:52.914 12 info flutter.tools 
13:57:52.914 13 info flutter.tools Xcode's output:
13:57:52.914 14 info flutter.tools ↳
13:57:52.914 15 info flutter.tools     === BUILD TARGET geofencing OF PROJECT Pods WITH CONFIGURATION Debug ===
13:57:52.914 16 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:26:8: error: unknown type name 'FlutterPluginRegistrantCallback'
13:57:52.914 17 info flutter.tools     static FlutterPluginRegistrantCallback registerPlugins = nil;
13:57:52.914 18 info flutter.tools            ^
13:57:52.914 19 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:40:38: error: expected a type
13:57:52.914 20 info flutter.tools     + (void)setPluginRegistrantCallback:(FlutterPluginRegistrantCallback)callback {
13:57:52.914 21 info flutter.tools                                          ^
13:57:52.914 22 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:125:42: warning: 'center' is deprecated: first deprecated in iOS 7.0 - Please see CLCircularRegion [-Wdeprecated-declarations]
13:57:52.914 23 info flutter.tools       CLLocationCoordinate2D center = region.center;
13:57:52.914 24 info flutter.tools                                              ^
13:57:52.914 25 info flutter.tools     In module 'CoreLocation' imported from /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.h:6:
13:57:52.914 26 info flutter.tools     /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.0.sdk/System/Library/Frameworks/CoreLocation.framework/Headers/CLRegion.h:78:56: note: 'center' has been explicitly marked deprecated here
13:57:52.914 27 info flutter.tools     @property (readonly, nonatomic) CLLocationCoordinate2D center API_DEPRECATED("Please see CLCircularRegion", ios(4.0, 7.0), macos(10.7, 10.10)) API_UNAVAILABLE(tvos);
13:57:52.914 28 info flutter.tools                                                            ^
13:57:52.914 29 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:142:20: warning: 'setAllowsBackgroundLocationUpdates:' is only available on iOS 9.0 or newer [-Wunguarded-availability]
13:57:52.914 30 info flutter.tools       _locationManager.allowsBackgroundLocationUpdates = YES;
13:57:52.914 31 info flutter.tools                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13:57:52.914 32 info flutter.tools     In module 'CoreLocation' imported from /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.h:6:
13:57:52.914 33 info flutter.tools     /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.0.sdk/System/Library/Frameworks/CoreLocation.framework/Headers/CLLocationManager.h:266:35: note: 'setAllowsBackgroundLocationUpdates:' has been explicitly marked partial here
13:57:52.914 34 info flutter.tools     @property(assign, nonatomic) BOOL allowsBackgroundLocationUpdates API_AVAILABLE(ios(9.0), watchos(4.0)) API_UNAVAILABLE(macos) API_UNAVAILABLE(tvos);
13:57:52.914 35 info flutter.tools                                       ^
13:57:52.914 36 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:142:20: note: enclose 'setAllowsBackgroundLocationUpdates:' in an @available check to silence this warning
13:57:52.914 37 info flutter.tools       _locationManager.allowsBackgroundLocationUpdates = YES;
13:57:52.914 38 info flutter.tools                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13:57:52.914 39 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:220:74: warning: sending 'const NSString *__strong' to parameter of type 'NSString * _Nonnull' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
13:57:52.914 40 info flutter.tools       NSMutableDictionary *callbackDict = [_persistentState dictionaryForKey:key];
13:57:52.914 41 info flutter.tools                                                                              ^~~
13:57:52.914 42 info flutter.tools     In module 'Foundation' imported from /Users/alex/Documents/project/FlutterGeofencing/example/ios/Pods/Headers/Public/Flutter/Flutter/FlutterBinaryMessenger.h:8:
13:57:52.914 43 info flutter.tools     /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSUserDefaults.h:93:73: note: passing argument to parameter 'defaultName' here
13:57:52.914 44 info flutter.tools     - (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName;
13:57:52.914 45 info flutter.tools                                                                             ^
13:57:52.914 46 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:220:24: warning: incompatible pointer types initializing 'NSMutableDictionary *' with an expression of type 'NSDictionary<NSString *,id> * _Nullable' [-Wincompatible-pointer-types]
13:57:52.914 47 info flutter.tools       NSMutableDictionary *callbackDict = [_persistentState dictionaryForKey:key];
13:57:52.914 48 info flutter.tools                            ^              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13:57:52.914 49 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:222:18: warning: incompatible pointer types assigning to 'NSMutableDictionary *' from 'NSDictionary *' [-Wincompatible-pointer-types]
13:57:52.914 50 info flutter.tools         callbackDict = @{};
13:57:52.914 51 info flutter.tools                      ^ ~~~
13:57:52.914 52 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:223:53: warning: sending 'const NSString *__strong' to parameter of type 'NSString * _Nonnull' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
13:57:52.914 53 info flutter.tools         [_persistentState setObject:callbackDict forKey:key];
13:57:52.914 54 info flutter.tools                                                         ^~~
13:57:52.914 55 info flutter.tools     In module 'Foundation' imported from /Users/alex/Documents/project/FlutterGeofencing/example/ios/Pods/Headers/Public/Flutter/Flutter/FlutterBinaryMessenger.h:8:
13:57:52.914 56 info flutter.tools     /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSUserDefaults.h:82:57: note: passing argument to parameter 'defaultName' here
13:57:52.914 57 info flutter.tools     - (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
13:57:52.914 58 info flutter.tools                                                             ^
13:57:52.914 59 info flutter.tools     /Users/alex/Documents/project/FlutterGeofencing/ios/Classes/GeofencingPlugin.m:231:46: warning: sending 'const NSString *__strong' to parameter of type 'NSString * _Nonnull' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
13:57:52.914 60 info flutter.tools       [_persistentState setObject:mapping forKey:key];
13:57:52.914 61 info flutter.tools                                                  ^~~
13:57:52.914 62 info flutter.tools     In module 'Foundation' imported from /Users/alex/Documents/project/FlutterGeofencing/example/ios/Pods/Headers/Public/Flutter/Flutter/FlutterBinaryMessenger.h:8:
13:57:52.914 63 info flutter.tools     /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSUserDefaults.h:82:57: note: passing argument to parameter 'defaultName' here
13:57:52.914 64 info flutter.tools     - (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
13:57:52.914 65 info flutter.tools                                                             ^
13:57:52.914 66 info flutter.tools     7 warnings and 2 errors generated.

If anyone knows how the above can be fixed, please let me know. In the meantime, I will working on a standalone audio_player_service plugin that does not use a background isolate, as it doesn't seem to be required for iOS.

Also, the audio_service project was created with an older ios template, which was also broken. I created a new repo and moved over the android code, which fixed it, so I recommend recreating this repo with the latest flutter plugin template.

@ryanheise
Copy link
Owner

Hi @alexelisenko and first thank you so much for trying to get the iOS side working.

I unfortunately don't own a Mac to test this on, however I did do a Google search for the missing type name "FlutterPluginRegistrantCallback" and found that apparently this type was only committed to git very recently: flutter/engine@1ba3295#diff-dc093c279508589dcc672b1f75d69a11

So your two options would be:

  1. Check out an older version of the FlutterGeofencing repo; or
  2. Upgrade to the latest version of the Flutter engine.

It is probably better to do the latter since there's a chance you'll want to use the latest APIs.

First up, of course:

flutter upgrade

If the plugin still doesn't work, then you need to switch to a channel that is updated more regularly. First, you can try the beta channel which is what I personally use:

flutter channel beta

The beta channel is updated once a month, so I'd expect it should have this missing datatype since it was added last month. If it doesn't have the missing data type yet, then you can try:

flutter channel dev

You could then switch back to beta within a month after the missing datatype has had time to make its way into beta.

Regarding the project template, I agree and would love to update it, although would you like me to generate a Swift or Objective C template?

@alexelisenko
Copy link

@ryanheise switching to the beta channel resolved the error, thanks.

I think you should go with Objective-C since it reduces the complexity of setup for the background execution. The swift template actually uses both swift and obj-c by adding bridge headers, which complicates the setup and examples that are available for background execution.

I am currently flushing out all the iOS requirements in a standalone xcode project before integrating into the plugin.

@docaohuynh
Copy link

Waiting for this implementation 👍

@ryanheise
Copy link
Owner

@alexelisenko you've become a very popular person :-)

Regarding the iOS template, I'll try to update it tonight.

I watched an introductory video about Objective C recently, and while it's still a bit foreign to me, I might try my hand at filling in the basic plugin structure in a separate branch (it's bound to have syntax errors though since I don't have a Mac / Xcode to test it on). Swift looks a bit easier to me, but I'll trust you that the bridge headers add complexity.

@alexelisenko
Copy link

After playing around with the Flutter background Dart execution, I abandoned that approach for iOS to keep my project deadlines. The good news is I do have a working audio service for iOS (For Audio and Video files), which I will share here soon (have a few tweaks to make before making it public).

This does mean that I had to resort to using things like if(platform.isIOS){}, but this was honestly the fastest route for my project. Once I publish my version (which does not use background dart code), anyone who needs this functionality can at least use it, but I do hope to have time to merge my work with this plugin, I just didn't have enough time to fiddle with the background dart implementation.

@ryanheise We could potentially create a wrapper interface for my iOS plugin, without adding the background dart execution. One of the main reasons I wrote my own plugin for iOS, was that the audioplayer plugin that is typically used with audio_service was not feature complete. The audio_player_service plugin I wrote is basically the service and player rolled into one, since iOS does not require much for background audio playback, so the background dart execution added alot of code and moving parts that aren't needed.

I will post the link to the plugin here, hopefully in the next few days.

@alexelisenko
Copy link

alexelisenko commented Apr 3, 2019

Here is the iOS plugin:
https://github.com/alexelisenko/audio_player_service

I would consider this in BETA. While it does work, it is lacking in documentation and thorough testing.

The example project does give you everything you need to use it, but it does not include examples of all the methods available, so some code reading will be required for some use cases.

Please keep in mind that I will be making updates to this repo, which may or may not be breaking changes.

EDIT I still intend to integrate this into audio_service, may need to have a different interface for iOS, or create the same interface with some of the methods doing nothing. Once Im done with my active project I will circle back to this, but for now, I need to get my project done :)

@ryanheise
Copy link
Owner

Excellent! I fully expect iOS to have a different, though partially overlapping, set of features, so it will be interesting to get an iOS developer's input to help shape the audio_service API.

Does your plugin handle button clicks on the headset to play/pause media playback, and if so, which part of the code is responsible for it? I'd be interested to know if this is just handled automatically, or whether iOS gives you the flexibility to handle these button clicks with your own callbacks. After a stack overflow search, I found this (https://stackoverflow.com/questions/9797562/iphone-headset-play-button-click) which gives me hope that it's possible.

@alexelisenko
Copy link

alexelisenko commented Apr 3, 2019

iOS uses whats called the CommandCenter to display media metadata and provide controls.

The plugin supports the following

togglePlayPauseCommand
playCommand
pauseCommand
stopCommand
nextTrackCommand
previousTrackCommand
changePlaybackPositionCommand (seeking from command center/ external devices)

The unimplemented commands available are:

[commandCenter.skipForwardCommand setEnabled:NO];
[commandCenter.skipBackwardCommand setEnabled:NO];
[commandCenter.enableLanguageOptionCommand setEnabled:NO];
[commandCenter.disableLanguageOptionCommand setEnabled:NO];
[commandCenter.changeRepeatModeCommand setEnabled:NO];
[commandCenter.seekForwardCommand setEnabled:NO];
[commandCenter.seekBackwardCommand setEnabled:NO];
[commandCenter.changeShuffleModeCommand setEnabled:NO];
        
// Rating Command
[commandCenter.ratingCommand setEnabled:NO];
        
// Feedback Commands
 // These are generalized to three distinct actions. Your application can provide
// additional context about these actions with the localizedTitle property in
// MPFeedbackCommand.
[commandCenter.likeCommand setEnabled:NO];
[commandCenter.dislikeCommand setEnabled:NO];
[commandCenter.bookmarkCommand setEnabled:NO];

The above can be added very easily.

I have currently tested this with various bluetooth headphones and over bluetooth audio in several cars

As far as what code is responsible, look at this file: https://github.com/alexelisenko/audio_player_service/blob/master/ios/Classes/AudioPlayer.m

The init method sets up the CommandCenter with the callbacks.

NOTE: The iOS simulator does not show the command center, so it has to be tested on a device, unfortunately.

@ryanheise
Copy link
Owner

So then when you click the headset button, does iOS itself maintain a play/pause toggle state and flip it on a click? That would be unfortunate, since what I'm really hoping for is to be able to just listen to button click events and allow the application to process them and manage its own state.

@alexelisenko
Copy link

alexelisenko commented Apr 3, 2019

Each of the command I listed requires a callback, which are implemented in the plugin. The state is managed by the plugin entirely, meaning it's the plugins job to decide what to do with the command events.

@adriancmurray
Copy link

adriancmurray commented Apr 3, 2019 via email

@alexelisenko
Copy link

@adriancmurray Yes, the plugin actually requires that you initialize a Queue of items (which can be just one item). The plugin does not automatically play the next item in the queue, although this could be added easily. Im currently playing the next item in the queue in the actual flutter project using the plugin by watching that playback state and running next as needed.

@adriancmurray
Copy link

adriancmurray commented Apr 3, 2019 via email

@alexelisenko
Copy link

@adriancmurray There is one known bug with this though:

When supplying multiple items in the queue, any bluetooth devices that display the queue position, i.e. 2/10 etc, do not display the correct index, Im still working on a fix

This is related to not being able to easily go to the previous item, and having to reinit the queue, which screws up the metadata for external devices that rely on this data.

This does not affect devices that do not look for this data, or general playlist playback via command center

@ryanheise
Copy link
Owner

Ah, nice! So for example, on receiving a togglePlayPauseCommand, the application in theory has the power to decide based on runtime conditions to ignore the click without necessarily running the risk of falling out of sync with iOS's internal toggle state (because there is no iOS internal toggle state).

Well... It sounds like you have all of the features I would need for my own use case! 👍

@adriancmurray
Copy link

adriancmurray commented Apr 3, 2019 via email

@alexelisenko
Copy link

@ryanheise Yes, if the callback does not actually play or pause the Player, the command center will not flip the play/pause button state. The state is provided to the OS via the nowPlayingInfo property of the CommandCenter. This property is updated by the plugin whenever the plugin state changes.

@ryanheise
Copy link
Owner

I see, that is more or less the way it also works in Android, so it should fit nicely into the current plugin API.

I've just copied the latest iOS template across to audio_service and will look more in depth at your iOS code tomorrow.

@hacker1024
Copy link
Contributor Author

@alexelisenko Interesting.
I'm actually going to publish my own audio player plugin soon, which is designed for queuing URLs. Perhaps, when I publish it, you could decouple you media player code and move it into there? (It's a really simple plugin, just all your usual media and queue management functions to implement).

That way you're one step closer to the way this plugin works.

@alexelisenko
Copy link

@hacker1024 What do you mean by queuing URLs?

@hacker1024
Copy link
Contributor Author

hacker1024 commented Apr 3, 2019

@alexelisenko The plugin maintains a list of audio URLs to stream and will play through them. It preloads upcoming queue items, which is what I couldn't get any other plugin to do.

EDIT: The Android implementation uses ExoPlayer's ConcatentatingMediaSource.

@alexelisenko
Copy link

@hacker1024 So if you pass it a list of URLs, it will download the, then load them from local filesystem?

@hacker1024
Copy link
Contributor Author

@alexelisenko Yes, but to RAM or a cache only. The downloads aren't made to be accessed.

@ZheGuangZeng
Copy link

Great Job Guys. Waiting...

@adriancmurray
Copy link

For those waiting for background audio plugins... might I suggest just writing it yourself. I was hoping for a plugin to be made and briefly used one mentioned on here earlier in the thread (as in I ran my own code on it for about 5 hours until I realized it wasn't going to offer what I needed). My needs are rather specific so I decided to code one myself in swift. It's not that difficult and I had never coded in swift up until writing my own plug in. This allows you a whole lot more freedom in how you implement the native APIs (and there are many for audio). I tried to alter some of the existing plugins for iOS but they all use Objective C, which kind of feels like reading a book written in the 1800's when you're used to languages like Dart/Javascript, so I used Swift. This also allows you to have a tighter coupling between your dart code and the native APIs. Hope this helps. Thanks to everyone who has been working on audio plugins. Cheers!

@ryanheise ryanheise self-assigned this Dec 17, 2019
@ryanheise ryanheise added the iOS label Dec 17, 2019
@mrxten
Copy link

mrxten commented Jan 7, 2020

Hi, would be pretty good, if via your plugin we can configure hide/show NextTrack, PreviousTrack, SeekToPlaybackPosition for iOS notification.

@bardiarastin
Copy link

@ryanheise Hi, thanks for all awesome work you're putting on this I wanted to know how are things going with remaining ios stuff?

image

@ryanheise
Copy link
Owner

Thank you, @bardiarastin

There are some other items on my priority list right, such as Android v2 support, and also web support for just_audio, although I do also welcome pull requests especially on the iOS side to help the project move forward more swiftly (no pun intended!).

@YaarPatandarAA
Copy link
Contributor

Using the Example on a physical iPhone iOS 13. I don't see the command center, is the example old and not updated to use command center?

@ryanheise
Copy link
Owner

I don't have a physical iPhone, but my understanding was that the command center is always accessible and there is nothing my plugin can do to make it inaccessible. Please correct me if this is not the case, as I don't have a physical device to check. Alternatively, do you mean you can see the command center, but you just don't see any media displayed on it when you call AudioServiceBackground.setMediaItem ?

@YaarPatandarAA
Copy link
Contributor

YaarPatandarAA commented Feb 16, 2020

The command center is there but it doesn’t show the current playing media. It is just blank and interactions with the buttons don’t do anything.

I am using the Example app from this code base. I have not altered it in any way. Just installing it on my phone.

I do see from previous comments that the command center does indeed work.

@minhdev2017
Copy link

ios not show music control bar, how to fix please,
sr my bad english :v

@YaarPatandarAA
Copy link
Contributor

YaarPatandarAA commented Feb 20, 2020

@ryanheise after some checking of old commits the iOS Command Center broke at commit 7578d7745ee7546302f5f7317575be9740279518

The change that broke it is changing flutter_tts: ^0.7.0 to flutter_tts: ^0.8.5. Reverting to the old version fixes the Example App.

I tested on a Physical iPhone 11 Pro Max iOS 13.3.1 with Flutter:

[✓] Flutter (Channel stable, v1.12.13+hotfix.8, on Mac OS X 10.15.1 19B88, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.3.1)
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.42.0)

@ryanheise
Copy link
Owner

ryanheise commented Mar 7, 2020

@YaarPatandarAA , that's certainly interesting, thanks for isolating the specific plugin and version.

I don't see anything obviously problematic with flutter_tts 0.8.5's code, though. Perhaps it is related to its build/configuration files.

Note: This particular issue is now being tracked in #202 .

I am closing this "iOS Support" issue finally, now that it has for the most part served its original purpose. From now on I would encourage people to open new issues for specific iOS features or bugs. It almost feels sad to close it after such a long journey, and with all of the suggestions and contributions from you all. It was a true community effort. Thank you!

@YaarPatandarAA
Copy link
Contributor

YaarPatandarAA commented Apr 24, 2020

Not sure if this counts as Apple CarPlay support but here is this package working on Apple CarPlay. The shown buttons also work, this screen is under Now Playing app on Apple CarPlay. I know there is way more that can be done with Apple CarPlay than just this, but it's a start. 👍🏻

20200423_194851

@snaeji
Copy link
Contributor

snaeji commented May 11, 2020

@YaarPatandarAA can you tell me what this package is? It's just bold not hyperlinked 😄

@YaarPatandarAA
Copy link
Contributor

YaarPatandarAA commented May 11, 2020

@YaarPatandarAA can you tell me what this package is? It's just bold not hyperlinked 😄

This package refers to this package on which we are commenting on, in this packages issue.
https://pub.dev/packages/audio_service

@snaeji
Copy link
Contributor

snaeji commented May 11, 2020

@YaarPatandarAA Thanks for clearing that! I was reading it in a completely different way 👍

@ryanheise
Copy link
Owner

Ah, that makes perfect sense :-) I also had the same misunderstanding as @snaeji

This plugin might now actually have better support for Apple CarPlay than for Android Auto, which is an interesting twist.

@nazdream
Copy link

nazdream commented May 5, 2021

Hey there. I am working on a project which requires bookmarking functionality for both iOS and Android. I am wondering, has anyone managed to add "bookmarkCommand" for iOS?
I would be very thankful if there is anyone who did that and can share their experience.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
help wanted Extra attention is needed iOS
Projects
None yet
Development

No branches or pull requests