Objective-C/Swift SDK,用于从 macOS 与 Allihoopa 交互。
如果您正在开发 iOS 应用,可以查看我们的 iOS SDK。
根据您的设置,安装 Allihoopa SDK 有多种方式。
如果您使用 CocoaPods,可以简单地将此 SDK 添加到您的 Podfile
target 'TargetName' do
pod 'Allihoopa-macOS', '~> 1.2.1'
end
然后运行以下命令以下载依赖项
pod install
完成此操作后,您运行 carthage
以构建框架,然后将位于 Carthage/Build
文件夹中的 Allihoopa.framework
拖到您的项目中。
或者,您可以简单地从 Releases 标签 下载最新构建的框架。
如果您愿意,可以将 Allihoopa-macOS
项目作为子项目包含到您的应用程序中,并将框架作为依赖项构建。在这种情况下,由于该项目与 iOS SDK 共享大量代码和功能,您还需要包含 AllihoopaCore-ObjC 作为依赖项。
如果您使用上述任何一种方法,此依赖项将自动为您管理。
您需要将一个 URL 方案添加到应用程序的 Info.plist
中:ah-{APP_IDENTIFIER}
,例如,如果您的应用程序标识为 figure
,则为 ah-figure
。当您在 Allihoopa 中注册应用程序时,您将收到您的应用程序标识和 API 密钥。如果要开始加入,请发送电子邮件到 [email protected]。
请查看 SDKExample 文件夹以了解如何使用此 SDK。
最新发布的 API 文档可在 http://cocoadocs.org/docsets/Allihoopa-macOS 上找到。
你需要一个类,例如你的应用代理,来实现AHAAllihoopaSDKDelegate
以支持“打开”功能。下面是如何实现此功能的说明。
import Allihoopa
// In your UIApplicationDelegate implementation
func applicationDidFinishLaunching(_ aNotification: Notification) {
AHAAllihoopaSDK.shared().setup([
.applicationIdentifier: "your-application-identifier",
.apiKey: "your-api-key",
.delegate: self,
])
}
#import <Allihoopa/Allihoopa.h>
// In your UIApplicationDelegate implementation
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
[[AHAAllihoopaSDK sharedInstance] setupWithConfiguration:@{
AHAConfigKeyApplicationIdentifier: @"your-application-identifier",
AHAConfigKeyAPIKey: @"your-api-key",
AHAConfigKeySDKDelegate: self,
}];
}
setupWithConfiguration:
必须在调用任何其他API之前调用。如果SDK设置不正确,将抛出异常:如果缺少凭证或没有正确设置URL方案。有关更多信息,请参阅上面“设置SDK”部分。
配置字典支持以下键 - Objective-C与Swift中的名称
AHAConfigKeyApplicationIdentifier
/.applicationIdentifier
:必需的由Allihoopa提供的应用标识符的字符串。AHAConfigKeyAPIKey
/.apiKey
:必需的包含应用程序API密钥的字符串。AHAConfigKeySDKDelegate
/.sdkDelegate
:可选的实例,用于在用户尝试将物品导入应用时通知应用程序。如果提供,该实例必须符合AHAAllihoopaSDKDelegate
协议。AHAConfigKeyFacebookAppID
/.facebookAppID
:可选的包含应用程序的Facebook App ID的字符串。这将启用通过iOS内置的Accounts和Social框架进行二级社交分享。AHAConfigKeyRegisterURLEventHandler
/.registerURLEventHandler
:可选的布尔值,指示SDK是否应注册自己的URL打开处理器。为方便起见,默认为true
,因此如果你想要自己的URL事件处理器,**必须将其设置为false**。如果你将其设置为false
,你必须在你的事件处理器中调用-handleOpenURL:(NSURL*)
/handleOpen(url)
。let piece = try! AHADropPieceData(
defaultTitle: "Default title", // The default title of the piece
lengthMicroseconds: 40000000, // Length of the piece, in microseconds
tempo: nil,
loopMarkers: nil,
timeSignature: nil,
basedOn: [])
let vc = AHAAllihoopaSDK.shared().dropViewController(forPiece: piece, delegate: self)
self.presentViewControllerAsSheet(vc)
extension ViewController : AHADropDelegate {
// The drop view controller will ask your application for audio data.
// You should perform work in the background and call the completion
// handler on the main queue. If you already have the data available,
// you can just call the completion handler directly.
//
// This method *must* call completion with a data bundle for the drop to
// succeed. If it doesn't, an error screen will be shown.
func renderMixStem(forPiece piece: AHADropPieceData, completion: @escaping (AHAAudioDataBundle?, Error?) -> Void) {
DispatchQueue.global().async {
// Render Wave data into an NSData object
let bundle = AHAAudioDataBundle(format: .wave, data: data)
DispatchQueue.main.async {
completion(bundle, nil)
}
}
}
// You are responsible for dismissing the drop view controller in a
// suitable way, matching how you presented it.
func dropViewController(_ sender: NSViewController, willClose piece: AHADropPieceData, successfulDrop: Bool) {
self.dismissViewController(sender)
}
}
NSError* error;
AHADropPieceData* piece = [[AHADropPieceData alloc] initWithDefaultTitle:@"Default title"
lengthMicroseconds:40000000
tempo:nil
loopMarkers:nil
timeSignature:nil
basedOnPieceIDs:@[]
error:&error];
if (piece) {
UIViewController* vc = [[AHAAllihoopaSDK sharedInstance] dropViewControllerForPiece:piece delegate:self];
[self presentViewControllerAsSheet:vc];
}
@implementation ViewController
- (void)renderMixStemForPiece:(AHADropPieceData *)piece
completion:(void (^)(AHAAudioDataBundle * _Nullable, NSError * _Nullable))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Render wave data into an NSData object
AHAAudioDataBundle* bundle = [[AHAAudioDataBundle alloc] initWithFormat:AHAAudioFormatWave data:data];
dispatch_async(dispatch_get_main_queue(), ^{
completion(bundle, nil);
});
});
}
- (void)dropViewController:(NSViewController*)sender
forPieceWillClose:(AHADropPieceData*)piece
afterSuccessfulDrop:(BOOL)successfulDrop {
[self dismissViewController:sender];
}
@end
dropViewController
创建一个视图控制器,它将使用委托对象帮助投放你提供的物品。如果用户未登录,SDK将在用户的默认浏览器中打开登录对话框,并在用户登录后继续。当用户想关闭投放视图时,委托将被通知,并且必须解散视图控制器。
一个物品可以包含不同类型的元数据。上面的示例显示了我们所需要的最小数据量:默认标题、物品音频数据的长度以及将音频渲染成已知格式的委托方法。
AHADropPieceData
会对输入执行基本验证:它将返回包含错误信息的NSError
。错误可能包括标记倒置或长度超出合理范围等问题。这些基本上是程序员错误 - 而不是可以以有意义方式处理的运行时错误。
如果你的应用程序了解这一点,它可以向AHADropPieceData
提供更多信息,以及在AHADropDelegate
上实现比上面显示更多的方法。以下是一个完整示例,显示了你可以设置的所有数据。
let piece = try! AHADropPieceData(
defaultTitle: "Default title",
lengthMicroseconds: 100000000,
// The fixed tempo in BPM. Allowed range: 1 - 999.999 BPM
tempo: AHAFixedTempo(fixedTempo: 128),
// If the piece is a loop, you can provide the loop markers.
loopMarkers: AHALoopMarkers(startMicroseconds: 0, endMicroseconds: 500000),
// If the time signature is available and fixed, you can provide a time
// signature object.
//
// The upper numeral can range from 1 to 16, incnlusive. The lower numeral
// must be one of 2, 4, 8, and 16.
timeSignature: AHATimeSignature(upper: 8, lower: 4),
// If the piece is based on other pieces, provide a list of the IDs of those
// pieces here.
basedOn: [],
// If the piece has a known tonality, provide the scale and root here.
// Other values are AHATonality.unknown() (default if omitted), and
// AHATonality.atonal() for pieces that contain audio that doesn't have a
// tonality, e.g. drum loops.
tonality: AHATonality(tonalScale: AHAGetMajorScale(4), root: 4)
)
extension ViewController : AHADropDelegate {
// The "mix stem" is the audio data that should be used to place the piece
// on a timeline. Call the completion handler with a data bundle instance
// on the main queue when data is available.
//
// The mix stem is mandatory.
func renderMixStem(forPiece piece: AHADropPieceData, completion: @escaping (AHAAudioDataBundle?, Error?) -> Void) {
}
// You can supply a default cover image that the user can upload or change.
// Call the completion handler with an image of size 640x640 px, or nil.
func renderCoverImage(forPiece piece: AHADropPieceData, completion: @escaping (UIImage?) -> Void) {
completion(nil)
}
// If the audio to be placed on the timeline is different from what users
// should listen to, use this delegate method to provide a "preview"
// audio bundle.
//
// For example, if you're providing a short loop you can supply only the
// loop data in a lossless format as the mix stem, and then a longer track
// containing a few loops with fade in/out in a lossy format in the
// preview audio.
//
// The preview audio is what's going to be played on the website.
//
// If no preview audio is provided, the mix stem will be used instead. This
// replacement is done server-side, the mix stem data will only be uploaded
// once from the client.
func renderPreviewAudio(forPiece piece: AHADropPieceData, completion: @escaping (AHAAudioDataBundle?, Error?) -> Void) {
}
// You can include an application specific attachment, which can be of
// arbitrary file type referred by a registered MIME type
func renderAttachment(forPiece piece: AHADropPieceData, completion: @escaping (AHAAttachmentBundle?, Error?) -> Void) {
}
}
有关音调和其他字段的更多信息,请参阅iOS SDK文档。
当用户在网站中选择“在[你的应用]中打开”时,SDK将获取请求,获取物品元数据,并调用openPieceFromAllihoopa:error:
,其中包括用户想要打开的物品。AHAPiece
实例有用于下载指定格式的音频数据的方法,你可以使用这些方法将音频导入当前文档或保存以供以后使用。AHAPiece
还包含与AHADropPiece
类似的元数据。
func openPiece(fromAllihoopa piece: AHAPiece?, error: Error?) {
if let piece = piece {
// The user wanted to open a piece
// Download the mix stem audio in Ogg format. You can also use .wave
piece.downloadMixStem(format: .oggVorbis, completion: { (data, error) in
if let data = data {
// Data downloaded successfully
}
})
}
else {
// Handle the error
//
// This should not *usually* happen, but if the piece was removed after
// opening, or if there were some connection issues we can end up here
}
}
- (void)openPieceFromAllihoopa:(AHAPiece*)piece error:(NSError*)error {
if (piece != nil) {
// The user wanted to open a piece
[piece downloadMixStemWithFormat:AHAAudioFormatOggVorbis completion:^(NSData* data, NSError* error) {
if (data != nil) {
// Data downloaded successfully
}
}];
}
else {
// Handle the error
}
}
您还可以在降落碎片时包含特定应用的附件,并在导入碎片时类似地下载该附件。附件可以是任意文件类型,并通过MIME类型字符串引用。必须在Allihoopa服务中预先注册MIME类型,即如果您应用程序需要使用附件,请通过发送电子邮件至[email protected]进行注册。
特定应用附件可以包含在降落代理对象中。
extension ViewController : AHADropDelegate {
// ...
// You can include an application specific attachment along other asset files
func renderAttachment(forPiece piece: AHADropPieceData, completion: @escaping (AHAAttachmentBundle?, Error?) -> Void) {
DispatchQueue.global().async {
do {
let data = try Data(contentsOf: Bundle.main.url(forResource: "figure_song", withExtension: "xml")!)
let bundle = AHAAttachmentBundle(mimeType: "application/figure", data: data)
DispatchQueue.main.async {
completion(bundle, nil)
}
}
catch let error {
DispatchQueue.main.async {
completion(nil, error)
}
}
}
}
}
导入碎片时,可以使用相同的MIME类型下载附件。
func openPiece(fromAllihoopa piece: AHAPiece?, error: Error?) {
if let piece = piece {
// ...
// Download an attachment based on specific MIME type
piece.downloadAttachment(mimeType: "application/figure", completion: { (data, error) in
if let data = data {
// Data downloaded successfully
}
})
}
}