Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 34c1f493ae | |||
| 30750a0c81 | |||
| e57cf9d2e5 | |||
| 0eeedc6467 | |||
| e96cbf6337 | |||
| debc1c519f | |||
| 09bec023a9 | |||
| 44e022389c | |||
| 05ca97b8eb | |||
| f6ff2b4cc0 | |||
| 9e3f5c0291 | |||
| 8ba40ca45f | |||
| 9919bde9fe | |||
| 3ba62d8657 | |||
| 0f283b171f | |||
| f6b5e30e85 | |||
| 04296fa681 | |||
| 252ed947d2 | |||
| b5fdb5c54e | |||
| 1b4c0b0d3b | |||
| e0aa2a09a9 | |||
| ab0eb4f8eb | |||
| 99e7c65bbc | |||
| 9072259631 | |||
| eb9af1007a | |||
| 69d3a9c0c0 | |||
| 43821c68a9 | |||
| 610ff4c7f3 | |||
| 1fc533214f | |||
| 71f22c3e25 | |||
| 8a5e6d18cc | |||
| a7f53bfec9 | |||
| 7b10f0476b | |||
| 618da75339 | |||
| 0694f5d8a0 | |||
| 61268b45eb | |||
| 962e64fe24 | |||
| 0f9b2656a1 | |||
| 5e871fc7e2 | |||
| fdf8fc9482 | |||
| a9eb713964 | |||
| a0efa5f408 | |||
| 74cafd4c42 | |||
| 53780ac03e | |||
| ba438c8ede | |||
| 06cb6577c1 | |||
| 5a0f379275 | |||
| 11e793f963 | |||
| d094067ac7 | |||
| 4bacd5f1ff | |||
| 73089bbe8b | |||
| b4f919e8f4 | |||
| 7e33789644 | |||
| 843ba9f450 | |||
| 4305110867 | |||
| cd43ecc6f9 | |||
| 274377af3f | |||
| ef41885eaf | |||
| 21da2da43b | |||
| 336d5586bf | |||
| e917867220 | |||
| 7897a79a79 | |||
| 43824a9700 | |||
| 460af7ab1e | |||
| 670bf0e09e | |||
| 238c02db12 | |||
| 20190152eb | |||
| 9e6683674b | |||
| b27332aafb | |||
| 18688ab766 | |||
| 6835738754 | |||
| 9f05915993 | |||
| 2f9a5481ff | |||
| 079f8c8ce1 | |||
| 720b4739b4 | |||
| 2b150b5652 | |||
| 4adc84aaf3 | |||
| 76f2d22e7a | |||
| 3c409200ee | |||
| b44777fcd7 | |||
| d682fd9468 | |||
| 10e6c46c18 | |||
| 89715d9d38 | |||
| 99bd43769c | |||
| ce5e5e886f | |||
| 521141ba0d | |||
| d3d354d5bd | |||
| 664c56b79c | |||
| 7bb83e87e0 | |||
| 6cad96b4f8 | |||
| 50a58c2306 | |||
| 2dbaf3d4dc | |||
| 851d8704d9 | |||
| 7c4dc27868 |
+1
-1
@@ -1 +1 @@
|
||||
4.0
|
||||
4.2
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
# * http://www.objc.io/issue-6/travis-ci.html
|
||||
# * https://github.com/supermarin/xcpretty#usage
|
||||
|
||||
osx_image: xcode9.4
|
||||
osx_image: xcode10.1
|
||||
language: swift
|
||||
cache: cocoapods
|
||||
podfile: Example/Podfile
|
||||
@@ -10,7 +10,7 @@ before_install:
|
||||
- gem install cocoapods # Since Travis is not always on latest version
|
||||
- pod install --project-directory=Example --repo-update
|
||||
script:
|
||||
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/SwiftAudio.xcworkspace -scheme SwiftAudio-Example -sdk iphonesimulator11.4 -destination "OS=11.4,name=iPhone X" | xcpretty
|
||||
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/SwiftAudio.xcworkspace -scheme SwiftAudio-Example -sdk iphonesimulator12.1 -destination "OS=11.4,name=iPhone X" | xcpretty
|
||||
- pod lib lint
|
||||
|
||||
after_success:
|
||||
|
||||
+80
-42
@@ -12,6 +12,15 @@
|
||||
0262F9E0C329E749D1B3480EBF568E48 /* Quick-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 25DBE2FD6322DB92AF73F714DBCDAC39 /* Quick-dummy.m */; };
|
||||
03D7EA5C17FCEB40D10C3C73F9472F4E /* NimbleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194F331624F24ECBAEF52731D29FAD3E /* NimbleEnvironment.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
04AA9A137A0D38056D1081B4B304CFB6 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9753520784E15A0CF31F3233F37520 /* Predicate.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
075131AD2182FF1600D3BFB9 /* AVPlayerWrapperProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075131AC2182FF1600D3BFB9 /* AVPlayerWrapperProtocol.swift */; };
|
||||
075131AF21830D9500D3BFB9 /* AVPlayerWrapperDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075131AE21830D9500D3BFB9 /* AVPlayerWrapperDelegate.swift */; };
|
||||
075131B9218322E000D3BFB9 /* MediaItemProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075131B3218322E000D3BFB9 /* MediaItemProperty.swift */; };
|
||||
075131BA218322E000D3BFB9 /* NowPlayingInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075131B4218322E000D3BFB9 /* NowPlayingInfoController.swift */; };
|
||||
075131BB218322E000D3BFB9 /* NowPlayingInfoProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075131B5218322E000D3BFB9 /* NowPlayingInfoProperty.swift */; };
|
||||
075131BC218322E000D3BFB9 /* RemoteCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075131B7218322E000D3BFB9 /* RemoteCommand.swift */; };
|
||||
075131BD218322E000D3BFB9 /* RemoteCommandController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075131B8218322E000D3BFB9 /* RemoteCommandController.swift */; };
|
||||
07756B74218C2D590023935E /* AudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07756B71218C2D590023935E /* AudioSession.swift */; };
|
||||
07756B75218C2D590023935E /* AudioSessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07756B72218C2D590023935E /* AudioSessionController.swift */; };
|
||||
089FC967E3D8DD6212509D9C6AC7185B /* CwlCatchException.m in Sources */ = {isa = PBXBuildFile; fileRef = 25045F70284E802FFFF0F6EFD40A01C7 /* CwlCatchException.m */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
08EA01BACA4194902667F1FF29DC6265 /* MatcherProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945BF3F9EA32D635660DD7902B1A3D03 /* MatcherProtocols.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
095BD8D416719E425189E8E4963A8D4E /* TimeEventFrequency.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF4534C7E47CA5B502A19513117CBFC3 /* TimeEventFrequency.swift */; };
|
||||
@@ -25,12 +34,10 @@
|
||||
15FA146C0756A295AE40D1FBD77ABB6B /* AllPass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD70D6C69732CC5E9DE6DC55873B188 /* AllPass.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
19FB07979AE2C3531B7F249AA237B30D /* BeGreaterThanOrEqualTo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC94BD467B9F8E22E297008546EB659 /* BeGreaterThanOrEqualTo.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
1B1A26FD4363A19DEFED48BF5E5341BE /* NMBExceptionCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = D73E015835BBDFB245780DE9E43ED9FE /* NMBExceptionCapture.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
1CAA71BDD1BCB7A8EBD8A890526774C6 /* RemoteCommandController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 689190491F4225B281ABC68343A24D61 /* RemoteCommandController.swift */; };
|
||||
1CFDB94DFF57F98BA7A40829DDB3CFC0 /* CwlPreconditionTesting.h in Headers */ = {isa = PBXBuildFile; fileRef = 20E6435A404A70F289B347C5815ABC10 /* CwlPreconditionTesting.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
1D230EC73A17C8494F279447636B6D51 /* Nimble.h in Headers */ = {isa = PBXBuildFile; fileRef = 572C9B621320D273AC417E7C04066AEA /* Nimble.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
1DC86A0AEBBA8A5B9353C4F7B2D4649A /* SwiftAudio-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B6541C48548E76AC573D95227051A3E8 /* SwiftAudio-dummy.m */; };
|
||||
1F626496F8B9ACBEA1C48D85D371790E /* ExampleMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D0BC856D7B86412A484A3DD582F9841 /* ExampleMetadata.swift */; };
|
||||
213745669894F37CD02BED396AC2EFFA /* AudioSessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006367118F9218EE3420DCEF343E7B4C /* AudioSessionController.swift */; };
|
||||
2722196E68C16CB6AE1C739A6632DDDD /* ToSucceed.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08DE2F262516A80F255EE2310F0EDD0 /* ToSucceed.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
275B03F03D701555A341DEFDA586D3F2 /* Nimble-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ADDE4EA63584E9BF3933026B32B66E2 /* Nimble-dummy.m */; };
|
||||
2A561B51439F9724CF97415032FFB649 /* NMBObjCMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDAE77638703F05830D6AF66FBCAE75 /* NMBObjCMatcher.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
@@ -79,17 +86,13 @@
|
||||
838A64480DA58965581A1750F3FED016 /* World+DSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB337D9B97833B31DBEAB7D4FB2AE0E /* World+DSL.swift */; };
|
||||
83C035F188A4F80F9696E6AD261DF00B /* Pods-SwiftAudio_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3350F0F9B5DDF05AE230A91E5FD38686 /* Pods-SwiftAudio_Tests-dummy.m */; };
|
||||
83F282C7E57F327D439604EB838FAA92 /* URL+FileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F09E00015A51CF7CCAC20DF7144AE44 /* URL+FileName.swift */; };
|
||||
8A77D5CE0D8380C1FD2FE859015C7DAA /* NowPlayingInfoProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4609750B9014770E50A7FF8F13C08BE9 /* NowPlayingInfoProperty.swift */; };
|
||||
8AA1573A6E580F689A48C3237CC36268 /* Contain.swift in Sources */ = {isa = PBXBuildFile; fileRef = E49C5B1205F3B26219A5F9471156353B /* Contain.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
8DECE3E28E934801E17DE5C0ACA24ABA /* RemoteCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C4339512935F0311EE3E2406CC855F /* RemoteCommand.swift */; };
|
||||
8F69EDCC09739459EB61749494F1E002 /* CwlCatchException.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7DB1CD8C2C3735948E12DC35B0BED7 /* CwlCatchException.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
907EBF43A4A37DD92C61F0AEA6ED1330 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF45228F79391196CE7D3A18D70519B /* Filter.swift */; };
|
||||
9108613C28FC0D1A7948C6B4FE64932F /* Nimble-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = E4D833ED9179EEF01C24DCA76DD19097 /* Nimble-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
95ABBFEBA3AF22853E345BAAC8867FF3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80C82283F763D9ABC7DD3BB91787CA8D /* XCTest.framework */; };
|
||||
95C261DC185368D41443743CBA636141 /* APError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011EE192D3B1D2A686C8C5D93E3AB0D0 /* APError.swift */; };
|
||||
9676A05220B174FC299049577162F1C8 /* MediaItemProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F815B046C467FFE21F47EEF4F76CBE2 /* MediaItemProperty.swift */; };
|
||||
9AADA2573B8098C022E8A390155DFD90 /* Functional.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EF3195721B0195371824114C2A4BEE /* Functional.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
9B94816B4EC1BBEC4E7971F04EA5192A /* SimpleAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C7830817928E0FBBAE48F19F730C2C /* SimpleAudioPlayer.swift */; };
|
||||
9D321DC100B95C35B6FF4D01BFCD6C07 /* ThrowError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FBFB0070F2E0C4399EAD76B169D24 /* ThrowError.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
9DEECB22E5869BB2ECD155D53946F939 /* NimbleXCTestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA9613DD13D6685DDEE70B45F0ADFB6 /* NimbleXCTestHandler.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
A0871080516F7088835670597EB06674 /* CwlDarwinDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF5CEB6ECB6C3C4793E3828927021A57 /* CwlDarwinDefinitions.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
@@ -123,7 +126,6 @@
|
||||
C74928AC19FB27AA9D7AC4FC94A2D7F1 /* AVPlayerTimeObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7481CD97B89126BA4A1C6FCC80366BD /* AVPlayerTimeObserver.swift */; };
|
||||
CCEC9E4FCE42FF652C0563E4F6BB0459 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54476EB21A4C89C927FF42E858D9E3F /* Example.swift */; };
|
||||
D10E1F8FA9C618DFE1DE13BBF1A1B683 /* AudioItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C9AEEB071CD4A301C5C9CC7DB3CD15 /* AudioItem.swift */; };
|
||||
D1CC79EBF2498C6E01C4234330E33DA4 /* NowPlayingInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E72935CCE60D79BE7ECCC2A6645B9A6 /* NowPlayingInfoController.swift */; };
|
||||
D1F6601FD7B12B4DD24F04BB553F0B6D /* BeLessThan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B5E02942CB72732D6947AFD0D263FA /* BeLessThan.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
D5CEB8D0B830BAE216347870F154079D /* DSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63BAD0DA551276454D9ACE719DEFCEB1 /* DSL.m */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
DCD90D9E9A79C53BC7CD0FA8FA02961D /* BeEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6074FF9C29583B7D7FA989EDEDB550 /* BeEmpty.swift */; settings = {COMPILER_FLAGS = "-DPRODUCT_NAME=Nimble/Nimble"; }; };
|
||||
@@ -175,23 +177,30 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
006367118F9218EE3420DCEF343E7B4C /* AudioSessionController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AudioSessionController.swift; path = SwiftAudio/Classes/AudioSessionController.swift; sourceTree = "<group>"; };
|
||||
01056F9C4DEDD08119B7F98ECE8B2845 /* HaveCount.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HaveCount.swift; path = Sources/Nimble/Matchers/HaveCount.swift; sourceTree = "<group>"; };
|
||||
011EE192D3B1D2A686C8C5D93E3AB0D0 /* APError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = APError.swift; path = SwiftAudio/Classes/APError.swift; sourceTree = "<group>"; };
|
||||
01DCEA2B70C5FE3C749141DED60AA3A1 /* AVPlayerObserver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AVPlayerObserver.swift; sourceTree = "<group>"; };
|
||||
048A74934A0826237E15D12BBFA56C98 /* Quick-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Quick-umbrella.h"; sourceTree = "<group>"; };
|
||||
049D65258659A381E53BD1E11B7345E8 /* BeVoid.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BeVoid.swift; path = Sources/Nimble/Matchers/BeVoid.swift; sourceTree = "<group>"; };
|
||||
04C9AEEB071CD4A301C5C9CC7DB3CD15 /* AudioItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AudioItem.swift; path = SwiftAudio/Classes/AudioItem.swift; sourceTree = "<group>"; };
|
||||
075131AC2182FF1600D3BFB9 /* AVPlayerWrapperProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerWrapperProtocol.swift; sourceTree = "<group>"; };
|
||||
075131AE21830D9500D3BFB9 /* AVPlayerWrapperDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerWrapperDelegate.swift; sourceTree = "<group>"; };
|
||||
075131B3218322E000D3BFB9 /* MediaItemProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaItemProperty.swift; sourceTree = "<group>"; };
|
||||
075131B4218322E000D3BFB9 /* NowPlayingInfoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NowPlayingInfoController.swift; sourceTree = "<group>"; };
|
||||
075131B5218322E000D3BFB9 /* NowPlayingInfoProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NowPlayingInfoProperty.swift; sourceTree = "<group>"; };
|
||||
075131B7218322E000D3BFB9 /* RemoteCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteCommand.swift; sourceTree = "<group>"; };
|
||||
075131B8218322E000D3BFB9 /* RemoteCommandController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteCommandController.swift; sourceTree = "<group>"; };
|
||||
07756B71218C2D590023935E /* AudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioSession.swift; sourceTree = "<group>"; };
|
||||
07756B72218C2D590023935E /* AudioSessionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioSessionController.swift; sourceTree = "<group>"; };
|
||||
0A362FBD9BA860D03408EC59B8DAD715 /* QueuedAudioPlayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = QueuedAudioPlayer.swift; path = SwiftAudio/Classes/QueuedAudioPlayer.swift; sourceTree = "<group>"; };
|
||||
0B2E2D47B69C1FB3AC152895C90935DE /* NMBStringify.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = NMBStringify.m; path = Sources/NimbleObjectiveC/NMBStringify.m; sourceTree = "<group>"; };
|
||||
0B83D7847D2E148325682799F8D762FB /* AdapterProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AdapterProtocols.swift; path = Sources/Nimble/Adapters/AdapterProtocols.swift; sourceTree = "<group>"; };
|
||||
0C8C8954498E96A380A9E019FA368062 /* Pods_SwiftAudio_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_SwiftAudio_Tests.framework; path = "Pods-SwiftAudio_Tests.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0C8C8954498E96A380A9E019FA368062 /* Pods_SwiftAudio_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftAudio_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0CFE2240B90DBC44697F16996798ADC0 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0D9BE9AED5D0D4A233A5F28CDAF64E5F /* NMBExceptionCapture.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = NMBExceptionCapture.m; path = Sources/NimbleObjectiveC/NMBExceptionCapture.m; sourceTree = "<group>"; };
|
||||
0E696C2BC0CF5F4035F7B6A9613C5608 /* BeAnInstanceOf.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BeAnInstanceOf.swift; path = Sources/Nimble/Matchers/BeAnInstanceOf.swift; sourceTree = "<group>"; };
|
||||
0E72935CCE60D79BE7ECCC2A6645B9A6 /* NowPlayingInfoController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NowPlayingInfoController.swift; path = SwiftAudio/Classes/NowPlayingInfoController.swift; sourceTree = "<group>"; };
|
||||
0F8B70224FF4BAA3EF856694DAEFEEEA /* BeGreaterThan.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BeGreaterThan.swift; path = Sources/Nimble/Matchers/BeGreaterThan.swift; sourceTree = "<group>"; };
|
||||
1038701997A03A7D55AAAC5A3E1D42F8 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; path = README.md; sourceTree = "<group>"; };
|
||||
1038701997A03A7D55AAAC5A3E1D42F8 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
1093B46C09355EB4CED5CA7DE7FDBFEE /* mach_excServer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = mach_excServer.h; path = Carthage/Checkouts/CwlPreconditionTesting/Sources/CwlMachBadInstructionHandler/mach_excServer.h; sourceTree = "<group>"; };
|
||||
10D81A32DD08157E729CDA25855D4178 /* QCKDSL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QCKDSL.h; path = Sources/QuickObjectiveC/DSL/QCKDSL.h; sourceTree = "<group>"; };
|
||||
160931E5B7C58FBCE27C6F77CD31065A /* Expectation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Expectation.swift; path = Sources/Nimble/Expectation.swift; sourceTree = "<group>"; };
|
||||
@@ -211,7 +220,7 @@
|
||||
2DEC6727EE6F37FE78F0B0879E3FC89C /* ExpectationMessage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpectationMessage.swift; path = Sources/Nimble/ExpectationMessage.swift; sourceTree = "<group>"; };
|
||||
2EE57213362ECA66BB3B1C08B2DB0C57 /* CwlCatchBadInstruction.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CwlCatchBadInstruction.swift; path = Carthage/Checkouts/CwlPreconditionTesting/Sources/CwlPreconditionTesting/CwlCatchBadInstruction.swift; sourceTree = "<group>"; };
|
||||
2F45FD38D2C696978A840807E542F9DA /* ExampleGroup.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExampleGroup.swift; path = Sources/Quick/ExampleGroup.swift; sourceTree = "<group>"; };
|
||||
2FD31C5F451C5A3BD436B937CE169BAD /* SwiftAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftAudio.framework; path = SwiftAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2FD31C5F451C5A3BD436B937CE169BAD /* SwiftAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3350F0F9B5DDF05AE230A91E5FD38686 /* Pods-SwiftAudio_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-SwiftAudio_Tests-dummy.m"; sourceTree = "<group>"; };
|
||||
336E11B7F8854A9118FB070A9E34CF78 /* Await.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Await.swift; path = Sources/Nimble/Utils/Await.swift; sourceTree = "<group>"; };
|
||||
354FCCFF316C6B11FFA6BB86B5B58D43 /* NSBundle+CurrentTestBundle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSBundle+CurrentTestBundle.swift"; path = "Sources/Quick/NSBundle+CurrentTestBundle.swift"; sourceTree = "<group>"; };
|
||||
@@ -220,7 +229,6 @@
|
||||
3D0BC856D7B86412A484A3DD582F9841 /* ExampleMetadata.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExampleMetadata.swift; path = Sources/Quick/ExampleMetadata.swift; sourceTree = "<group>"; };
|
||||
3EB72EE755307D2502E70A6B529B73EC /* QuickSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QuickSpec.h; path = Sources/QuickObjectiveC/QuickSpec.h; sourceTree = "<group>"; };
|
||||
42CE60C662D9A1382B4BF26914AC82D0 /* DSL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DSL.h; path = Sources/NimbleObjectiveC/DSL.h; sourceTree = "<group>"; };
|
||||
4609750B9014770E50A7FF8F13C08BE9 /* NowPlayingInfoProperty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NowPlayingInfoProperty.swift; path = SwiftAudio/Classes/NowPlayingInfoProperty.swift; sourceTree = "<group>"; };
|
||||
4760419BE7E8BC5915D5CFF14109A979 /* Pods-SwiftAudio_Tests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-SwiftAudio_Tests-resources.sh"; sourceTree = "<group>"; };
|
||||
48FB816DE4B6A4BFDD305F58A6472925 /* ThrowAssertion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThrowAssertion.swift; path = Sources/Nimble/Matchers/ThrowAssertion.swift; sourceTree = "<group>"; };
|
||||
4ADDE4EA63584E9BF3933026B32B66E2 /* Nimble-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Nimble-dummy.m"; sourceTree = "<group>"; };
|
||||
@@ -243,19 +251,18 @@
|
||||
5F4FB27486313C6A081388FCCB02EC16 /* QuickSpecBase.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QuickSpecBase.m; path = Sources/QuickSpecBase/QuickSpecBase.m; sourceTree = "<group>"; };
|
||||
5FF5498ACD8246B3B1EC6CBFCAD4C8D7 /* Quick-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Quick-prefix.pch"; sourceTree = "<group>"; };
|
||||
60B070F60A08FF5EF7CF3D1A69143000 /* NMBExpectation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NMBExpectation.swift; path = Sources/Nimble/Adapters/NMBExpectation.swift; sourceTree = "<group>"; };
|
||||
60D239C6021277D481ED86E2E29B1670 /* SwiftAudio.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; path = SwiftAudio.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
60D239C6021277D481ED86E2E29B1670 /* SwiftAudio.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; path = SwiftAudio.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
63BAD0DA551276454D9ACE719DEFCEB1 /* DSL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DSL.m; path = Sources/NimbleObjectiveC/DSL.m; sourceTree = "<group>"; };
|
||||
660D7D4C3B82FFD945DFA23D0C1C6A9E /* SatisfyAnyOf.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SatisfyAnyOf.swift; path = Sources/Nimble/Matchers/SatisfyAnyOf.swift; sourceTree = "<group>"; };
|
||||
66323C82A0A7CB5FB54B5F774BEC72DC /* Quick.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Quick.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
66323C82A0A7CB5FB54B5F774BEC72DC /* Quick.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6708E82D864D86CC9A66E3A038B6FDB9 /* ExampleHooks.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExampleHooks.swift; path = Sources/Quick/Hooks/ExampleHooks.swift; sourceTree = "<group>"; };
|
||||
689190491F4225B281ABC68343A24D61 /* RemoteCommandController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RemoteCommandController.swift; path = SwiftAudio/Classes/RemoteCommandController.swift; sourceTree = "<group>"; };
|
||||
6CCBA4A43D738FAE7BC1910666153FD0 /* SwiftAudio.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftAudio.xcconfig; sourceTree = "<group>"; };
|
||||
6DEE0ABD257E7A562F3A8FAFE03E9FBC /* Pods-SwiftAudio_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SwiftAudio_Tests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
6F09E00015A51CF7CCAC20DF7144AE44 /* URL+FileName.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URL+FileName.swift"; path = "Sources/Quick/URL+FileName.swift"; sourceTree = "<group>"; };
|
||||
70B5E02942CB72732D6947AFD0D263FA /* BeLessThan.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BeLessThan.swift; path = Sources/Nimble/Matchers/BeLessThan.swift; sourceTree = "<group>"; };
|
||||
731667DB8FCC21C2D4FEAE6CC1FED184 /* Errors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Errors.swift; path = Sources/Nimble/Utils/Errors.swift; sourceTree = "<group>"; };
|
||||
74320E6F1A8A9105DC422E9C6DFE820C /* AssertionDispatcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssertionDispatcher.swift; path = Sources/Nimble/Adapters/AssertionDispatcher.swift; sourceTree = "<group>"; };
|
||||
747BC4EDBF186DED5F6ECE69B29801A9 /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Nimble.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
747BC4EDBF186DED5F6ECE69B29801A9 /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7AB337D9B97833B31DBEAB7D4FB2AE0E /* World+DSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "World+DSL.swift"; path = "Sources/Quick/DSL/World+DSL.swift"; sourceTree = "<group>"; };
|
||||
7C47506434F8C86E58A02928CC65CC10 /* Pods-SwiftAudio_Tests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-SwiftAudio_Tests-acknowledgements.plist"; sourceTree = "<group>"; };
|
||||
7CF92AE58937B3AD4BA6DA7134DA9F52 /* AudioPlayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AudioPlayer.swift; path = SwiftAudio/Classes/AudioPlayer.swift; sourceTree = "<group>"; };
|
||||
@@ -265,17 +272,15 @@
|
||||
81B10C2CFD2B4D759CF041A45CF58FF2 /* CwlBadInstructionException.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CwlBadInstructionException.swift; path = Carthage/Checkouts/CwlPreconditionTesting/Sources/CwlPreconditionTesting/CwlBadInstructionException.swift; sourceTree = "<group>"; };
|
||||
823F6A12075FF0A33C70898ECC8A145B /* NSString+C99ExtendedIdentifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSString+C99ExtendedIdentifier.swift"; path = "Sources/Quick/NSString+C99ExtendedIdentifier.swift"; sourceTree = "<group>"; };
|
||||
8350374D2D2D105674580DABB85F37FF /* AVPlayerWrapper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AVPlayerWrapper.swift; sourceTree = "<group>"; };
|
||||
84A291A79F9C752A91C561C0AFB7B957 /* Pods_SwiftAudio_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_SwiftAudio_Example.framework; path = "Pods-SwiftAudio_Example.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84A291A79F9C752A91C561C0AFB7B957 /* Pods_SwiftAudio_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftAudio_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
85A12CC592518974E5AF8CC73C019039 /* Quick.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Quick.modulemap; sourceTree = "<group>"; };
|
||||
879D4131C2851B0A039CD43D72FA9AF5 /* Stringers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Stringers.swift; path = Sources/Nimble/Utils/Stringers.swift; sourceTree = "<group>"; };
|
||||
87C7830817928E0FBBAE48F19F730C2C /* SimpleAudioPlayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SimpleAudioPlayer.swift; path = SwiftAudio/Classes/SimpleAudioPlayer.swift; sourceTree = "<group>"; };
|
||||
8967DA4BBBE5E17E301D3C56BE293A95 /* QuickSpec.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QuickSpec.m; path = Sources/QuickObjectiveC/QuickSpec.m; sourceTree = "<group>"; };
|
||||
8CA0648396C81EDE0A9B7C711638FA8B /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8D87AE93CC56AA3DD5FA80EF8564C9C2 /* EndWith.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EndWith.swift; path = Sources/Nimble/Matchers/EndWith.swift; sourceTree = "<group>"; };
|
||||
8DA9613DD13D6685DDEE70B45F0ADFB6 /* NimbleXCTestHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NimbleXCTestHandler.swift; path = Sources/Nimble/Adapters/NimbleXCTestHandler.swift; sourceTree = "<group>"; };
|
||||
8DBDFCD12FE89ED7977BAEC1F0EC7101 /* XCTestSuite+QuickTestSuiteBuilder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "XCTestSuite+QuickTestSuiteBuilder.m"; path = "Sources/QuickObjectiveC/XCTestSuite+QuickTestSuiteBuilder.m"; sourceTree = "<group>"; };
|
||||
8F815B046C467FFE21F47EEF4F76CBE2 /* MediaItemProperty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MediaItemProperty.swift; path = SwiftAudio/Classes/MediaItemProperty.swift; sourceTree = "<group>"; };
|
||||
93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
942F4368934964E307D71356A565E34F /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
945BF3F9EA32D635660DD7902B1A3D03 /* MatcherProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MatcherProtocols.swift; path = Sources/Nimble/Matchers/MatcherProtocols.swift; sourceTree = "<group>"; };
|
||||
993D3D81BF52A44266E4840EF7D5D995 /* AssertionRecorder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssertionRecorder.swift; path = Sources/Nimble/Adapters/AssertionRecorder.swift; sourceTree = "<group>"; };
|
||||
@@ -286,7 +291,6 @@
|
||||
9FC94BD467B9F8E22E297008546EB659 /* BeGreaterThanOrEqualTo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BeGreaterThanOrEqualTo.swift; path = Sources/Nimble/Matchers/BeGreaterThanOrEqualTo.swift; sourceTree = "<group>"; };
|
||||
A474607214D558C1FDF0E33F10A70CC2 /* PostNotification.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PostNotification.swift; path = Sources/Nimble/Matchers/PostNotification.swift; sourceTree = "<group>"; };
|
||||
A54476EB21A4C89C927FF42E858D9E3F /* Example.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Example.swift; path = Sources/Quick/Example.swift; sourceTree = "<group>"; };
|
||||
A9C4339512935F0311EE3E2406CC855F /* RemoteCommand.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RemoteCommand.swift; path = SwiftAudio/Classes/RemoteCommand.swift; sourceTree = "<group>"; };
|
||||
AF2AA3686E2E5C992300580BE170EF0B /* Quick.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Quick.xcconfig; sourceTree = "<group>"; };
|
||||
AFE4BE7D9282807F0AEB8591BD9031E6 /* Nimble.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Nimble.xcconfig; sourceTree = "<group>"; };
|
||||
B110998EFA9DEB0A41CF492290C3CC9E /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
@@ -306,7 +310,7 @@
|
||||
C965EB7D2ABC715652F428F0755CC37C /* Pods-SwiftAudio_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SwiftAudio_Tests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
CA2B83B52F99EEA836F6F0669A3B9B3A /* DSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DSL.swift; path = Sources/Quick/DSL/DSL.swift; sourceTree = "<group>"; };
|
||||
CA4741D70CAC2E64FB39C437CE5CF9AF /* SwiftAudio.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftAudio.modulemap; sourceTree = "<group>"; };
|
||||
CD78111E631F1A9FBB37A70A9EF3B187 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE; sourceTree = "<group>"; };
|
||||
CD78111E631F1A9FBB37A70A9EF3B187 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||
CFCDA081DA07D782D75DA9C33E476214 /* SourceLocation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SourceLocation.swift; path = Sources/Nimble/Utils/SourceLocation.swift; sourceTree = "<group>"; };
|
||||
D0463AFE8D72243A6EB984085BE04A65 /* BeLessThanOrEqual.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BeLessThanOrEqual.swift; path = Sources/Nimble/Matchers/BeLessThanOrEqual.swift; sourceTree = "<group>"; };
|
||||
D08DE2F262516A80F255EE2310F0EDD0 /* ToSucceed.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToSucceed.swift; path = Sources/Nimble/Matchers/ToSucceed.swift; sourceTree = "<group>"; };
|
||||
@@ -328,7 +332,7 @@
|
||||
EA5E86ABAB681E9B91D7D7CD0FCD5EB2 /* Pods-SwiftAudio_Example-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-SwiftAudio_Example-frameworks.sh"; sourceTree = "<group>"; };
|
||||
EDE8144940E39AB93895BF39F2E9FEBC /* Pods-SwiftAudio_Tests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-SwiftAudio_Tests-umbrella.h"; sourceTree = "<group>"; };
|
||||
EE8BB96B10E6B8555DD8E78B0E39FEBE /* World.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = World.swift; path = Sources/Quick/World.swift; sourceTree = "<group>"; };
|
||||
EED52EE491F66BC008AB993C58365EBE /* mach_excServer.c */ = {isa = PBXFileReference; includeInIndex = 1; name = mach_excServer.c; path = Carthage/Checkouts/CwlPreconditionTesting/Sources/CwlMachBadInstructionHandler/mach_excServer.c; sourceTree = "<group>"; };
|
||||
EED52EE491F66BC008AB993C58365EBE /* mach_excServer.c */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.c; name = mach_excServer.c; path = Carthage/Checkouts/CwlPreconditionTesting/Sources/CwlMachBadInstructionHandler/mach_excServer.c; sourceTree = "<group>"; };
|
||||
EF4534C7E47CA5B502A19513117CBFC3 /* TimeEventFrequency.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TimeEventFrequency.swift; path = SwiftAudio/Classes/TimeEventFrequency.swift; sourceTree = "<group>"; };
|
||||
EF5CEB6ECB6C3C4793E3828927021A57 /* CwlDarwinDefinitions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CwlDarwinDefinitions.swift; path = Carthage/Checkouts/CwlPreconditionTesting/Sources/CwlPreconditionTesting/CwlDarwinDefinitions.swift; sourceTree = "<group>"; };
|
||||
F031D2A41C0E2D94B334BC31754F2F09 /* AVPlayerWrapperState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AVPlayerWrapperState.swift; sourceTree = "<group>"; };
|
||||
@@ -389,6 +393,37 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
075131B2218322E000D3BFB9 /* NowPlayingInfoController */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
075131B3218322E000D3BFB9 /* MediaItemProperty.swift */,
|
||||
075131B4218322E000D3BFB9 /* NowPlayingInfoController.swift */,
|
||||
075131B5218322E000D3BFB9 /* NowPlayingInfoProperty.swift */,
|
||||
);
|
||||
name = NowPlayingInfoController;
|
||||
path = SwiftAudio/Classes/NowPlayingInfoController;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
075131B6218322E000D3BFB9 /* RemoteCommandController */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
075131B7218322E000D3BFB9 /* RemoteCommand.swift */,
|
||||
075131B8218322E000D3BFB9 /* RemoteCommandController.swift */,
|
||||
);
|
||||
name = RemoteCommandController;
|
||||
path = SwiftAudio/Classes/RemoteCommandController;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
07756B6F218C2D590023935E /* AudioSessionController */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
07756B72218C2D590023935E /* AudioSessionController.swift */,
|
||||
07756B71218C2D590023935E /* AudioSession.swift */,
|
||||
);
|
||||
name = AudioSessionController;
|
||||
path = SwiftAudio/Classes/AudioSessionController;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1E85F0F44277212282918D8D5DDB3588 /* iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -464,7 +499,6 @@
|
||||
8DBDFCD12FE89ED7977BAEC1F0EC7101 /* XCTestSuite+QuickTestSuiteBuilder.m */,
|
||||
3E8F7D7BA6F7BD122DBB624992079C91 /* Support Files */,
|
||||
);
|
||||
name = Quick;
|
||||
path = Quick;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -481,6 +515,8 @@
|
||||
children = (
|
||||
8350374D2D2D105674580DABB85F37FF /* AVPlayerWrapper.swift */,
|
||||
F031D2A41C0E2D94B334BC31754F2F09 /* AVPlayerWrapperState.swift */,
|
||||
075131AC2182FF1600D3BFB9 /* AVPlayerWrapperProtocol.swift */,
|
||||
075131AE21830D9500D3BFB9 /* AVPlayerWrapperDelegate.swift */,
|
||||
);
|
||||
name = AVPlayerWrapper;
|
||||
path = SwiftAudio/Classes/AVPlayerWrapper;
|
||||
@@ -661,7 +697,6 @@
|
||||
56AED7BB8EBC87A7394B1B4309775DC2 /* XCTestObservationCenter+Register.m */,
|
||||
5F94F4B7D93544A44CDA76AFC8704CB7 /* Support Files */,
|
||||
);
|
||||
name = Nimble;
|
||||
path = Nimble;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -671,16 +706,12 @@
|
||||
011EE192D3B1D2A686C8C5D93E3AB0D0 /* APError.swift */,
|
||||
04C9AEEB071CD4A301C5C9CC7DB3CD15 /* AudioItem.swift */,
|
||||
7CF92AE58937B3AD4BA6DA7134DA9F52 /* AudioPlayer.swift */,
|
||||
006367118F9218EE3420DCEF343E7B4C /* AudioSessionController.swift */,
|
||||
8F815B046C467FFE21F47EEF4F76CBE2 /* MediaItemProperty.swift */,
|
||||
0E72935CCE60D79BE7ECCC2A6645B9A6 /* NowPlayingInfoController.swift */,
|
||||
4609750B9014770E50A7FF8F13C08BE9 /* NowPlayingInfoProperty.swift */,
|
||||
0A362FBD9BA860D03408EC59B8DAD715 /* QueuedAudioPlayer.swift */,
|
||||
7D4763A422F83644D194E97981775831 /* QueueManager.swift */,
|
||||
A9C4339512935F0311EE3E2406CC855F /* RemoteCommand.swift */,
|
||||
689190491F4225B281ABC68343A24D61 /* RemoteCommandController.swift */,
|
||||
87C7830817928E0FBBAE48F19F730C2C /* SimpleAudioPlayer.swift */,
|
||||
EF4534C7E47CA5B502A19513117CBFC3 /* TimeEventFrequency.swift */,
|
||||
07756B6F218C2D590023935E /* AudioSessionController */,
|
||||
075131B2218322E000D3BFB9 /* NowPlayingInfoController */,
|
||||
075131B6218322E000D3BFB9 /* RemoteCommandController */,
|
||||
581526FDBE4DC5C1C3F143696A8DE42E /* AVPlayerWrapper */,
|
||||
A6AF9F48F4B25035E227BFDAE847B384 /* Observer */,
|
||||
5D68DC904FAAE9CFC1059C6B3F4BB87D /* Pod */,
|
||||
@@ -864,6 +895,11 @@
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0930;
|
||||
LastUpgradeCheck = 0930;
|
||||
TargetAttributes = {
|
||||
1AB61EB02FDF0033DCB1F8416419F110 = {
|
||||
LastSwiftMigration = 1010;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -907,24 +943,26 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
075131BD218322E000D3BFB9 /* RemoteCommandController.swift in Sources */,
|
||||
95C261DC185368D41443743CBA636141 /* APError.swift in Sources */,
|
||||
075131BB218322E000D3BFB9 /* NowPlayingInfoProperty.swift in Sources */,
|
||||
D10E1F8FA9C618DFE1DE13BBF1A1B683 /* AudioItem.swift in Sources */,
|
||||
42249CE127313A81E6DF9F492BC3FD60 /* AudioPlayer.swift in Sources */,
|
||||
213745669894F37CD02BED396AC2EFFA /* AudioSessionController.swift in Sources */,
|
||||
075131B9218322E000D3BFB9 /* MediaItemProperty.swift in Sources */,
|
||||
121F6F4C3C3FBD49FBFF3BC91205C11B /* AVPlayerItemNotificationObserver.swift in Sources */,
|
||||
E10CAB33DDA27C101EF36E8923046511 /* AVPlayerItemObserver.swift in Sources */,
|
||||
13AFC3FFA6B2175A542C1279345CAAA3 /* AVPlayerObserver.swift in Sources */,
|
||||
C74928AC19FB27AA9D7AC4FC94A2D7F1 /* AVPlayerTimeObserver.swift in Sources */,
|
||||
07756B74218C2D590023935E /* AudioSession.swift in Sources */,
|
||||
315B8C7DDF47918E6F996F8AC45C6BB7 /* AVPlayerWrapper.swift in Sources */,
|
||||
F462AF5848880C2C1791D0C7F943879A /* AVPlayerWrapperState.swift in Sources */,
|
||||
9676A05220B174FC299049577162F1C8 /* MediaItemProperty.swift in Sources */,
|
||||
D1CC79EBF2498C6E01C4234330E33DA4 /* NowPlayingInfoController.swift in Sources */,
|
||||
8A77D5CE0D8380C1FD2FE859015C7DAA /* NowPlayingInfoProperty.swift in Sources */,
|
||||
075131AD2182FF1600D3BFB9 /* AVPlayerWrapperProtocol.swift in Sources */,
|
||||
075131BC218322E000D3BFB9 /* RemoteCommand.swift in Sources */,
|
||||
36DAEF0CCEB4AEFA64DFAF8197FD5C68 /* QueuedAudioPlayer.swift in Sources */,
|
||||
075131BA218322E000D3BFB9 /* NowPlayingInfoController.swift in Sources */,
|
||||
47B306C3AA1AA9EB82EE2FDE5267B13C /* QueueManager.swift in Sources */,
|
||||
8DECE3E28E934801E17DE5C0ACA24ABA /* RemoteCommand.swift in Sources */,
|
||||
1CAA71BDD1BCB7A8EBD8A890526774C6 /* RemoteCommandController.swift in Sources */,
|
||||
9B94816B4EC1BBEC4E7971F04EA5192A /* SimpleAudioPlayer.swift in Sources */,
|
||||
07756B75218C2D590023935E /* AudioSessionController.swift in Sources */,
|
||||
075131AF21830D9500D3BFB9 /* AVPlayerWrapperDelegate.swift in Sources */,
|
||||
1DC86A0AEBBA8A5B9353C4F7B2D4649A /* SwiftAudio-dummy.m in Sources */,
|
||||
095BD8D416719E425189E8E4963A8D4E /* TimeEventFrequency.swift in Sources */,
|
||||
);
|
||||
@@ -1192,7 +1230,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -1412,7 +1450,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1010"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1AB61EB02FDF0033DCB1F8416419F110"
|
||||
BuildableName = "SwiftAudio.framework"
|
||||
BlueprintName = "SwiftAudio"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1AB61EB02FDF0033DCB1F8416419F110"
|
||||
BuildableName = "SwiftAudio.framework"
|
||||
BlueprintName = "SwiftAudio"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -29,8 +29,8 @@
|
||||
07732654205ECA8B00C4D1CD /* WAV-MP3.wav in Resources */ = {isa = PBXBuildFile; fileRef = 07732650205EACA300C4D1CD /* WAV-MP3.wav */; };
|
||||
07732655205ECE1C00C4D1CD /* nasa_throttle_up.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 07732652205EB1B500C4D1CD /* nasa_throttle_up.mp3 */; };
|
||||
0775575920668B020002C6A1 /* QueueManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0775575820668B020002C6A1 /* QueueManagerTests.swift */; };
|
||||
07756B69218A4E870023935E /* AudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07756B68218A4E870023935E /* AudioSession.swift */; };
|
||||
078C908F210D263200555E80 /* AVPlayerItemObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 078C908D210D25F700555E80 /* AVPlayerItemObserverTests.swift */; };
|
||||
07CC171C213E912E005F880E /* SimpleAudioPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CC171A213E912A005F880E /* SimpleAudioPlayerTests.swift */; };
|
||||
07DBB1E1212C17E600BB4278 /* QueuedAudioPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DBB1E0212C17E600BB4278 /* QueuedAudioPlayerTests.swift */; };
|
||||
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
|
||||
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
|
||||
@@ -68,8 +68,8 @@
|
||||
07732650205EACA300C4D1CD /* WAV-MP3.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "WAV-MP3.wav"; sourceTree = "<group>"; };
|
||||
07732652205EB1B500C4D1CD /* nasa_throttle_up.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = nasa_throttle_up.mp3; sourceTree = "<group>"; };
|
||||
0775575820668B020002C6A1 /* QueueManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueManagerTests.swift; sourceTree = "<group>"; };
|
||||
07756B68218A4E870023935E /* AudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSession.swift; sourceTree = "<group>"; };
|
||||
078C908D210D25F700555E80 /* AVPlayerItemObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerItemObserverTests.swift; sourceTree = "<group>"; };
|
||||
07CC171A213E912A005F880E /* SimpleAudioPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleAudioPlayerTests.swift; sourceTree = "<group>"; };
|
||||
07DBB1E0212C17E600BB4278 /* QueuedAudioPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueuedAudioPlayerTests.swift; sourceTree = "<group>"; };
|
||||
521F3AEC1228A2FA2637355F /* Pods-SwiftAudio_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftAudio_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftAudio_Tests/Pods-SwiftAudio_Tests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
607FACD01AFB9204008FA782 /* SwiftAudio_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftAudio_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -124,6 +124,14 @@
|
||||
path = Source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
07756B67218A4E640023935E /* Mocks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
07756B68218A4E870023935E /* AudioSession.swift */,
|
||||
);
|
||||
path = Mocks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
607FACC71AFB9204008FA782 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -175,6 +183,7 @@
|
||||
607FACE81AFB9204008FA782 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
07756B67218A4E640023935E /* Mocks */,
|
||||
0708ED732116EE0100EB29BD /* AudioPlayerTests.swift */,
|
||||
607FACEB1AFB9204008FA782 /* AVPlayerObserverTests.swift */,
|
||||
074A6482205C155E0083D868 /* AVPlayerTimeObserverTests.swift */,
|
||||
@@ -184,7 +193,6 @@
|
||||
078C908D210D25F700555E80 /* AVPlayerItemObserverTests.swift */,
|
||||
0708ED6B2116DA4B00EB29BD /* AudioSessionControllerTests.swift */,
|
||||
07DBB1E0212C17E600BB4278 /* QueuedAudioPlayerTests.swift */,
|
||||
07CC171A213E912A005F880E /* SimpleAudioPlayerTests.swift */,
|
||||
0708ED712116E91300EB29BD /* Source */,
|
||||
607FACE91AFB9204008FA782 /* Supporting Files */,
|
||||
);
|
||||
@@ -278,13 +286,13 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0830;
|
||||
LastUpgradeCheck = 0830;
|
||||
LastUpgradeCheck = 1010;
|
||||
ORGANIZATIONNAME = CocoaPods;
|
||||
TargetAttributes = {
|
||||
607FACCF1AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = HPNZWPB9JK;
|
||||
LastSwiftMigration = 0900;
|
||||
LastSwiftMigration = 1010;
|
||||
SystemCapabilities = {
|
||||
com.apple.BackgroundModes = {
|
||||
enabled = 1;
|
||||
@@ -294,7 +302,7 @@
|
||||
607FACE41AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = HPNZWPB9JK;
|
||||
LastSwiftMigration = 0900;
|
||||
LastSwiftMigration = 1010;
|
||||
TestTargetID = 607FACCF1AFB9204008FA782;
|
||||
};
|
||||
};
|
||||
@@ -443,9 +451,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
07756B69218A4E870023935E /* AudioSession.swift in Sources */,
|
||||
0708ED702116E89900EB29BD /* Source.swift in Sources */,
|
||||
0708ED742116EE0100EB29BD /* AudioPlayerTests.swift in Sources */,
|
||||
07CC171C213E912E005F880E /* SimpleAudioPlayerTests.swift in Sources */,
|
||||
0775575920668B020002C6A1 /* QueueManagerTests.swift in Sources */,
|
||||
074A6483205C155E0083D868 /* AVPlayerTimeObserverTests.swift in Sources */,
|
||||
078C908F210D263200555E80 /* AVPlayerItemObserverTests.swift in Sources */,
|
||||
@@ -499,12 +507,14 @@
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
@@ -552,12 +562,14 @@
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
@@ -598,8 +610,8 @@
|
||||
MODULE_NAME = ExampleApp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -615,8 +627,8 @@
|
||||
MODULE_NAME = ExampleApp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -637,8 +649,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftAudio_Example.app/SwiftAudio_Example";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -656,8 +667,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftAudio_Example.app/SwiftAudio_Example";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0900"
|
||||
LastUpgradeVersion = "1010"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
<dict>
|
||||
<key>BuildSystemType</key>
|
||||
<string>Original</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
|
||||
application.beginReceivingRemoteControlEvents()
|
||||
|
||||
@@ -13,7 +13,7 @@ import SwiftAudio
|
||||
class AudioController {
|
||||
|
||||
static let shared = AudioController()
|
||||
let player = QueuedAudioPlayer()
|
||||
let player: QueuedAudioPlayer
|
||||
let audioSessionController = AudioSessionController.shared
|
||||
|
||||
let sources: [AudioItem] = [
|
||||
@@ -24,8 +24,12 @@ class AudioController {
|
||||
]
|
||||
|
||||
init() {
|
||||
let controller = RemoteCommandController()
|
||||
player = QueuedAudioPlayer(remoteCommandController: controller)
|
||||
player.remoteCommands = [
|
||||
.stop,
|
||||
.play,
|
||||
.pause,
|
||||
.togglePlayPause,
|
||||
.next,
|
||||
.previous,
|
||||
|
||||
@@ -46,7 +46,7 @@ extension QueueViewController: UITableViewDataSource, UITableViewDelegate {
|
||||
case 0:
|
||||
return 1
|
||||
case 1:
|
||||
return controller.player.nextItems.count ?? 0
|
||||
return controller.player.nextItems.count
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class ViewController: UIViewController {
|
||||
if (!controller.audioSessionController.audioSessionIsActive) {
|
||||
try? controller.audioSessionController.activateSession()
|
||||
}
|
||||
try? controller.player.togglePlaying()
|
||||
controller.player.togglePlaying()
|
||||
}
|
||||
|
||||
@IBAction func previous(_ sender: Any) {
|
||||
@@ -50,7 +50,7 @@ class ViewController: UIViewController {
|
||||
}
|
||||
|
||||
@IBAction func scrubbing(_ sender: UISlider) {
|
||||
try? controller.player.seek(to: Double(slider.value))
|
||||
controller.player.seek(to: Double(slider.value))
|
||||
}
|
||||
|
||||
@IBAction func scrubbingValueChanged(_ sender: UISlider) {
|
||||
@@ -61,6 +61,10 @@ class ViewController: UIViewController {
|
||||
}
|
||||
|
||||
extension ViewController: AudioPlayerDelegate {
|
||||
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
func audioPlayer(playerDidChangeState state: AVPlayerWrapperState) {
|
||||
playButton.setTitle(state == .playing ? "Pause" : "Play", for: .normal)
|
||||
@@ -89,10 +93,6 @@ extension ViewController: AudioPlayerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func audioPlayerItemDidComplete() {
|
||||
|
||||
}
|
||||
|
||||
func audioPlayer(secondsElapsed seconds: Double) {
|
||||
if !isScrubbing {
|
||||
slider.setValue(Float(seconds), animated: false)
|
||||
|
||||
@@ -7,8 +7,8 @@ import AVFoundation
|
||||
|
||||
class AVPlayerObserverTests: QuickSpec, AVPlayerObserverDelegate {
|
||||
|
||||
var status: AVPlayerStatus?
|
||||
var timeControlStatus: AVPlayerTimeControlStatus?
|
||||
var status: AVPlayer.Status?
|
||||
var timeControlStatus: AVPlayer.TimeControlStatus?
|
||||
|
||||
override func spec() {
|
||||
|
||||
@@ -58,11 +58,11 @@ class AVPlayerObserverTests: QuickSpec, AVPlayerObserverDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func player(statusDidChange status: AVPlayerStatus) {
|
||||
func player(statusDidChange status: AVPlayer.Status) {
|
||||
self.status = status
|
||||
}
|
||||
|
||||
func player(didChangeTimeControlStatus status: AVPlayerTimeControlStatus) {
|
||||
func player(didChangeTimeControlStatus status: AVPlayer.TimeControlStatus) {
|
||||
self.timeControlStatus = status
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Quick
|
||||
import Nimble
|
||||
import AVFoundation
|
||||
|
||||
@testable import SwiftAudio
|
||||
|
||||
@@ -13,51 +14,12 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
var wrapper: AVPlayerWrapper!
|
||||
|
||||
beforeEach {
|
||||
wrapper = AVPlayerWrapper()
|
||||
wrapper.automaticallyWaitsToMinimizeStalling = false
|
||||
let player = AVPlayer()
|
||||
player.automaticallyWaitsToMinimizeStalling = false
|
||||
player.volume = 0.0
|
||||
wrapper = AVPlayerWrapper(avPlayer: player)
|
||||
wrapper.bufferDuration = 0.0001
|
||||
wrapper.volume = 0.0
|
||||
}
|
||||
|
||||
context("when calling play() with no item", {
|
||||
var err: APError.PlaybackError?
|
||||
|
||||
beforeEach {
|
||||
do {
|
||||
try wrapper.play()
|
||||
}
|
||||
catch {
|
||||
if let error = error as? APError.PlaybackError {
|
||||
err = error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should throw a noItemLoaded error", closure: {
|
||||
expect(err).toNot(beNil())
|
||||
expect(err).to(equal(APError.PlaybackError.noLoadedItem))
|
||||
})
|
||||
})
|
||||
|
||||
context("when calling pause() with no item", {
|
||||
var err: APError.PlaybackError?
|
||||
|
||||
beforeEach {
|
||||
do {
|
||||
try wrapper.pause()
|
||||
}
|
||||
catch {
|
||||
if let error = error as? APError.PlaybackError {
|
||||
err = error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should throw a noItemLoaded error", closure: {
|
||||
expect(err).toNot(beNil())
|
||||
expect(err).to(equal(APError.PlaybackError.noLoadedItem))
|
||||
})
|
||||
})
|
||||
|
||||
describe("its state", {
|
||||
it("should be idle", closure: {
|
||||
@@ -66,7 +28,7 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
|
||||
context("when loading a source", {
|
||||
beforeEach {
|
||||
try? wrapper.load(fromFilePath: Source.path, playWhenReady: false)
|
||||
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: false)
|
||||
}
|
||||
|
||||
it("should be loading", closure: {
|
||||
@@ -80,7 +42,7 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
|
||||
context("when playing with no source", {
|
||||
beforeEach {
|
||||
try? wrapper.play()
|
||||
wrapper.play()
|
||||
}
|
||||
it("should be idle", closure: {
|
||||
expect(wrapper.state).to(equal(AVPlayerWrapperState.idle))
|
||||
@@ -89,7 +51,7 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
|
||||
context("when playing a source", {
|
||||
beforeEach {
|
||||
try? wrapper.load(fromFilePath: Source.path, playWhenReady: true)
|
||||
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("should eventually be playing", closure: {
|
||||
@@ -106,10 +68,10 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
wrapper.delegate = holder
|
||||
holder.stateUpdate = { (state) in
|
||||
if state == .playing {
|
||||
try? wrapper.pause()
|
||||
wrapper.pause()
|
||||
}
|
||||
}
|
||||
try? wrapper.load(fromFilePath: Source.path, playWhenReady: true)
|
||||
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("should eventually be paused", closure: {
|
||||
@@ -123,10 +85,10 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
wrapper.delegate = holder
|
||||
holder.stateUpdate = { (state) in
|
||||
if state == .playing {
|
||||
try? wrapper.togglePlaying()
|
||||
wrapper.togglePlaying()
|
||||
}
|
||||
}
|
||||
try? wrapper.load(fromFilePath: Source.path, playWhenReady: true)
|
||||
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
|
||||
}
|
||||
it("should eventually be paused", closure: {
|
||||
expect(wrapper.state).toEventually(equal(AVPlayerWrapperState.paused))
|
||||
@@ -149,7 +111,7 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
receivedIdleUpdate = true
|
||||
}
|
||||
}
|
||||
try? wrapper.load(fromFilePath: Source.path, playWhenReady: true)
|
||||
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("should eventually be 'idle'", closure: {
|
||||
@@ -160,7 +122,7 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
|
||||
context("when seeking before loading", {
|
||||
beforeEach {
|
||||
try? wrapper.seek(to: 10)
|
||||
wrapper.seek(to: 10)
|
||||
}
|
||||
it("should be idle", closure: {
|
||||
expect(wrapper.state).to(equal(AVPlayerWrapperState.idle))
|
||||
@@ -175,7 +137,7 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
|
||||
context("when loading source", {
|
||||
beforeEach {
|
||||
try? wrapper.load(fromFilePath: Source.path, playWhenReady: false)
|
||||
wrapper.load(from: URL(fileURLWithPath: LongSource.path), playWhenReady: false)
|
||||
}
|
||||
it("should eventually not be 0", closure: {
|
||||
expect(wrapper.duration).toEventuallyNot(equal(0))
|
||||
@@ -193,12 +155,8 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
let seekTime: TimeInterval = 0.5
|
||||
beforeEach {
|
||||
wrapper.delegate = holder
|
||||
holder.stateUpdate = { (state) in
|
||||
if state == .ready && wrapper.duration != 0 {
|
||||
try? wrapper.seek(to: seekTime)
|
||||
}
|
||||
}
|
||||
try? wrapper.load(fromFilePath: Source.path, playWhenReady: false)
|
||||
wrapper.load(from: Source.url, playWhenReady: false)
|
||||
wrapper.seek(to: seekTime)
|
||||
}
|
||||
|
||||
it("should eventually be equal to the seeked time", closure: {
|
||||
@@ -207,6 +165,51 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
})
|
||||
})
|
||||
|
||||
describe("its rate", {
|
||||
it("should be 0", closure: {
|
||||
expect(wrapper.rate).to(equal(0.0))
|
||||
})
|
||||
|
||||
context("when playing a source", {
|
||||
beforeEach {
|
||||
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("should eventually be 1.0", closure: {
|
||||
expect(wrapper.rate).toEventually(equal(1.0))
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe("its automaticallyWaitsToMinimizeStalling option", {
|
||||
it("should be false", closure: {
|
||||
expect(wrapper.automaticallyWaitsToMinimizeStalling).to(beFalse())
|
||||
})
|
||||
|
||||
context("when setting it to true", {
|
||||
beforeEach {
|
||||
wrapper.automaticallyWaitsToMinimizeStalling = true
|
||||
}
|
||||
|
||||
it("should be true", closure: {
|
||||
expect(wrapper.automaticallyWaitsToMinimizeStalling).to(beTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("its timeEventFrequency", {
|
||||
context("when updated", {
|
||||
beforeEach {
|
||||
wrapper.timeEventFrequency = .everyHalfSecond
|
||||
}
|
||||
|
||||
it("should update the playerTimeObservers periodicObserverTimeInterval", closure: {
|
||||
expect(wrapper.playerTimeObserver.periodicObserverTimeInterval).to(equal(TimeEventFrequency.everyHalfSecond.getTime()))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -214,12 +217,12 @@ class AVPlayerWrapperTests: QuickSpec {
|
||||
}
|
||||
|
||||
class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
|
||||
|
||||
|
||||
func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {
|
||||
|
||||
}
|
||||
|
||||
var state: AVPlayerWrapperState? {
|
||||
didSet {
|
||||
print(state)
|
||||
if let state = state {
|
||||
self.stateUpdate?(state)
|
||||
}
|
||||
@@ -233,10 +236,6 @@ class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
|
||||
self.state = state
|
||||
}
|
||||
|
||||
func AVWrapperItemDidComplete() {
|
||||
|
||||
}
|
||||
|
||||
func AVWrapper(secondsElapsed seconds: Double) {
|
||||
|
||||
}
|
||||
@@ -245,8 +244,9 @@ class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
|
||||
|
||||
}
|
||||
|
||||
var seekCompletion: (() -> Void)?
|
||||
func AVWrapper(seekTo seconds: Int, didFinish: Bool) {
|
||||
|
||||
seekCompletion?()
|
||||
}
|
||||
|
||||
func AVWrapper(didUpdateDuration duration: Double) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Quick
|
||||
import Nimble
|
||||
import AVFoundation
|
||||
|
||||
@testable import SwiftAudio
|
||||
|
||||
@@ -11,9 +12,9 @@ class AudioPlayerTests: QuickSpec {
|
||||
|
||||
beforeEach {
|
||||
audioPlayer = AudioPlayer()
|
||||
audioPlayer.automaticallyWaitsToMinimizeStalling = false
|
||||
audioPlayer.bufferDuration = 0.0001
|
||||
audioPlayer.volume = 0
|
||||
audioPlayer.automaticallyWaitsToMinimizeStalling = false
|
||||
audioPlayer.volume = 0.0
|
||||
}
|
||||
|
||||
describe("its state", {
|
||||
@@ -24,7 +25,7 @@ class AudioPlayerTests: QuickSpec {
|
||||
|
||||
context("when audio item is loaded", {
|
||||
beforeEach {
|
||||
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: false)
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
|
||||
}
|
||||
|
||||
it("it should eventually be ready", closure: {
|
||||
@@ -34,7 +35,7 @@ class AudioPlayerTests: QuickSpec {
|
||||
|
||||
context("when an item is loaded (playWhenReady=true)", {
|
||||
beforeEach {
|
||||
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: true)
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("it should eventually be playing", closure: {
|
||||
@@ -50,10 +51,10 @@ class AudioPlayerTests: QuickSpec {
|
||||
holder.stateUpdate = { state in
|
||||
print(state.rawValue)
|
||||
if state == .ready {
|
||||
try? audioPlayer.play()
|
||||
audioPlayer.play()
|
||||
}
|
||||
}
|
||||
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: false)
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
|
||||
}
|
||||
|
||||
it("should eventually be playing", closure: {
|
||||
@@ -68,10 +69,10 @@ class AudioPlayerTests: QuickSpec {
|
||||
audioPlayer.delegate = holder
|
||||
holder.stateUpdate = { (state) in
|
||||
if state == .playing {
|
||||
try? audioPlayer.pause()
|
||||
audioPlayer.pause()
|
||||
}
|
||||
}
|
||||
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: true)
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("should eventually be paused", closure: {
|
||||
@@ -89,7 +90,7 @@ class AudioPlayerTests: QuickSpec {
|
||||
audioPlayer.stop()
|
||||
}
|
||||
}
|
||||
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: true)
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("should eventually be idle", closure: {
|
||||
@@ -105,20 +106,18 @@ class AudioPlayerTests: QuickSpec {
|
||||
})
|
||||
|
||||
context("when seeking to a time", {
|
||||
var passed = false
|
||||
let holder = AudioPlayerDelegateHolder()
|
||||
let seekTime: TimeInterval = 0.5
|
||||
beforeEach {
|
||||
audioPlayer.delegate = holder
|
||||
holder.stateUpdate = { (state) in
|
||||
if state == .ready && audioPlayer.duration != 0 {
|
||||
try? audioPlayer.seek(to: seekTime)
|
||||
}
|
||||
}
|
||||
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: false)
|
||||
holder.seekCompletion = { passed = true }
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
|
||||
audioPlayer.seek(to: seekTime)
|
||||
}
|
||||
|
||||
it("should eventually be equal to the seeked time", closure: {
|
||||
expect(audioPlayer.currentTime).toEventually(equal(seekTime))
|
||||
expect(passed).toEventually(beTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -130,7 +129,7 @@ class AudioPlayerTests: QuickSpec {
|
||||
|
||||
context("when playing an item", {
|
||||
beforeEach {
|
||||
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: true)
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
|
||||
}
|
||||
|
||||
it("should eventually be 1.0", closure: {
|
||||
@@ -138,12 +137,60 @@ class AudioPlayerTests: QuickSpec {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("its currentItem", {
|
||||
it("should be nil", closure: {
|
||||
expect(audioPlayer.currentItem).to(beNil())
|
||||
})
|
||||
|
||||
context("when loading an item", {
|
||||
beforeEach {
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
|
||||
}
|
||||
|
||||
it("should not be nil", closure: {
|
||||
expect(audioPlayer.currentItem).toNot(beNil())
|
||||
})
|
||||
})
|
||||
|
||||
context("when setting the timePitchAlgorithm", {
|
||||
|
||||
beforeEach {
|
||||
audioPlayer.audioTimePitchAlgorithm = .timeDomain
|
||||
}
|
||||
|
||||
context("then loading an item", {
|
||||
beforeEach {
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
|
||||
}
|
||||
|
||||
it("should have the applied timePitchAlgorithm", closure: {
|
||||
expect(audioPlayer.wrapper.currentItem?.audioTimePitchAlgorithm).to(equal(AVAudioTimePitchAlgorithm.timeDomain))
|
||||
})
|
||||
})
|
||||
|
||||
context("then loading a timepitching item", {
|
||||
beforeEach {
|
||||
let item = DefaultAudioItemTimePitching(audioUrl: Source.path, artist: nil, title: nil, albumTitle: nil, sourceType: .file, artwork: nil, audioTimePitchAlgorithm: AVAudioTimePitchAlgorithm.spectral)
|
||||
try? audioPlayer.load(item: item, playWhenReady: false)
|
||||
}
|
||||
|
||||
it("should have the applied timePitchAlgorithm", closure: {
|
||||
expect(audioPlayer.wrapper.currentItem?.audioTimePitchAlgorithm).to(equal(AVAudioTimePitchAlgorithm.spectral))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AudioPlayerDelegateHolder: AudioPlayerDelegate {
|
||||
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
var stateUpdate: ((_ state: AudioPlayerState) -> Void)?
|
||||
var state: AudioPlayerState? {
|
||||
@@ -158,10 +205,6 @@ class AudioPlayerDelegateHolder: AudioPlayerDelegate {
|
||||
self.state = state
|
||||
}
|
||||
|
||||
func audioPlayerItemDidComplete() {
|
||||
|
||||
}
|
||||
|
||||
func audioPlayer(secondsElapsed seconds: Double) {
|
||||
|
||||
}
|
||||
@@ -170,8 +213,9 @@ class AudioPlayerDelegateHolder: AudioPlayerDelegate {
|
||||
|
||||
}
|
||||
|
||||
var seekCompletion: (() -> Void)?
|
||||
func audioPlayer(seekTo seconds: Int, didFinish: Bool) {
|
||||
|
||||
seekCompletion?()
|
||||
}
|
||||
|
||||
func audioPlayer(didUpdateDuration duration: Double) {
|
||||
|
||||
@@ -9,7 +9,7 @@ class AudioSessionControllerTests: QuickSpec {
|
||||
override func spec() {
|
||||
|
||||
describe("An AudioSessionController") {
|
||||
let audioSessionController: AudioSessionController = AudioSessionController.shared
|
||||
let audioSessionController: AudioSessionController = AudioSessionController(audioSession: NonFailingAudioSession())
|
||||
|
||||
it("should be inactive", closure: {
|
||||
expect(audioSessionController.audioSessionIsActive).to(beFalse())
|
||||
@@ -55,7 +55,7 @@ class AudioSessionControllerTests: QuickSpec {
|
||||
context("when a interruption arrives", {
|
||||
var delegate: AudioSessionControllerDelegateImplementation!
|
||||
beforeEach {
|
||||
let notification = Notification(name: .AVAudioSessionInterruption, object: nil, userInfo: [
|
||||
let notification = Notification(name: AVAudioSession.interruptionNotification, object: nil, userInfo: [
|
||||
AVAudioSessionInterruptionTypeKey: UInt(0)
|
||||
])
|
||||
delegate = AudioSessionControllerDelegateImplementation()
|
||||
@@ -69,18 +69,32 @@ class AudioSessionControllerTests: QuickSpec {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
describe("An AudioSessionController with a failing AudioSession") {
|
||||
var audioSessionController: AudioSessionController!
|
||||
beforeEach {
|
||||
audioSessionController = AudioSessionController(audioSession: FailingAudioSession())
|
||||
}
|
||||
|
||||
context("when activated", {
|
||||
beforeEach {
|
||||
try? audioSessionController.activateSession()
|
||||
}
|
||||
|
||||
it("should be inactive", closure: {
|
||||
expect(audioSessionController.audioSessionIsActive).to(beFalse())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AudioSessionControllerDelegateImplementation: AudioSessionControllerDelegate {
|
||||
|
||||
var interruptionType: AVAudioSessionInterruptionType? = nil
|
||||
var interruptionType: AVAudioSession.InterruptionType? = nil
|
||||
|
||||
func handleInterruption(type: AVAudioSessionInterruptionType) {
|
||||
func handleInterruption(type: AVAudioSession.InterruptionType) {
|
||||
self.interruptionType = type
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// AudioSession.swift
|
||||
// SwiftAudio_Tests
|
||||
//
|
||||
// Created by Jørgen Henrichsen on 31/10/2018.
|
||||
// Copyright © 2018 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
@testable import SwiftAudio
|
||||
|
||||
|
||||
class NonFailingAudioSession: AudioSession {
|
||||
|
||||
var category: AVAudioSession.Category = AVAudioSession.Category.playback
|
||||
|
||||
var mode: AVAudioSession.Mode = AVAudioSession.Mode.default
|
||||
|
||||
var categoryOptions: AVAudioSession.CategoryOptions = []
|
||||
|
||||
var availableCategories: [AVAudioSession.Category] = []
|
||||
|
||||
var isOtherAudioPlaying: Bool = false
|
||||
|
||||
func setCategory(_ category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions) throws {}
|
||||
|
||||
func setCategory(_ category: AVAudioSession.Category, mode: AVAudioSession.Mode, policy: AVAudioSession.RouteSharingPolicy, options: AVAudioSession.CategoryOptions) throws {}
|
||||
|
||||
func setActive(_ active: Bool) throws {}
|
||||
|
||||
func setActive(_ active: Bool, options: AVAudioSession.SetActiveOptions) throws {}
|
||||
|
||||
}
|
||||
|
||||
class FailingAudioSession: AudioSession {
|
||||
|
||||
var category: AVAudioSession.Category = AVAudioSession.Category.playback
|
||||
|
||||
var mode: AVAudioSession.Mode = AVAudioSession.Mode.default
|
||||
|
||||
var categoryOptions: AVAudioSession.CategoryOptions = AVAudioSession.CategoryOptions.allowBluetooth
|
||||
|
||||
var availableCategories: [AVAudioSession.Category] = []
|
||||
|
||||
var isOtherAudioPlaying: Bool = false
|
||||
|
||||
func setCategory(_ category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions) throws {
|
||||
throw AVError(AVError.unknown)
|
||||
}
|
||||
|
||||
func setCategory(_ category: AVAudioSession.Category, mode: AVAudioSession.Mode, policy: AVAudioSession.RouteSharingPolicy, options: AVAudioSession.CategoryOptions) throws {
|
||||
throw AVError(AVError.unknown)
|
||||
}
|
||||
|
||||
func setActive(_ active: Bool) throws {
|
||||
throw AVError(AVError.unknown)
|
||||
}
|
||||
|
||||
func setActive(_ active: Bool, options: AVAudioSession.SetActiveOptions) throws {
|
||||
throw AVError(AVError.unknown)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -20,6 +20,57 @@ class QueueManagerTests: QuickSpec {
|
||||
manager = QueueManager()
|
||||
}
|
||||
|
||||
describe("its current item", {
|
||||
|
||||
it("should be nil", closure: {
|
||||
expect(manager.current).to(beNil())
|
||||
})
|
||||
|
||||
context("when one item is added", closure: {
|
||||
beforeEach {
|
||||
manager.addItem(self.dummyItem)
|
||||
}
|
||||
|
||||
it("should not be nil", closure: {
|
||||
expect(manager.current).toNot(beNil())
|
||||
})
|
||||
|
||||
it("should be the added item", closure: {
|
||||
expect(manager.current).to(equal(self.dummyItem))
|
||||
})
|
||||
|
||||
context("then replaced", closure: {
|
||||
beforeEach {
|
||||
manager.replaceCurrentItem(with: 1)
|
||||
}
|
||||
it("should be the new item", closure: {
|
||||
expect(manager.current).to(equal(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context("when replaced", closure: {
|
||||
beforeEach {
|
||||
manager.replaceCurrentItem(with: 1)
|
||||
}
|
||||
|
||||
it("should not be nil", closure: {
|
||||
expect(manager.current).toNot(beNil())
|
||||
})
|
||||
})
|
||||
|
||||
context("when mulitple items are added", {
|
||||
beforeEach {
|
||||
manager.addItems(self.dummyItems)
|
||||
}
|
||||
|
||||
it("should not be nil", closure: {
|
||||
expect(manager.current).toNot(beNil())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
context("when adding one item", {
|
||||
|
||||
beforeEach {
|
||||
@@ -30,9 +81,13 @@ class QueueManagerTests: QuickSpec {
|
||||
expect(manager.items).notTo(beEmpty())
|
||||
})
|
||||
|
||||
it("should set it as the current item", closure: {
|
||||
expect(manager.current).toNot(beNil())
|
||||
expect(manager.current).to(equal(self.dummyItem))
|
||||
context("then replacing the item", closure: {
|
||||
beforeEach {
|
||||
manager.replaceCurrentItem(with: 1)
|
||||
}
|
||||
it("should have replaced the current item", closure: {
|
||||
expect(manager.current).to(equal(1))
|
||||
})
|
||||
})
|
||||
|
||||
context("then calling next", {
|
||||
@@ -115,14 +170,72 @@ class QueueManagerTests: QuickSpec {
|
||||
expect(manager.current).to(equal(self.dummyItems.first))
|
||||
})
|
||||
})
|
||||
|
||||
context("then removing previous items", {
|
||||
beforeEach {
|
||||
manager.removePreviousItems()
|
||||
}
|
||||
it("should have no previous items", closure: {
|
||||
expect(manager.previousItems.count).to(equal(0))
|
||||
})
|
||||
it("should have current index zero", closure: {
|
||||
expect(manager.currentIndex).to(equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context("adding more items", {
|
||||
var initialItemCount: Int!
|
||||
let newItems: [Int] = [10, 11, 12, 13]
|
||||
beforeEach {
|
||||
initialItemCount = manager.items.count
|
||||
try? manager.addItems(newItems, at: manager.items.endIndex - 1)
|
||||
}
|
||||
|
||||
it("should have more items", closure: {
|
||||
expect(manager.items.count).to(equal(initialItemCount + newItems.count))
|
||||
})
|
||||
})
|
||||
|
||||
context("adding more items at a smaller index than currentIndex", {
|
||||
var initialCurrentIndex: Int!
|
||||
let newItems: [Int] = [10, 11, 12, 13]
|
||||
beforeEach {
|
||||
initialCurrentIndex = manager.currentIndex
|
||||
try? manager.addItems(newItems, at: initialCurrentIndex)
|
||||
}
|
||||
|
||||
it("currentIndex should increase by number of new items", closure: {
|
||||
expect(manager.currentIndex).to(equal(initialCurrentIndex + newItems.count))
|
||||
})
|
||||
})
|
||||
|
||||
// MARK: - Removal
|
||||
|
||||
context("then removing a item with index less than currentIndex", {
|
||||
beforeEach {
|
||||
var removed: Int?
|
||||
var initialCurrentIndex: Int!
|
||||
beforeEach {
|
||||
let _ = try? manager.jump(to: 3)
|
||||
initialCurrentIndex = manager.currentIndex
|
||||
removed = try? manager.removeItem(at: initialCurrentIndex - 1)
|
||||
}
|
||||
|
||||
it("should remove an item", closure: {
|
||||
expect(removed).toNot(beNil())
|
||||
})
|
||||
|
||||
it("should decrement the currentIndex", closure: {
|
||||
expect(manager.currentIndex).to(equal(initialCurrentIndex - 1))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
context("then removing the second item", {
|
||||
var removed: Int?
|
||||
beforeEach {
|
||||
removed = try? manager.remove(atIndex: 1)
|
||||
removed = try? manager.removeItem(at: 1)
|
||||
}
|
||||
|
||||
it("should have one less item", closure: {
|
||||
@@ -134,7 +247,7 @@ class QueueManagerTests: QuickSpec {
|
||||
context("then removing the last item", {
|
||||
var removed: Int?
|
||||
beforeEach {
|
||||
removed = try? manager.remove(atIndex: self.dummyItems.count - 1)
|
||||
removed = try? manager.removeItem(at: self.dummyItems.count - 1)
|
||||
}
|
||||
|
||||
it("should have one less item", closure: {
|
||||
@@ -146,7 +259,7 @@ class QueueManagerTests: QuickSpec {
|
||||
context("then removing the current item", {
|
||||
var removed: Int?
|
||||
beforeEach {
|
||||
removed = try? manager.remove(atIndex: manager.currentIndex)
|
||||
removed = try? manager.removeItem(at: manager.currentIndex)
|
||||
}
|
||||
it("should not remove any items", closure: {
|
||||
expect(removed).to(beNil())
|
||||
@@ -157,7 +270,7 @@ class QueueManagerTests: QuickSpec {
|
||||
context("then removing with too large index", {
|
||||
var removed: Int?
|
||||
beforeEach {
|
||||
removed = try? manager.remove(atIndex: self.dummyItems.count)
|
||||
removed = try? manager.removeItem(at: self.dummyItems.count)
|
||||
}
|
||||
|
||||
it("should not remove any items", closure: {
|
||||
@@ -169,7 +282,7 @@ class QueueManagerTests: QuickSpec {
|
||||
context("then removing with too small index", {
|
||||
var removed: Int?
|
||||
beforeEach {
|
||||
removed = try? manager.remove(atIndex: -1)
|
||||
removed = try? manager.removeItem(at: -1)
|
||||
}
|
||||
|
||||
it("should not remove any items", closure: {
|
||||
@@ -178,6 +291,16 @@ class QueueManagerTests: QuickSpec {
|
||||
})
|
||||
})
|
||||
|
||||
context("then removing upcoming items", {
|
||||
beforeEach {
|
||||
manager.removeUpcomingItems()
|
||||
}
|
||||
|
||||
it("should have no next items", closure: {
|
||||
expect(manager.nextItems.count).to(equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
// MARK: - Jumping
|
||||
|
||||
context("then jumping to the current item", {
|
||||
@@ -343,6 +466,22 @@ class QueueManagerTests: QuickSpec {
|
||||
expect(manager.items).to(equal(afterMoving))
|
||||
})
|
||||
})
|
||||
|
||||
// MARK: - Clear
|
||||
|
||||
context("when queue is cleared", {
|
||||
beforeEach {
|
||||
manager.clearQueue()
|
||||
}
|
||||
|
||||
it("should have currentIndex 0", closure: {
|
||||
expect(manager.currentIndex).to(equal(0))
|
||||
})
|
||||
|
||||
it("should have no items", closure: {
|
||||
expect(manager.items.count).to(equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ class QueuedAudioPlayerTests: QuickSpec {
|
||||
var audioPlayer: QueuedAudioPlayer!
|
||||
beforeEach {
|
||||
audioPlayer = QueuedAudioPlayer()
|
||||
audioPlayer.automaticallyWaitsToMinimizeStalling = false
|
||||
audioPlayer.bufferDuration = 0.0001
|
||||
audioPlayer.volume = 0
|
||||
audioPlayer.automaticallyWaitsToMinimizeStalling = false
|
||||
audioPlayer.volume = 0.0
|
||||
}
|
||||
describe("its current item", {
|
||||
it("should be nil", closure: {
|
||||
@@ -19,12 +19,24 @@ class QueuedAudioPlayerTests: QuickSpec {
|
||||
})
|
||||
|
||||
context("when adding one item", {
|
||||
var item: AudioItem!
|
||||
beforeEach {
|
||||
try? audioPlayer.add(item: ShortSource.getAudioItem(), playWhenReady: false)
|
||||
item = ShortSource.getAudioItem()
|
||||
try? audioPlayer.add(item: item, playWhenReady: false)
|
||||
}
|
||||
it("should not be nil", closure: {
|
||||
expect(audioPlayer.currentItem).toNot(beNil())
|
||||
})
|
||||
|
||||
context("then loading a new item", closure: {
|
||||
beforeEach {
|
||||
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
|
||||
}
|
||||
|
||||
it("should have replaced the item", closure: {
|
||||
expect(audioPlayer.currentItem?.getSourceUrl()).toNot(equal(item.getSourceUrl()))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context("when adding multiple items", {
|
||||
@@ -70,7 +82,7 @@ class QueuedAudioPlayerTests: QuickSpec {
|
||||
|
||||
context("then removing one item", {
|
||||
beforeEach {
|
||||
try? audioPlayer.removeItem(atIndex: 1)
|
||||
try? audioPlayer.removeItem(at: 1)
|
||||
}
|
||||
|
||||
it("should be empty", closure: {
|
||||
@@ -86,6 +98,26 @@ class QueuedAudioPlayerTests: QuickSpec {
|
||||
expect(audioPlayer.nextItems.count).to(equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
context("then removing upcoming items", {
|
||||
beforeEach {
|
||||
audioPlayer.removeUpcomingItems()
|
||||
}
|
||||
|
||||
it("should be empty", closure: {
|
||||
expect(audioPlayer.nextItems.count).to(equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
context("then stopping", {
|
||||
beforeEach {
|
||||
audioPlayer.stop()
|
||||
}
|
||||
|
||||
it("should be empty", closure: {
|
||||
expect(audioPlayer.nextItems.count).to(equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -112,6 +144,26 @@ class QueuedAudioPlayerTests: QuickSpec {
|
||||
})
|
||||
})
|
||||
|
||||
context("then removing all previous items", {
|
||||
beforeEach {
|
||||
audioPlayer.removePreviousItems()
|
||||
}
|
||||
|
||||
it("should be empty", closure: {
|
||||
expect(audioPlayer.previousItems.count).to(equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
context("then stopping", {
|
||||
beforeEach {
|
||||
audioPlayer.stop()
|
||||
}
|
||||
|
||||
it("should be empty", closure: {
|
||||
expect(audioPlayer.previousItems.count).to(equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SwiftAudio
|
||||
|
||||
class SimpleAudioPlayerTests: QuickSpec {
|
||||
override func spec() {
|
||||
describe("A SimpleAudioPlayer") {
|
||||
var player: SimpleAudioPlayer!
|
||||
beforeEach {
|
||||
player = SimpleAudioPlayer()
|
||||
player.automaticallyWaitsToMinimizeStalling = false
|
||||
player.bufferDuration = 0.0001
|
||||
player.volume = 0
|
||||
}
|
||||
|
||||
describe("its state", {
|
||||
it("should be idle", closure: {
|
||||
expect(player.playerState).to(equal(AudioPlayerState.idle))
|
||||
})
|
||||
|
||||
context("when loading an item with playeWhenReady: false", {
|
||||
beforeEach {
|
||||
try? player.load(item: Source.getAudioItem(), playWhenReady: false)
|
||||
}
|
||||
it("should eventually be ready", closure: {
|
||||
expect(player.playerState).toEventually(equal(AudioPlayerState.ready))
|
||||
})
|
||||
})
|
||||
|
||||
context("when loading an item with playWhenReady: true", {
|
||||
beforeEach {
|
||||
try? player.load(item: Source.getAudioItem(), playWhenReady: true)
|
||||
}
|
||||
it("should eventually be playing", closure: {
|
||||
expect(player.playerState).toEventually(equal(AudioPlayerState.playing))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import SwiftAudio
|
||||
|
||||
struct Source {
|
||||
static let path: String = Bundle.main.path(forResource: "TestSound", ofType: "m4a")!
|
||||
static let url: URL = URL(fileURLWithPath: Source.path)
|
||||
|
||||
static func getAudioItem() -> AudioItem {
|
||||
return DefaultAudioItem(audioUrl: Source.path, sourceType: .file)
|
||||
@@ -19,8 +20,18 @@ struct Source {
|
||||
|
||||
struct ShortSource {
|
||||
static let path: String = Bundle.main.path(forResource: "ShortTestSound", ofType: "m4a")!
|
||||
static let url: URL = URL(fileURLWithPath: ShortSource.path)
|
||||
|
||||
static func getAudioItem() -> AudioItem {
|
||||
return DefaultAudioItem(audioUrl: ShortSource.path, sourceType: .file)
|
||||
}
|
||||
}
|
||||
|
||||
struct LongSource {
|
||||
static let path: String = Bundle.main.path(forResource: "WAV-MP3", ofType: "wav")!
|
||||
static let url: URL = URL(fileURLWithPath: LongSource.path)
|
||||
|
||||
static func getAudioItem() -> AudioItem {
|
||||
return DefaultAudioItem(audioUrl: LongSource.path, sourceType: .file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ SwiftAudio is an audio player written in Swift, making it simpler to work with a
|
||||
|
||||
## Example
|
||||
|
||||
To see the audio player in action clone the repo and run the example project!
|
||||
To see the audio player in action, run the example project!
|
||||
To run the example project, clone the repo, and run `pod install` from the Example directory first.
|
||||
|
||||
## Requirements
|
||||
@@ -18,54 +18,69 @@ iOS 10.0+
|
||||
|
||||
## Installation
|
||||
|
||||
### CocoaPods
|
||||
SwiftAudio is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile:
|
||||
|
||||
```ruby
|
||||
pod 'SwiftAudio'
|
||||
pod 'SwiftAudio', '~> 0.6.1'
|
||||
```
|
||||
|
||||
### Carthage
|
||||
SwiftAudio supports [Carthage](https://github.com/Carthage/Carthage). Add this to your Cartfile:
|
||||
```ruby
|
||||
github "jorgenhenrichsen/SwiftAudio" ~> 0.6.1
|
||||
```
|
||||
Then follow the rest of Carthage instructions on [adding a framework](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
|
||||
|
||||
## Usage
|
||||
|
||||
### AudioPlayer
|
||||
To get started playing some audio:
|
||||
```swift
|
||||
let player = AudioPlayer()
|
||||
let audioItem = DefaultAudioItem(audioUrl: "someUrl", sourceType: .stream)
|
||||
player.load(item: audioItem, playWhenReady: true) // Load the item and start playing when the player is ready.
|
||||
```
|
||||
|
||||
Implement `AudioPlayerDelegate` to get notified about useful events and updates to the state of the `AudioPlayer`.
|
||||
|
||||
#### QueuedAudioPlayer
|
||||
The `QueuedAudioPlayer` is asubclass of `AudioPlayer` that maintains a queue of audio tracks.
|
||||
```swift
|
||||
let player = QueuedAudioPlayer()
|
||||
let audioItem = DefaultAudioItem(audioUrl: "someUrl", sourceType: .stream)
|
||||
player.add(item: audioItem)
|
||||
player.add(item: audioItem, playWhenReady: true) // Since this is the first item, we can supply playWhenReady: true to immedietaly start playing when the item is loaded.
|
||||
```
|
||||
|
||||
The player will load the track and start playing when ready. To disable this behaviour use `add(item:playWhenReady:)` and pass in `false`. This is `true` by default. To get notified of events during playback and loading, implement `AudioPlayerDelegate` and the player will notify you with changes.
|
||||
When a track is done playing, the player will load the next track and update the queue, as long as `automaticallyPlayNextSong` is `true` (default).
|
||||
|
||||
If you want a simpler audio player without queue functionality, use:
|
||||
##### Navigating the queue
|
||||
All `AudioItem`s are stored in either `previousItems` or `nextItems`, which refers to items that come prior to the `currentItem` and after, respectively. The queue is navigated with:
|
||||
```swift
|
||||
let player = SimpleAudioPlayer()
|
||||
let audioItem = DefaultAudioItem(audioUrl: "someUrl", sourceType: .stream)
|
||||
player.load(item: audioItem)
|
||||
player.next() // Increments the queue, and loads the next item.
|
||||
player.previous() // Decrements the queue, and loads the previous item.
|
||||
player.jumpToItem(atIndex:) // Jumps to a certain item and loads that item.
|
||||
```
|
||||
|
||||
**NOTE**: Do not use `AudioPlayer` directly. Use one of the above types.
|
||||
##### Manipulating the queue
|
||||
```swift
|
||||
player.removeItem(at:) // Remove a specific item from the queue.
|
||||
player.removeUpcomingItems() // Remove all items in nextItems.
|
||||
```
|
||||
|
||||
#### States
|
||||
The `AudioPlayer` has a `state` property, to make it easier to determine appropriate actions. The different states:
|
||||
+ **idle**: The player is doing nothing, no item is set as current. This is the default state.
|
||||
+ **ready**: The player has its current item set and is ready to start loading for playback. This is when you can call `play()` if you supplied `playWhenReady=false` when calling `load(item:playWhenReady)`.
|
||||
+ **loading**: The player is loading the track and will start playback soon.
|
||||
+ **playing**: The player is playing.
|
||||
+ **paused**: The player is paused.
|
||||
|
||||
#### Queue
|
||||
The `QueuedAudioPlayer` maintains a queue of audio tracks.
|
||||
The arrangement of the tracks are: [Previous]-[Current]-[Next].
|
||||
|
||||
When a track is done playing, the player will load the next track and update the queue, as long as `automaticallyPlayNextSong` is `true` (This is by default).
|
||||
|
||||
Items can be added to the queue by calling `player.add(item:)` or `player.add(items:)`.
|
||||
Use `removeItem(atIndex:)` and `moveItem(fromIndex:toIndex:)` to manipulate the queue.
|
||||
|
||||
The queue can be navigated by using `next()`, `previous()` and `jumpToItem(atIndex:)`
|
||||
### Configuring the AudioPlayer
|
||||
Current options for configuring the `AudioPlayer`:
|
||||
- `bufferDuration`: The amount of seconds to be buffered by the player.
|
||||
- `timeEventFrequency`: How often the player should call the delegate with time progress events.
|
||||
- `automaticallyWaitsToMinimizeStalling`: Indicates whether the player should automatically delay playback in order to minimize stalling.
|
||||
- `volume`
|
||||
- `isMuted`
|
||||
- `rate`
|
||||
- `audioTimePitchAlgorithm`: This value decides the `AVAudioTimePitchAlgorithm` used for each `AudioItem`. Implement `TimePitching` in your `AudioItem`-subclass to override individually for each `AudioItem`.
|
||||
|
||||
### Audio Session
|
||||
Remember to activate an audio session with an appropriate category for your app. This can be done with `AudioSessionCategory`:
|
||||
Remember to activate an audio session with an appropriate category for your app. This can be done with `AudioSessionController`:
|
||||
```swift
|
||||
try? AudioSessionController.set(category: .playback)
|
||||
//...
|
||||
@@ -74,22 +89,23 @@ try? AudioSessionController.set(category: .playback)
|
||||
try? AudioSessionController.activateSession()
|
||||
```
|
||||
|
||||
If you want audio to continue playing when the app is inactive, remember to activate background audio:
|
||||
**Important**: If you want audio to continue playing when the app is inactive, remember to activate background audio:
|
||||
App Settings -> Capabilities -> Background Modes -> Check 'Audio, AirPlay, and Picture in Picture'.
|
||||
|
||||
#### Interruptions
|
||||
If you are using the AudioSessionController for setting up the audio session, you can use it to handle interruptions too.
|
||||
If you are using the `AudioSessionController` for setting up the audio session, you can use it to handle interruptions too.
|
||||
Implement `AudioSessionControllerDelegate` and you will be notified by `handleInterruption(type: AVAudioSessionInterruptionType)`.
|
||||
If you are storing progress for playback time on items when the app quits, it can be a good idea to do it on interruptions as well.
|
||||
To disable interruption notifcations set `isObservingForInterruptions` to `false`.
|
||||
|
||||
### Now Playing Info
|
||||
The `AudioPlayer` will automatically update the `MPNowPlayingInfoCenter` with artist, title, album, artwork, time if the passed in `AudioItem` supports this.
|
||||
If you need to set additional properties for some items use `AudioPlayer.add(property:)`. Available properties can be found in `NowPlayingInfoProperty`.
|
||||
The `AudioPlayer` will automatically update the `MPNowPlayingInfoCenter` with artist, title, album, artwork and time if the passed in `AudioItem` supports this. This functionality can be turned off by setting `automaticallyUpdateNowPlayingInfo` to `false`.
|
||||
If you need to set additional properties for some items, access the player's `NowPlayingInfoController` and call `set(keyValue:)`. Available properties can be found in `NowPlayingInfoProperty`.
|
||||
|
||||
### Remote Commands
|
||||
**First** go to App Settings -> Capabilites -> Background Modes -> Check 'Remote notifications'
|
||||
|
||||
The player will handle remote commands received from `MPRemoteCommandCenter`'s shared instance, enabled by:
|
||||
To enable remote commands for the player you need to populate the RemoteCommands array for the player:
|
||||
```swift
|
||||
audioPlayer.remoteCommands = [
|
||||
.play,
|
||||
@@ -98,20 +114,17 @@ audioPlayer.remoteCommands = [
|
||||
.skipBackward(intervals: [30]),
|
||||
]
|
||||
```
|
||||
These commands will be activated for each `AudioItem`. If you need some audio items to have different commands, implement `RemoteCommandable` in a custom `AudioItem`-subclass. These commands will override the commands found in `AudioPlayer.remoteCommands` so make sure to supply all commands you need for that particular `AudioItem`.
|
||||
|
||||
These commands will be activated for each `AudioItem`. If you need some audio items to have different commands, implement `RemoteCommandable`. These commands will override the commands found in `AudioPlayer.remoteCommands` so make sure to supply all commands you need for that particular `AudioItem`.
|
||||
|
||||
**Remember** to go to App Settings -> Capabilites -> Background Modes -> Check 'Remote notifications'
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Currently some configuration options are supported:
|
||||
+ `automaticallyWaitsToMinimizeStalling`: Whether the player should delay playback start to minimize stalling. If you are streaming large audio files and playback start is slow, it can help to set this to `false`. Default is `true`.
|
||||
+ `bufferDuration`: The amount of seconds to be buffered by the player. Does not have any effect if `automaticallyWaitsToMinimizeStalling` is set to `true`.
|
||||
+ `timeEventFrequency`: This decides how ofen the delegate should be notified that a time unit elapsed in the playback.
|
||||
+ `volume`: The volume of the player. From 0.0 to 1.0.
|
||||
+ `automaticallyUpdateNowPlayingInfo`: If you want to handle updating of the `MPNowPlayingInfoCenter` yourself, set this to `false`. Default is `true`.
|
||||
#### Custom handlers for remote commands
|
||||
To supply custom handlers for your remote commands, just override the handlers contained in the player's `RemoteCommandController`:
|
||||
```swift
|
||||
let player = QueuedAudioPlayer()
|
||||
player.remoteCommandController.handlePlayCommand = { (event) in
|
||||
// Handle remote command here.
|
||||
}
|
||||
```
|
||||
All available overrides can be found by looking at `RemoteCommandController`.
|
||||
|
||||
## Author
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'SwiftAudio'
|
||||
s.version = '0.3.4'
|
||||
s.version = '0.6.1'
|
||||
s.summary = 'Easy audio streaming for iOS'
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
|
||||
Regular → Executable
+94
-204
@@ -10,19 +10,15 @@ import Foundation
|
||||
import AVFoundation
|
||||
import MediaPlayer
|
||||
|
||||
|
||||
protocol AVPlayerWrapperDelegate: class {
|
||||
|
||||
func AVWrapper(didChangeState state: AVPlayerWrapperState)
|
||||
func AVWrapperItemDidComplete()
|
||||
func AVWrapper(secondsElapsed seconds: Double)
|
||||
func AVWrapper(failedWithError error: Error?)
|
||||
func AVWrapper(seekTo seconds: Int, didFinish: Bool)
|
||||
func AVWrapper(didUpdateDuration duration: Double)
|
||||
|
||||
public enum PlaybackEndedReason: String {
|
||||
case playedUntilEnd
|
||||
case playerStopped
|
||||
case skippedToNext
|
||||
case skippedToPrevious
|
||||
case jumpedToIndex
|
||||
}
|
||||
|
||||
class AVPlayerWrapper {
|
||||
class AVPlayerWrapper: AVPlayerWrapperProtocol {
|
||||
|
||||
struct Constants {
|
||||
static let assetPlayableKey = "playable"
|
||||
@@ -35,19 +31,11 @@ class AVPlayerWrapper {
|
||||
let playerTimeObserver: AVPlayerTimeObserver
|
||||
let playerItemNotificationObserver: AVPlayerItemNotificationObserver
|
||||
let playerItemObserver: AVPlayerItemObserver
|
||||
|
||||
|
||||
/**
|
||||
True if the last call to load(from:playWhenReady) had playWhenReady=true.
|
||||
Cannot be set directly.
|
||||
*/
|
||||
var playWhenReady: Bool { return _playWhenReady }
|
||||
|
||||
private var _playWhenReady: Bool = true
|
||||
|
||||
/**
|
||||
The current `AudioPlayerState` of the player.
|
||||
*/
|
||||
var state: AVPlayerWrapperState { return _state }
|
||||
fileprivate var _playWhenReady: Bool = true
|
||||
|
||||
fileprivate var _state: AVPlayerWrapperState = AVPlayerWrapperState.idle {
|
||||
didSet {
|
||||
@@ -57,102 +45,12 @@ class AVPlayerWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The delegate receiving events.
|
||||
*/
|
||||
weak var delegate: AVPlayerWrapperDelegate?
|
||||
|
||||
// MARK: - AVPlayer Get Properties
|
||||
|
||||
/**
|
||||
The AVAsset for the currentItem.
|
||||
*/
|
||||
var currentAsset: AVAsset? {
|
||||
return currentItem?.asset
|
||||
}
|
||||
|
||||
/**
|
||||
The current item of the AVPlayer.
|
||||
*/
|
||||
var currentItem: AVPlayerItem? {
|
||||
return avPlayer.currentItem
|
||||
}
|
||||
|
||||
/**
|
||||
The duration of the current item.
|
||||
*/
|
||||
var duration: Double {
|
||||
if let seconds = currentItem?.duration.seconds, !seconds.isNaN {
|
||||
return seconds
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
The current time of the item in the player.
|
||||
*/
|
||||
var currentTime: Double {
|
||||
let seconds = avPlayer.currentTime().seconds
|
||||
return seconds.isNaN ? 0 : seconds
|
||||
}
|
||||
|
||||
/**
|
||||
The rate of the AVPlayer
|
||||
*/
|
||||
var rate: Float {
|
||||
return avPlayer.rate
|
||||
}
|
||||
|
||||
// MARK: - AVPlayer Config Properties
|
||||
|
||||
/**
|
||||
Indicates wether the player should automatically delay playback in order to minimize stalling.
|
||||
[Read more from Apple Documentation](https://developer.apple.com/documentation/avfoundation/avplayer/1643482-automaticallywaitstominimizestal)
|
||||
*/
|
||||
var automaticallyWaitsToMinimizeStalling: Bool {
|
||||
get { return avPlayer.automaticallyWaitsToMinimizeStalling }
|
||||
set { avPlayer.automaticallyWaitsToMinimizeStalling = newValue }
|
||||
}
|
||||
|
||||
/**
|
||||
The amount of seconds to be buffered by the player. Default value is 0 seconds, this means the AVPlayer will choose an appropriate level of buffering.
|
||||
|
||||
[Read more from Apple Documentation](https://developer.apple.com/documentation/avfoundation/avplayeritem/1643630-preferredforwardbufferduration)
|
||||
|
||||
- Important: This setting will have no effect if `automaticallyWaitsToMinimizeStalling` is set to `true`
|
||||
*/
|
||||
var bufferDuration: TimeInterval
|
||||
|
||||
/**
|
||||
Set this to decide how often the player should call the delegate with time progress events.
|
||||
*/
|
||||
var timeEventFrequency: TimeEventFrequency {
|
||||
didSet {
|
||||
playerTimeObserver.periodicObserverTimeInterval = timeEventFrequency.getTime()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The player volume, from 0.0 to 1.0
|
||||
Default is 1.0
|
||||
*/
|
||||
public var volume: Float {
|
||||
get { return avPlayer.volume }
|
||||
set { avPlayer.volume = newValue }
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
public init(timeEventFrequency: TimeEventFrequency = .everySecond) {
|
||||
|
||||
self.avPlayer = AVPlayer()
|
||||
public init(avPlayer: AVPlayer = AVPlayer()) {
|
||||
self.avPlayer = avPlayer
|
||||
self.playerObserver = AVPlayerObserver(player: avPlayer)
|
||||
self.playerTimeObserver = AVPlayerTimeObserver(player: avPlayer, periodicObserverTimeInterval: timeEventFrequency.getTime())
|
||||
self.playerItemNotificationObserver = AVPlayerItemNotificationObserver()
|
||||
self.playerItemObserver = AVPlayerItemObserver()
|
||||
|
||||
self.bufferDuration = 0
|
||||
self.timeEventFrequency = timeEventFrequency
|
||||
|
||||
self.playerObserver.delegate = self
|
||||
self.playerTimeObserver.delegate = self
|
||||
@@ -162,117 +60,108 @@ class AVPlayerWrapper {
|
||||
playerTimeObserver.registerForPeriodicTimeEvents()
|
||||
}
|
||||
|
||||
/**
|
||||
Start playback.
|
||||
|
||||
- throws: APError.PlaybackError
|
||||
*/
|
||||
func play() throws {
|
||||
guard currentItem != nil else {
|
||||
throw APError.PlaybackError.noLoadedItem
|
||||
// MARK: - AVPlayerWrapperProtocol
|
||||
|
||||
var state: AVPlayerWrapperState {
|
||||
return _state
|
||||
}
|
||||
|
||||
var reasonForWaitingToPlay: AVPlayer.WaitingReason? {
|
||||
return avPlayer.reasonForWaitingToPlay
|
||||
}
|
||||
|
||||
var currentItem: AVPlayerItem? {
|
||||
return avPlayer.currentItem
|
||||
}
|
||||
|
||||
var automaticallyWaitsToMinimizeStalling: Bool {
|
||||
get { return avPlayer.automaticallyWaitsToMinimizeStalling }
|
||||
set { avPlayer.automaticallyWaitsToMinimizeStalling = newValue }
|
||||
}
|
||||
|
||||
var currentTime: TimeInterval {
|
||||
let seconds = avPlayer.currentTime().seconds
|
||||
return seconds.isNaN ? 0 : seconds
|
||||
}
|
||||
|
||||
var duration: TimeInterval {
|
||||
if let seconds = currentItem?.asset.duration.seconds, !seconds.isNaN {
|
||||
return seconds
|
||||
}
|
||||
|
||||
guard avPlayer.timeControlStatus == .paused else {
|
||||
return
|
||||
else if let seconds = currentItem?.duration.seconds, !seconds.isNaN {
|
||||
return seconds
|
||||
}
|
||||
|
||||
else if let seconds = currentItem?.loadedTimeRanges.first?.timeRangeValue.duration.seconds,
|
||||
!seconds.isNaN {
|
||||
return seconds
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
weak var delegate: AVPlayerWrapperDelegate? = nil
|
||||
|
||||
var bufferDuration: TimeInterval = 0
|
||||
|
||||
var timeEventFrequency: TimeEventFrequency = .everySecond {
|
||||
didSet {
|
||||
playerTimeObserver.periodicObserverTimeInterval = timeEventFrequency.getTime()
|
||||
}
|
||||
}
|
||||
|
||||
var rate: Float {
|
||||
get { return avPlayer.rate }
|
||||
set { avPlayer.rate = newValue }
|
||||
}
|
||||
|
||||
var volume: Float {
|
||||
get { return avPlayer.volume }
|
||||
set { avPlayer.volume = newValue }
|
||||
}
|
||||
|
||||
var isMuted: Bool {
|
||||
get { return avPlayer.isMuted }
|
||||
set { avPlayer.isMuted = newValue }
|
||||
}
|
||||
|
||||
func play() {
|
||||
avPlayer.play()
|
||||
}
|
||||
|
||||
/**
|
||||
Will pause playback.
|
||||
|
||||
- throws: APError.PlaybackError
|
||||
*/
|
||||
func pause() throws {
|
||||
guard currentItem != nil else {
|
||||
throw APError.PlaybackError.noLoadedItem
|
||||
}
|
||||
|
||||
guard avPlayer.timeControlStatus == .playing || avPlayer.timeControlStatus == .waitingToPlayAtSpecifiedRate else {
|
||||
return
|
||||
}
|
||||
|
||||
func pause() {
|
||||
avPlayer.pause()
|
||||
}
|
||||
|
||||
/**
|
||||
Will toggle playback.
|
||||
*/
|
||||
func togglePlaying() throws {
|
||||
func togglePlaying() {
|
||||
switch avPlayer.timeControlStatus {
|
||||
case .playing, .waitingToPlayAtSpecifiedRate:
|
||||
try pause()
|
||||
pause()
|
||||
case .paused:
|
||||
try play()
|
||||
play()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Stop the player and remove the currently playing item.
|
||||
*/
|
||||
func stop() {
|
||||
try? pause()
|
||||
pause()
|
||||
reset(soft: false)
|
||||
}
|
||||
|
||||
/**
|
||||
Seek to a point in the item.
|
||||
|
||||
- parameter seconds: The point to move the player head, in seconds. If the given value is less than 0, 0 is used. If the value is larger than the duration, the duration is used.
|
||||
- throws: `APError.PlaybackError`
|
||||
*/
|
||||
func seek(to seconds: TimeInterval) throws {
|
||||
guard currentItem != nil else {
|
||||
throw APError.PlaybackError.noLoadedItem
|
||||
}
|
||||
let millis = Int64(max(min(seconds, duration), 0) * 1000)
|
||||
let time = CMTime(value: millis, timescale: 1000)
|
||||
avPlayer.seek(to: time) { (finished) in
|
||||
func seek(to seconds: TimeInterval) {
|
||||
avPlayer.seek(to: CMTimeMakeWithSeconds(seconds, preferredTimescale: 1000)) { (finished) in
|
||||
self.delegate?.AVWrapper(seekTo: Int(seconds), didFinish: finished)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Load an item from a URL string. Use this when streaming sound.
|
||||
|
||||
- parameter urlString: The AudioSource to load the item from.
|
||||
- parameter playWhenReady: Whether playback should start immediately when the item is ready. Default is `true`
|
||||
*/
|
||||
func load(fromUrlString urlString: String, playWhenReady: Bool = true) throws {
|
||||
|
||||
guard let url = URL(string: urlString) else {
|
||||
throw APError.LoadError.invalidSourceUrl(urlString)
|
||||
}
|
||||
|
||||
self.load(from: url, playWhenReady: playWhenReady)
|
||||
}
|
||||
|
||||
/**
|
||||
Load an item from a file. Use this when playing local.
|
||||
|
||||
- parameter filePath: The path to the sound file.
|
||||
- parameter playWhenReady: Whether playback should start immediately when the item is ready. Default is `true`
|
||||
*/
|
||||
func load(fromFilePath filePath: String, playWhenReady: Bool = true) throws {
|
||||
let url = URL(fileURLWithPath: filePath)
|
||||
self.load(from: url, playWhenReady: playWhenReady)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func load(from url: URL, playWhenReady: Bool) {
|
||||
|
||||
|
||||
func load(from url: URL, playWhenReady: Bool) {
|
||||
reset(soft: true)
|
||||
_playWhenReady = playWhenReady
|
||||
_state = .loading
|
||||
|
||||
|
||||
// Set item
|
||||
let currentAsset = AVURLAsset(url: url)
|
||||
let currentItem = AVPlayerItem(asset: currentAsset, automaticallyLoadedAssetKeys: [Constants.assetPlayableKey])
|
||||
currentItem.preferredForwardBufferDuration = bufferDuration
|
||||
avPlayer.replaceCurrentItem(with: currentItem)
|
||||
|
||||
|
||||
// Register for events
|
||||
playerTimeObserver.registerForBoundaryTimeEvents()
|
||||
playerObserver.startObserving()
|
||||
@@ -280,15 +169,16 @@ class AVPlayerWrapper {
|
||||
playerItemObserver.startObserving(item: currentItem)
|
||||
}
|
||||
|
||||
/**
|
||||
Reset to get ready for playing from a different source.
|
||||
*/
|
||||
// MARK: - Util
|
||||
|
||||
private func reset(soft: Bool) {
|
||||
playerItemObserver.stopObservingCurrentItem()
|
||||
playerTimeObserver.unregisterForBoundaryTimeEvents()
|
||||
playerItemNotificationObserver.stopObservingCurrentItem()
|
||||
|
||||
if !soft {
|
||||
avPlayer.replaceCurrentItem(with: nil)
|
||||
}
|
||||
playerTimeObserver.unregisterForBoundaryTimeEvents()
|
||||
playerItemNotificationObserver.stopObservingCurrentItem()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -297,7 +187,7 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate {
|
||||
|
||||
// MARK: - AVPlayerObserverDelegate
|
||||
|
||||
func player(didChangeTimeControlStatus status: AVPlayerTimeControlStatus) {
|
||||
func player(didChangeTimeControlStatus status: AVPlayer.TimeControlStatus) {
|
||||
switch status {
|
||||
case .paused:
|
||||
if currentItem == nil {
|
||||
@@ -313,13 +203,13 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func player(statusDidChange status: AVPlayerStatus) {
|
||||
func player(statusDidChange status: AVPlayer.Status) {
|
||||
switch status {
|
||||
|
||||
case .readyToPlay:
|
||||
self._state = .ready
|
||||
if _playWhenReady {
|
||||
try? self.play()
|
||||
self.play()
|
||||
}
|
||||
break
|
||||
|
||||
@@ -353,7 +243,7 @@ extension AVPlayerWrapper: AVPlayerItemNotificationObserverDelegate {
|
||||
// MARK: - AVPlayerItemNotificationObserverDelegate
|
||||
|
||||
func itemDidPlayToEndTime() {
|
||||
delegate?.AVWrapperItemDidComplete()
|
||||
delegate?.AVWrapper(itemPlaybackDoneWithReason: .playedUntilEnd)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// AVPlayerWrapperDelegate.swift
|
||||
// SwiftAudio
|
||||
//
|
||||
// Created by Jørgen Henrichsen on 26/10/2018.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
protocol AVPlayerWrapperDelegate: class {
|
||||
|
||||
func AVWrapper(didChangeState state: AVPlayerWrapperState)
|
||||
func AVWrapper(itemPlaybackDoneWithReason: PlaybackEndedReason)
|
||||
func AVWrapper(secondsElapsed seconds: Double)
|
||||
func AVWrapper(failedWithError error: Error?)
|
||||
func AVWrapper(seekTo seconds: Int, didFinish: Bool)
|
||||
func AVWrapper(didUpdateDuration duration: Double)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// AVPlayerWrapperProtocol.swift
|
||||
// SwiftAudio
|
||||
//
|
||||
// Created by Jørgen Henrichsen on 26/10/2018.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
|
||||
protocol AVPlayerWrapperProtocol {
|
||||
|
||||
var state: AVPlayerWrapperState { get }
|
||||
|
||||
var currentItem: AVPlayerItem? { get }
|
||||
|
||||
var currentTime: TimeInterval { get }
|
||||
|
||||
var duration: TimeInterval { get }
|
||||
|
||||
var reasonForWaitingToPlay: AVPlayer.WaitingReason? { get }
|
||||
|
||||
|
||||
var rate: Float { get set }
|
||||
|
||||
var delegate: AVPlayerWrapperDelegate? { get set }
|
||||
|
||||
var bufferDuration: TimeInterval { get set }
|
||||
|
||||
var timeEventFrequency: TimeEventFrequency { get set }
|
||||
|
||||
var volume: Float { get set }
|
||||
|
||||
var isMuted: Bool { get set }
|
||||
|
||||
var automaticallyWaitsToMinimizeStalling: Bool { get set }
|
||||
|
||||
|
||||
func play()
|
||||
|
||||
func pause()
|
||||
|
||||
func togglePlaying()
|
||||
|
||||
func stop()
|
||||
|
||||
func seek(to seconds: TimeInterval)
|
||||
|
||||
func load(from url: URL, playWhenReady: Bool)
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public enum AVPlayerWrapperState: String {
|
||||
/// The player is playing.
|
||||
case playing
|
||||
|
||||
/// No item loaded, the player is stopped. Call play(from:) to start loading.
|
||||
/// No item loaded, the player is stopped.
|
||||
case idle
|
||||
|
||||
}
|
||||
|
||||
Regular → Executable
+29
-3
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
public enum SourceType {
|
||||
case stream
|
||||
@@ -23,9 +24,15 @@ public protocol AudioItem {
|
||||
|
||||
}
|
||||
|
||||
public struct DefaultAudioItem: AudioItem {
|
||||
/// Make your `AudioItem`-subclass conform to this protocol to control which AVAudioTimePitchAlgorithm is used for each item.
|
||||
public protocol TimePitching {
|
||||
|
||||
func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm
|
||||
|
||||
}
|
||||
|
||||
public class DefaultAudioItem: AudioItem {
|
||||
|
||||
public var audioUrl: String
|
||||
|
||||
public var artist: String?
|
||||
@@ -66,10 +73,29 @@ public struct DefaultAudioItem: AudioItem {
|
||||
public func getSourceType() -> SourceType {
|
||||
return sourceType
|
||||
}
|
||||
|
||||
|
||||
public func getArtwork(_ handler: @escaping (UIImage?) -> Void) {
|
||||
handler(artwork)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// An AudioItem that also conforms to the `TimePitching`-protocol
|
||||
public class DefaultAudioItemTimePitching: DefaultAudioItem, TimePitching {
|
||||
|
||||
public var pitchAlgorithmType: AVAudioTimePitchAlgorithm
|
||||
|
||||
public override init(audioUrl: String, artist: String?, title: String?, albumTitle: String?, sourceType: SourceType, artwork: UIImage?) {
|
||||
self.pitchAlgorithmType = AVAudioTimePitchAlgorithm.lowQualityZeroLatency
|
||||
super.init(audioUrl: audioUrl, artist: artist, title: title, albumTitle: albumTitle, sourceType: sourceType, artwork: artwork)
|
||||
}
|
||||
|
||||
public init(audioUrl: String, artist: String?, title: String?, albumTitle: String?, sourceType: SourceType, artwork: UIImage?, audioTimePitchAlgorithm: AVAudioTimePitchAlgorithm) {
|
||||
self.pitchAlgorithmType = audioTimePitchAlgorithm
|
||||
super.init(audioUrl: audioUrl, artist: artist, title: title, albumTitle: albumTitle, sourceType: sourceType, artwork: artwork)
|
||||
}
|
||||
|
||||
public func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm {
|
||||
return pitchAlgorithmType
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+78
-69
@@ -14,7 +14,7 @@ public protocol AudioPlayerDelegate: class {
|
||||
|
||||
func audioPlayer(playerDidChangeState state: AudioPlayerState)
|
||||
|
||||
func audioPlayerItemDidComplete()
|
||||
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason)
|
||||
|
||||
func audioPlayer(secondsElapsed seconds: Double)
|
||||
|
||||
@@ -23,22 +23,23 @@ public protocol AudioPlayerDelegate: class {
|
||||
func audioPlayer(seekTo seconds: Int, didFinish: Bool)
|
||||
|
||||
func audioPlayer(didUpdateDuration duration: Double)
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
The main AudioPlayer.
|
||||
- warning: DO NOT USE THIS CLASS, use `SimpleAudioPlayer` or `QueuedAudioPlayer`
|
||||
*/
|
||||
public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
|
||||
let wrapper: AVPlayerWrapper
|
||||
let nowPlayingInfoController: NowPlayingInfoController
|
||||
let remoteCommandController: RemoteCommandController
|
||||
private var _wrapper: AVPlayerWrapperProtocol
|
||||
|
||||
/// The wrapper around the underlying AVPlayer
|
||||
var wrapper: AVPlayerWrapperProtocol {
|
||||
return _wrapper
|
||||
}
|
||||
|
||||
public let nowPlayingInfoController: NowPlayingInfoController
|
||||
public let remoteCommandController: RemoteCommandController
|
||||
public weak var delegate: AudioPlayerDelegate?
|
||||
|
||||
var _currentItem: AudioItem?
|
||||
|
||||
public weak var delegate: AudioPlayerDelegate?
|
||||
public var currentItem: AudioItem? {
|
||||
return _currentItem
|
||||
}
|
||||
@@ -48,11 +49,18 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
*/
|
||||
public var automaticallyUpdateNowPlayingInfo: Bool = true
|
||||
|
||||
/**
|
||||
Controls the time pitch algorithm applied to each item loaded into the player.
|
||||
If the loaded `AudioItem` conforms to `TimePitcher`-protocol this will be overriden.
|
||||
*/
|
||||
public var audioTimePitchAlgorithm: AVAudioTimePitchAlgorithm = AVAudioTimePitchAlgorithm.lowQualityZeroLatency
|
||||
|
||||
/**
|
||||
Default remote commands to use for each playing item
|
||||
*/
|
||||
public var remoteCommands: [RemoteCommand] = []
|
||||
|
||||
|
||||
// MARK: - Getters from AVPlayerWrapper
|
||||
|
||||
/**
|
||||
@@ -69,13 +77,6 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
return wrapper.duration
|
||||
}
|
||||
|
||||
/**
|
||||
The current rate of the underlying `AudioPlayer`.
|
||||
*/
|
||||
public var rate: Float {
|
||||
return wrapper.rate
|
||||
}
|
||||
|
||||
/**
|
||||
The current state of the underlying `AudioPlayer`.
|
||||
*/
|
||||
@@ -85,42 +86,47 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
|
||||
// MARK: - Setters for AVPlayerWrapper
|
||||
|
||||
/**
|
||||
Indicates wether the player should automatically delay playback in order to minimize stalling.
|
||||
[Read more from Apple Documentation](https://developer.apple.com/documentation/avfoundation/avplayer/1643482-automaticallywaitstominimizestal)
|
||||
*/
|
||||
public var automaticallyWaitsToMinimizeStalling: Bool {
|
||||
get { return wrapper.automaticallyWaitsToMinimizeStalling }
|
||||
set { wrapper.automaticallyWaitsToMinimizeStalling = newValue }
|
||||
}
|
||||
|
||||
/**
|
||||
The amount of seconds to be buffered by the player. Default value is 0 seconds, this means the AVPlayer will choose an appropriate level of buffering.
|
||||
|
||||
[Read more from Apple Documentation](https://developer.apple.com/documentation/avfoundation/avplayeritem/1643630-preferredforwardbufferduration)
|
||||
|
||||
- Important: This setting will have no effect if `automaticallyWaitsToMinimizeStalling` is set to `true`
|
||||
- Important: This setting will have no effect if `automaticallyWaitsToMinimizeStalling` is set to `true` in the AVPlayer
|
||||
*/
|
||||
public var bufferDuration: TimeInterval {
|
||||
get { return wrapper.bufferDuration }
|
||||
set { wrapper.bufferDuration = newValue }
|
||||
set { _wrapper.bufferDuration = newValue }
|
||||
}
|
||||
|
||||
/**
|
||||
Set this to decide how often the player should call the delegate with time progress events.
|
||||
*/
|
||||
public var timeEventFrquency: TimeEventFrequency {
|
||||
public var timeEventFrequency: TimeEventFrequency {
|
||||
get { return wrapper.timeEventFrequency }
|
||||
set { wrapper.timeEventFrequency = newValue }
|
||||
set { _wrapper.timeEventFrequency = newValue }
|
||||
}
|
||||
|
||||
/**
|
||||
The player volume, from 0.0 to 1.0
|
||||
Default is 1.0
|
||||
Indicates whether the player should automatically delay playback in order to minimize stalling
|
||||
*/
|
||||
public var automaticallyWaitsToMinimizeStalling: Bool {
|
||||
get { return wrapper.automaticallyWaitsToMinimizeStalling }
|
||||
set { _wrapper.automaticallyWaitsToMinimizeStalling = newValue }
|
||||
}
|
||||
|
||||
public var volume: Float {
|
||||
get { return wrapper.volume }
|
||||
set { wrapper.volume = newValue }
|
||||
set { _wrapper.volume = newValue }
|
||||
}
|
||||
|
||||
public var isMuted: Bool {
|
||||
get { return wrapper.isMuted }
|
||||
set { _wrapper.isMuted = newValue }
|
||||
}
|
||||
|
||||
public var rate: Float {
|
||||
get { return wrapper.rate }
|
||||
set { _wrapper.rate = newValue }
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
@@ -130,12 +136,14 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
|
||||
- parameter infoCenter: The InfoCenter to update. Default is `MPNowPlayingInfoCenter.default()`.
|
||||
*/
|
||||
init(infoCenter: MPNowPlayingInfoCenter = MPNowPlayingInfoCenter.default()) {
|
||||
self.wrapper = AVPlayerWrapper()
|
||||
self.nowPlayingInfoController = NowPlayingInfoController(infoCenter: infoCenter)
|
||||
self.remoteCommandController = RemoteCommandController()
|
||||
public init(avPlayer: AVPlayer = AVPlayer(),
|
||||
nowPlayingInfoController: NowPlayingInfoController = NowPlayingInfoController(),
|
||||
remoteCommandController: RemoteCommandController = RemoteCommandController()) {
|
||||
self._wrapper = AVPlayerWrapper(avPlayer: avPlayer)
|
||||
self.nowPlayingInfoController = nowPlayingInfoController
|
||||
self.remoteCommandController = remoteCommandController
|
||||
|
||||
self.wrapper.delegate = self
|
||||
self._wrapper.delegate = self
|
||||
self.remoteCommandController.audioPlayer = self
|
||||
}
|
||||
|
||||
@@ -147,17 +155,29 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
- parameter item: The AudioItem to load. The info given in this item is the one used for the InfoCenter.
|
||||
- parameter playWhenReady: Immediately start playback when the item is ready. Default is `true`. If you disable this you have to call play() or togglePlay() when the `state` switches to `ready`.
|
||||
*/
|
||||
func loadItem(_ item: AudioItem, playWhenReady: Bool = true) throws {
|
||||
public func load(item: AudioItem, playWhenReady: Bool = true) throws {
|
||||
print("Loading: \(item)")
|
||||
switch item.getSourceType() {
|
||||
case .stream:
|
||||
try self.wrapper.load(fromUrlString: item.getSourceUrl(), playWhenReady: playWhenReady)
|
||||
if let url = URL(string: item.getSourceUrl()) {
|
||||
wrapper.load(from: url, playWhenReady: playWhenReady)
|
||||
}
|
||||
else {
|
||||
throw APError.LoadError.invalidSourceUrl(item.getSourceUrl())
|
||||
}
|
||||
case .file:
|
||||
try self.wrapper.load(fromFilePath: item.getSourceUrl(), playWhenReady: playWhenReady)
|
||||
wrapper.load(from: URL(fileURLWithPath: item.getSourceUrl()), playWhenReady: playWhenReady)
|
||||
}
|
||||
|
||||
if let item = item as? TimePitching {
|
||||
wrapper.currentItem?.audioTimePitchAlgorithm = item.getPitchAlgorithmType()
|
||||
}
|
||||
else {
|
||||
wrapper.currentItem?.audioTimePitchAlgorithm = audioTimePitchAlgorithm
|
||||
}
|
||||
|
||||
self._currentItem = item
|
||||
set(item: item)
|
||||
self.updateMetaValues(item: item)
|
||||
setArtwork(forItem: item)
|
||||
enableRemoteCommands(forItem: item)
|
||||
}
|
||||
@@ -165,28 +185,29 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
/**
|
||||
Toggle playback status.
|
||||
*/
|
||||
public func togglePlaying() throws {
|
||||
try self.wrapper.togglePlaying()
|
||||
public func togglePlaying() {
|
||||
self.wrapper.togglePlaying()
|
||||
}
|
||||
|
||||
/**
|
||||
Start playback
|
||||
*/
|
||||
public func play() throws {
|
||||
try self.wrapper.play()
|
||||
public func play() {
|
||||
self.wrapper.play()
|
||||
}
|
||||
|
||||
/**
|
||||
Pause playback
|
||||
*/
|
||||
public func pause() throws {
|
||||
try self.wrapper.pause()
|
||||
public func pause() {
|
||||
self.wrapper.pause()
|
||||
}
|
||||
|
||||
/**
|
||||
Stop playback, resetting the player.
|
||||
*/
|
||||
public func stop() {
|
||||
AVWrapper(itemPlaybackDoneWithReason: .playerStopped)
|
||||
self.reset()
|
||||
self.wrapper.stop()
|
||||
}
|
||||
@@ -194,16 +215,12 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
/**
|
||||
Seek to a specific time in the item.
|
||||
*/
|
||||
public func seek(to seconds: TimeInterval) throws {
|
||||
try self.wrapper.seek(to: seconds)
|
||||
public func seek(to seconds: TimeInterval) {
|
||||
self.wrapper.seek(to: seconds)
|
||||
}
|
||||
|
||||
// MARK: - Remote Command Center
|
||||
|
||||
/**
|
||||
Set the remote commands that should be activated and handled.
|
||||
Calling this will disable all earlier enabled commands, so include all commands you need.
|
||||
*/
|
||||
func enableRemoteCommands(_ commands: [RemoteCommand]) {
|
||||
self.remoteCommandController.enable(commands: commands)
|
||||
}
|
||||
@@ -219,21 +236,15 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
|
||||
// MARK: - NowPlayingInfo
|
||||
|
||||
/**
|
||||
Reloads the NowPlayingInfo from the current AudioItem.
|
||||
*/
|
||||
/// Reload all NowPlayingInfo for the playing item.
|
||||
public func reloadNowPlayingInfo() {
|
||||
guard let item = currentItem else { return }
|
||||
set(item: item)
|
||||
updateMetaValues(item: item)
|
||||
setArtwork(forItem: item)
|
||||
updatePlaybackValues()
|
||||
}
|
||||
|
||||
public func add(property: NowPlayingInfoKeyValue) {
|
||||
self.nowPlayingInfoController.set(keyValue: property)
|
||||
}
|
||||
|
||||
func set(item: AudioItem) {
|
||||
func updateMetaValues(item: AudioItem) {
|
||||
guard automaticallyUpdateNowPlayingInfo else { return }
|
||||
|
||||
nowPlayingInfoController.set(keyValues: [
|
||||
@@ -247,11 +258,9 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
guard automaticallyUpdateNowPlayingInfo else { return }
|
||||
item.getArtwork { (image) in
|
||||
if let image = image {
|
||||
|
||||
let artwork = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { (size) -> UIImage in
|
||||
return image
|
||||
})
|
||||
|
||||
self.nowPlayingInfoController.set(keyValue: MediaItemProperty.artwork(artwork))
|
||||
}
|
||||
}
|
||||
@@ -266,7 +275,7 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func reset() {
|
||||
func reset() {
|
||||
self._currentItem = nil
|
||||
}
|
||||
|
||||
@@ -280,8 +289,8 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
self.delegate?.audioPlayer(playerDidChangeState: state)
|
||||
}
|
||||
|
||||
func AVWrapperItemDidComplete() {
|
||||
self.delegate?.audioPlayerItemDidComplete()
|
||||
func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {
|
||||
self.delegate?.audioPlayer(itemPlaybackEndedWithReason: reason)
|
||||
}
|
||||
|
||||
func AVWrapper(secondsElapsed seconds: Double) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// AudioSession.swift
|
||||
// SwiftAudio
|
||||
//
|
||||
// Created by Jørgen Henrichsen on 02/11/2018.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
|
||||
protocol AudioSession {
|
||||
|
||||
var isOtherAudioPlaying: Bool { get }
|
||||
|
||||
var category: AVAudioSession.Category { get }
|
||||
|
||||
var mode: AVAudioSession.Mode { get }
|
||||
|
||||
var categoryOptions: AVAudioSession.CategoryOptions { get }
|
||||
|
||||
var availableCategories: [AVAudioSession.Category] { get }
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
func setCategory(_ category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions) throws
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
func setCategory(_ category: AVAudioSession.Category, mode: AVAudioSession.Mode, policy: AVAudioSession.RouteSharingPolicy, options: AVAudioSession.CategoryOptions) throws
|
||||
|
||||
func setActive(_ active: Bool, options: AVAudioSession.SetActiveOptions) throws
|
||||
|
||||
}
|
||||
|
||||
extension AVAudioSession: AudioSession {}
|
||||
+12
-56
@@ -8,54 +8,12 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
/**
|
||||
An enum wrapper around the AVAudioSessionCategories.
|
||||
For detailed info about the categories, see: [AudioSession Programming Guide](https://developer.apple.com/library/content/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/AudioSessionCategoriesandModes/AudioSessionCategoriesandModes.html#//apple_ref/doc/uid/TP40007875-CH10)
|
||||
*/
|
||||
public enum AudioSessionCategory {
|
||||
|
||||
case ambient
|
||||
|
||||
case soloAmbient
|
||||
|
||||
case playback
|
||||
|
||||
case record
|
||||
|
||||
case playAndRecord
|
||||
|
||||
case multiRoute
|
||||
|
||||
func getValue() -> String {
|
||||
switch self {
|
||||
|
||||
case .ambient:
|
||||
return AVAudioSessionCategoryAmbient
|
||||
|
||||
case .soloAmbient:
|
||||
return AVAudioSessionCategorySoloAmbient
|
||||
|
||||
case .playback:
|
||||
return AVAudioSessionCategoryPlayback
|
||||
|
||||
case .record:
|
||||
return AVAudioSessionCategoryRecord
|
||||
|
||||
case .playAndRecord:
|
||||
return AVAudioSessionCategoryPlayAndRecord
|
||||
|
||||
case .multiRoute:
|
||||
return AVAudioSessionCategoryMultiRoute
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public protocol AudioSessionControllerDelegate: class {
|
||||
func handleInterruption(type: AVAudioSessionInterruptionType)
|
||||
func handleInterruption(type: AVAudioSession.InterruptionType)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Simple controller for the `AVAudioSession`. If you need more advanced options, just use the `AVAudioSession` directly.
|
||||
- warning: Do not combine usage of this and `AVAudioSession` directly, chose one.
|
||||
@@ -64,7 +22,7 @@ public class AudioSessionController {
|
||||
|
||||
public static let shared = AudioSessionController()
|
||||
|
||||
private let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
|
||||
private let audioSession: AudioSession
|
||||
private let notificationCenter: NotificationCenter = NotificationCenter.default
|
||||
private var _isObservingForInterruptions: Bool = false
|
||||
|
||||
@@ -107,13 +65,14 @@ public class AudioSessionController {
|
||||
|
||||
public weak var delegate: AudioSessionControllerDelegate?
|
||||
|
||||
private init() {
|
||||
init(audioSession: AudioSession = AVAudioSession.sharedInstance()) {
|
||||
self.audioSession = audioSession
|
||||
registerForInterruptionNotification()
|
||||
}
|
||||
|
||||
public func activateSession() throws {
|
||||
do {
|
||||
try audioSession.setActive(true)
|
||||
try audioSession.setActive(true, options: [])
|
||||
audioSessionIsActive = true
|
||||
}
|
||||
catch let error { throw error }
|
||||
@@ -121,17 +80,14 @@ public class AudioSessionController {
|
||||
|
||||
public func deactivateSession() throws {
|
||||
do {
|
||||
try audioSession.setActive(false)
|
||||
try audioSession.setActive(false, options: [])
|
||||
audioSessionIsActive = false
|
||||
}
|
||||
catch let error { throw error }
|
||||
}
|
||||
|
||||
/**
|
||||
Set the audiosession.
|
||||
*/
|
||||
public func set(category: AudioSessionCategory) throws {
|
||||
try audioSession.setCategory(category.getValue())
|
||||
public func set(category: AVAudioSession.Category) throws {
|
||||
try audioSession.setCategory(category, mode: audioSession.mode, options: audioSession.categoryOptions)
|
||||
}
|
||||
|
||||
// MARK: - Interruptions
|
||||
@@ -139,20 +95,20 @@ public class AudioSessionController {
|
||||
private func registerForInterruptionNotification() {
|
||||
notificationCenter.addObserver(self,
|
||||
selector: #selector(handleInterruption),
|
||||
name: .AVAudioSessionInterruption,
|
||||
name: AVAudioSession.interruptionNotification,
|
||||
object: nil)
|
||||
_isObservingForInterruptions = true
|
||||
}
|
||||
|
||||
private func unregisterForInterruptionNotification() {
|
||||
notificationCenter.removeObserver(self, name: .AVAudioSessionInterruption, object: nil)
|
||||
notificationCenter.removeObserver(self, name: AVAudioSession.interruptionNotification, object: nil)
|
||||
_isObservingForInterruptions = false
|
||||
}
|
||||
|
||||
@objc func handleInterruption(notification: Notification) {
|
||||
guard let userInfo = notification.userInfo,
|
||||
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
|
||||
let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
|
||||
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
|
||||
return
|
||||
}
|
||||
|
||||
+6
-4
@@ -14,16 +14,18 @@ public protocol NowPlayingInfoKeyValue {
|
||||
func getValue() -> Any?
|
||||
}
|
||||
|
||||
/**
|
||||
Wrapper class to control the NowPlayingInfoCenter
|
||||
*/
|
||||
public class NowPlayingInfoController {
|
||||
|
||||
let infoCenter: MPNowPlayingInfoCenter
|
||||
|
||||
var info: [String: Any]
|
||||
|
||||
public init(infoCenter: MPNowPlayingInfoCenter) {
|
||||
/**
|
||||
Create a new NowPlayingInfoController.
|
||||
|
||||
- parameter infoCenter: The MPNowPlayingInfoCenter to use. Default is `MPNowPlayingInfoCenter.default()`
|
||||
*/
|
||||
public init(infoCenter: MPNowPlayingInfoCenter = MPNowPlayingInfoCenter.default()) {
|
||||
self.infoCenter = infoCenter
|
||||
self.info = [:]
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class AVPlayerItemObserver: NSObject {
|
||||
|
||||
private struct AVPlayerItemKeyPath {
|
||||
static let duration = #keyPath(AVPlayerItem.duration)
|
||||
static let loadedTimeRanges = #keyPath(AVPlayerItem.loadedTimeRanges)
|
||||
}
|
||||
|
||||
var isObserving: Bool = false
|
||||
@@ -53,11 +54,13 @@ class AVPlayerItemObserver: NSObject {
|
||||
self.isObserving = true
|
||||
self.observingItem = item
|
||||
item.addObserver(self, forKeyPath: AVPlayerItemKeyPath.duration, options: [.new], context: &AVPlayerItemObserver.context)
|
||||
item.addObserver(self, forKeyPath: AVPlayerItemKeyPath.loadedTimeRanges, options: [.new], context: &AVPlayerItemObserver.context)
|
||||
}
|
||||
}
|
||||
|
||||
private func stopObservingCurrentItem() {
|
||||
func stopObservingCurrentItem() {
|
||||
observingItem?.removeObserver(self, forKeyPath: AVPlayerItemKeyPath.duration, context: &AVPlayerItemObserver.context)
|
||||
observingItem?.removeObserver(self, forKeyPath: AVPlayerItemKeyPath.loadedTimeRanges, context: &AVPlayerItemObserver.context)
|
||||
self.isObserving = false
|
||||
self.observingItem = nil
|
||||
}
|
||||
@@ -73,8 +76,12 @@ class AVPlayerItemObserver: NSObject {
|
||||
if let duration = change?[.newKey] as? CMTime {
|
||||
self.delegate?.item(didUpdateDuration: duration.seconds)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
||||
case AVPlayerItemKeyPath.loadedTimeRanges:
|
||||
if let ranges = change?[.newKey] as? [NSValue], let duration = ranges.first?.timeRangeValue.duration {
|
||||
self.delegate?.item(didUpdateDuration: duration.seconds)
|
||||
}
|
||||
default: break
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ protocol AVPlayerObserverDelegate: class {
|
||||
/**
|
||||
Called when the AVPlayer.status changes.
|
||||
*/
|
||||
func player(statusDidChange status: AVPlayerStatus)
|
||||
func player(statusDidChange status: AVPlayer.Status)
|
||||
|
||||
/**
|
||||
Called when the AVPlayer.timeControlStatus changes.
|
||||
*/
|
||||
func player(didChangeTimeControlStatus status: AVPlayerTimeControlStatus)
|
||||
func player(didChangeTimeControlStatus status: AVPlayer.TimeControlStatus)
|
||||
|
||||
}
|
||||
|
||||
@@ -92,9 +92,9 @@ class AVPlayerObserver: NSObject {
|
||||
}
|
||||
|
||||
private func handleStatusChange(_ change: [NSKeyValueChangeKey: Any]?) {
|
||||
let status: AVPlayerStatus
|
||||
let status: AVPlayer.Status
|
||||
if let statusNumber = change?[.newKey] as? NSNumber {
|
||||
status = AVPlayerStatus(rawValue: statusNumber.intValue)!
|
||||
status = AVPlayer.Status(rawValue: statusNumber.intValue)!
|
||||
}
|
||||
else {
|
||||
status = .unknown
|
||||
@@ -104,9 +104,9 @@ class AVPlayerObserver: NSObject {
|
||||
|
||||
private func handleTimeControlStatusChange(_ change: [NSKeyValueChangeKey: Any]?) {
|
||||
|
||||
let status: AVPlayerTimeControlStatus
|
||||
let status: AVPlayer.TimeControlStatus
|
||||
if let statusNumber = change?[.newKey] as? NSNumber {
|
||||
status = AVPlayerTimeControlStatus(rawValue: statusNumber.intValue)!
|
||||
status = AVPlayer.TimeControlStatus(rawValue: statusNumber.intValue)!
|
||||
delegate?.player(didChangeTimeControlStatus: status)
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+72
-10
@@ -20,10 +20,10 @@ class QueueManager<T> {
|
||||
}
|
||||
|
||||
public var nextItems: [T] {
|
||||
guard _currentIndex < _items.count else {
|
||||
guard _currentIndex + 1 < _items.count else {
|
||||
return []
|
||||
}
|
||||
return Array(_items[_currentIndex + 1..<items.count])
|
||||
return Array(_items[_currentIndex + 1..<_items.count])
|
||||
}
|
||||
|
||||
public var previousItems: [T] {
|
||||
@@ -71,6 +71,21 @@ class QueueManager<T> {
|
||||
_items.append(contentsOf: items)
|
||||
}
|
||||
|
||||
/**
|
||||
Add an array of items to the queue at a given index.
|
||||
|
||||
- parameter items: The `AudioItem`s to be added.
|
||||
- parameter at: The index to insert the items at.
|
||||
*/
|
||||
public func addItems(_ items: [T], at index: Int) throws {
|
||||
guard index >= 0 && _items.count > index else {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "Index for addition has to be positive and smaller than the count of current items (\(_items.count))")
|
||||
}
|
||||
|
||||
_items.insert(contentsOf: items, at: index)
|
||||
if (_currentIndex >= index) { _currentIndex = _currentIndex + items.count }
|
||||
}
|
||||
|
||||
/**
|
||||
Get the next item in the queue, if there are any.
|
||||
Will update the current item.
|
||||
@@ -119,9 +134,10 @@ class QueueManager<T> {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "Cannot jump to the current item")
|
||||
}
|
||||
|
||||
guard index >= 0 && items.count > index else {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "The jump index has to be positive and smaller thant the count of current items (\(items.count))")
|
||||
guard index >= 0 && _items.count > index else {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "The jump index has to be positive and smaller thant the count of current items (\(_items.count))")
|
||||
}
|
||||
|
||||
_currentIndex = index
|
||||
return _items[index]
|
||||
}
|
||||
@@ -140,14 +156,15 @@ class QueueManager<T> {
|
||||
}
|
||||
|
||||
guard fromIndex >= 0 && fromIndex < _items.count else {
|
||||
throw APError.QueueError.invalidIndex(index: fromIndex, message: "The fromIndex has to be positive and smaller than the count of current items (\(items.count)).")
|
||||
throw APError.QueueError.invalidIndex(index: fromIndex, message: "The fromIndex has to be positive and smaller than the count of current items (\(_items.count)).")
|
||||
}
|
||||
|
||||
guard toIndex >= 0 && toIndex < _items.count else {
|
||||
throw APError.QueueError.invalidIndex(index: toIndex, message: "The toIndex has to be positive and smaller than the count of current items (\(items.count)).")
|
||||
throw APError.QueueError.invalidIndex(index: toIndex, message: "The toIndex has to be positive and smaller than the count of current items (\(_items.count)).")
|
||||
}
|
||||
|
||||
_items.insert(_items.remove(at: fromIndex), at: toIndex)
|
||||
let item = try removeItem(at: fromIndex)
|
||||
try addItems([item], at: toIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,16 +175,61 @@ class QueueManager<T> {
|
||||
- returns: The removed item.
|
||||
*/
|
||||
@discardableResult
|
||||
public func remove(atIndex index: Int) throws -> T {
|
||||
public func removeItem(at index: Int) throws -> T {
|
||||
guard index != _currentIndex else {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "Cannot remove the current item!")
|
||||
}
|
||||
|
||||
guard index >= 0 && _items.count > index else {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "Index for removal has to be postivie and smaller than the count of current items (\(items.count)).")
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "Index for removal has to be positive and smaller than the count of current items (\(_items.count)).")
|
||||
}
|
||||
|
||||
if index < _currentIndex {
|
||||
_currentIndex = _currentIndex - 1
|
||||
}
|
||||
|
||||
return _items.remove(at: index)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Replace the current item with a new one. If there is no current item, it is equivalent to calling add(item:).
|
||||
|
||||
- parameter item: The item to set as the new current item.
|
||||
*/
|
||||
public func replaceCurrentItem(with item: T) {
|
||||
if current == nil {
|
||||
self.addItem(item)
|
||||
}
|
||||
|
||||
self._items[_currentIndex] = item
|
||||
}
|
||||
|
||||
/**
|
||||
Remove all previous items in the queue.
|
||||
If no previous items exist, no action will be taken.
|
||||
*/
|
||||
public func removePreviousItems() {
|
||||
guard currentIndex > 0 else { return }
|
||||
_items.removeSubrange(0..<_currentIndex)
|
||||
_currentIndex = 0
|
||||
}
|
||||
|
||||
/**
|
||||
Remove upcoming items.
|
||||
If no upcoming items exist, no action will be taken.
|
||||
*/
|
||||
public func removeUpcomingItems() {
|
||||
let nextIndex = _currentIndex + 1
|
||||
guard nextIndex < _items.count else { return }
|
||||
_items.removeSubrange(nextIndex..<_items.count)
|
||||
}
|
||||
|
||||
/**
|
||||
Removes all items for queue
|
||||
*/
|
||||
public func clearQueue() {
|
||||
_currentIndex = 0
|
||||
_items.removeAll()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Regular → Executable
+55
-13
@@ -21,14 +21,21 @@ public class QueuedAudioPlayer: AudioPlayer {
|
||||
*/
|
||||
public var automaticallyPlayNextSong: Bool = true
|
||||
|
||||
public override init(infoCenter: MPNowPlayingInfoCenter = MPNowPlayingInfoCenter.default()) {
|
||||
super.init(infoCenter: infoCenter)
|
||||
}
|
||||
|
||||
public override var currentItem: AudioItem? {
|
||||
return queueManager.current
|
||||
}
|
||||
|
||||
/**
|
||||
Stops the player and clears the queue.
|
||||
*/
|
||||
public override func stop() {
|
||||
super.stop()
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
queueManager.clearQueue()
|
||||
}
|
||||
|
||||
/**
|
||||
The previous items held by the queue.
|
||||
*/
|
||||
@@ -43,6 +50,17 @@ public class QueuedAudioPlayer: AudioPlayer {
|
||||
return queueManager.nextItems
|
||||
}
|
||||
|
||||
/**
|
||||
Will replace the current item with a new one and load it into the player.
|
||||
|
||||
- parameter item: The AudioItem to replace the current item.
|
||||
- throws: APError.LoadError
|
||||
*/
|
||||
public override func load(item: AudioItem, playWhenReady: Bool) throws {
|
||||
try super.load(item: item, playWhenReady: playWhenReady)
|
||||
queueManager.replaceCurrentItem(with: item)
|
||||
}
|
||||
|
||||
/**
|
||||
Add a single item to the queue.
|
||||
|
||||
@@ -53,7 +71,7 @@ public class QueuedAudioPlayer: AudioPlayer {
|
||||
public func add(item: AudioItem, playWhenReady: Bool = true) throws {
|
||||
if currentItem == nil {
|
||||
queueManager.addItem(item)
|
||||
try self.loadItem(item, playWhenReady: playWhenReady)
|
||||
try self.load(item: item, playWhenReady: playWhenReady)
|
||||
}
|
||||
else {
|
||||
queueManager.addItem(item)
|
||||
@@ -70,29 +88,35 @@ public class QueuedAudioPlayer: AudioPlayer {
|
||||
public func add(items: [AudioItem], playWhenReady: Bool = true) throws {
|
||||
if currentItem == nil {
|
||||
queueManager.addItems(items)
|
||||
try self.loadItem(currentItem!, playWhenReady: playWhenReady)
|
||||
try self.load(item: currentItem!, playWhenReady: playWhenReady)
|
||||
}
|
||||
else {
|
||||
queueManager.addItems(items)
|
||||
}
|
||||
}
|
||||
|
||||
public func add(items: [AudioItem], at index: Int) throws {
|
||||
try queueManager.addItems(items, at: index)
|
||||
}
|
||||
|
||||
/**
|
||||
Step to the next item in the queue.
|
||||
|
||||
- throws: `APError`
|
||||
*/
|
||||
public func next() throws {
|
||||
AVWrapper(itemPlaybackDoneWithReason: .skippedToNext)
|
||||
let nextItem = try queueManager.next()
|
||||
try self.loadItem(nextItem, playWhenReady: true)
|
||||
try self.load(item: nextItem, playWhenReady: true)
|
||||
}
|
||||
|
||||
/**
|
||||
Step to the previous item in the queue.
|
||||
*/
|
||||
public func previous() throws {
|
||||
AVWrapper(itemPlaybackDoneWithReason: .skippedToPrevious)
|
||||
let previousItem = try queueManager.previous()
|
||||
try self.loadItem(previousItem, playWhenReady: true)
|
||||
try self.load(item: previousItem, playWhenReady: true)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,8 +125,8 @@ public class QueuedAudioPlayer: AudioPlayer {
|
||||
- parameter index: The index of the item to remove.
|
||||
- throws: `APError.QueueError`
|
||||
*/
|
||||
public func removeItem(atIndex index: Int) throws {
|
||||
try queueManager.remove(atIndex: index)
|
||||
public func removeItem(at index: Int) throws {
|
||||
try queueManager.removeItem(at: index)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,8 +137,10 @@ public class QueuedAudioPlayer: AudioPlayer {
|
||||
- throws: `APError`
|
||||
*/
|
||||
public func jumpToItem(atIndex index: Int, playWhenReady: Bool = true) throws {
|
||||
AVWrapper(itemPlaybackDoneWithReason: .jumpedToIndex)
|
||||
|
||||
let item = try queueManager.jump(to: index)
|
||||
try self.loadItem(item, playWhenReady: playWhenReady)
|
||||
try self.load(item: item, playWhenReady: playWhenReady)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,10 +154,26 @@ public class QueuedAudioPlayer: AudioPlayer {
|
||||
try queueManager.moveItem(fromIndex: fromIndex, toIndex: toIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
Remove all upcoming items, those returned by `next()`
|
||||
*/
|
||||
public func removeUpcomingItems() {
|
||||
queueManager.removeUpcomingItems()
|
||||
}
|
||||
|
||||
/**
|
||||
Remove all previous items, those returned by `previous()`
|
||||
*/
|
||||
public func removePreviousItems() {
|
||||
queueManager.removePreviousItems()
|
||||
}
|
||||
|
||||
// MARK: - AVPlayerWrapperDelegate
|
||||
|
||||
override func AVWrapperItemDidComplete() {
|
||||
super.AVWrapperItemDidComplete()
|
||||
override func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {
|
||||
super.AVWrapper(itemPlaybackDoneWithReason: reason)
|
||||
guard reason == .playedUntilEnd else { return }
|
||||
|
||||
if automaticallyPlayNextSong {
|
||||
try? self.next()
|
||||
}
|
||||
|
||||
+41
-66
@@ -14,27 +14,29 @@ public protocol RemoteCommandable {
|
||||
|
||||
public class RemoteCommandController {
|
||||
|
||||
private let center = MPRemoteCommandCenter.shared()
|
||||
private let center: MPRemoteCommandCenter
|
||||
|
||||
weak var audioPlayer: AudioPlayer?
|
||||
|
||||
var commandTargetPointers: [String: Any] = [:]
|
||||
|
||||
init() {}
|
||||
|
||||
/**
|
||||
Enable a set of RemoteCommands. Calling this will disable all earlier set commands, so include all commands that needs to be active.
|
||||
Create a new RemoteCommandController.
|
||||
|
||||
- parameter commands: The RemoteCommands that is to be enabled.
|
||||
- parameter remoteCommandCenter: The MPRemoteCommandCenter used. Default is `MPRemoteCommandCenter.shared()`
|
||||
*/
|
||||
public func enable(commands: [RemoteCommand]) {
|
||||
public init(remoteCommandCenter: MPRemoteCommandCenter = MPRemoteCommandCenter.shared()) {
|
||||
self.center = remoteCommandCenter
|
||||
}
|
||||
|
||||
internal func enable(commands: [RemoteCommand]) {
|
||||
self.disable(commands: RemoteCommand.all())
|
||||
commands.forEach { (command) in
|
||||
self.enable(command: command)
|
||||
}
|
||||
}
|
||||
|
||||
private func disable(commands: [RemoteCommand]) {
|
||||
internal func disable(commands: [RemoteCommand]) {
|
||||
commands.forEach { (command) in
|
||||
self.disable(command: command)
|
||||
}
|
||||
@@ -62,7 +64,6 @@ public class RemoteCommandController {
|
||||
case .changePlaybackPosition: self.enableCommand(ChangePlaybackPositionCommand.changePlaybackPosition)
|
||||
case .skipForward(let preferredIntervals): self.enableCommand(SkipIntervalCommand.skipForward.set(preferredIntervals: preferredIntervals))
|
||||
case .skipBackward(let preferredIntervals): self.enableCommand(SkipIntervalCommand.skipBackward.set(preferredIntervals: preferredIntervals))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,33 +83,33 @@ public class RemoteCommandController {
|
||||
|
||||
// MARK: - Handlers
|
||||
|
||||
lazy var handlePlayCommand: RemoteCommandHandler = { (event) in
|
||||
public lazy var handlePlayCommand: RemoteCommandHandler = self.handlePlayCommandDefault
|
||||
public lazy var handlePauseCommand: RemoteCommandHandler = self.handlePauseCommandDefault
|
||||
public lazy var handleStopCommand: RemoteCommandHandler = self.handleStopCommandDefault
|
||||
public lazy var handleTogglePlayPauseCommand: RemoteCommandHandler = self.handleTogglePlayPauseCommandDefault
|
||||
public lazy var handleSkipForwardCommand: RemoteCommandHandler = self.handleSkipForwardCommandDefault
|
||||
public lazy var handleSkipBackwardCommand: RemoteCommandHandler = self.handleSkipBackwardDefault
|
||||
public lazy var handleChangePlaybackPositionCommand: RemoteCommandHandler = self.handleChangePlaybackPositionCommandDefault
|
||||
public lazy var handleNextTrackCommand: RemoteCommandHandler = self.handleNextTrackCommandDefault
|
||||
public lazy var handlePreviousTrackCommand: RemoteCommandHandler = self.handlePreviousTrackCommandDefault
|
||||
|
||||
private func handlePlayCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let audioPlayer = self.audioPlayer {
|
||||
do {
|
||||
try audioPlayer.play()
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
catch let error {
|
||||
return self.getRemoteCommandHandlerStatus(forError: error)
|
||||
}
|
||||
audioPlayer.play()
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handlePauseCommand: RemoteCommandHandler = { (event) in
|
||||
private func handlePauseCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let audioPlayer = self.audioPlayer {
|
||||
do {
|
||||
try audioPlayer.pause()
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
catch let error {
|
||||
return self.getRemoteCommandHandlerStatus(forError: error)
|
||||
}
|
||||
audioPlayer.pause()
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handleStopCommand: RemoteCommandHandler = { (event) in
|
||||
private func handleStopCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let audioPlayer = self.audioPlayer {
|
||||
audioPlayer.stop()
|
||||
return .success
|
||||
@@ -116,64 +117,44 @@ public class RemoteCommandController {
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handleTogglePlayPauseCommand: RemoteCommandHandler = { (event) in
|
||||
private func handleTogglePlayPauseCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let audioPlayer = self.audioPlayer {
|
||||
do {
|
||||
try audioPlayer.togglePlaying()
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
catch let error {
|
||||
return self.getRemoteCommandHandlerStatus(forError: error)
|
||||
}
|
||||
audioPlayer.togglePlaying()
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handleSkipForwardCommand: RemoteCommandHandler = { (event) in
|
||||
private func handleSkipForwardCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let command = event.command as? MPSkipIntervalCommand,
|
||||
let interval = command.preferredIntervals.first,
|
||||
let audioPlayer = self.audioPlayer {
|
||||
do {
|
||||
try audioPlayer.seek(to: audioPlayer.currentTime + Double(truncating: interval))
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
catch let error {
|
||||
return self.getRemoteCommandHandlerStatus(forError: error)
|
||||
}
|
||||
audioPlayer.seek(to: audioPlayer.currentTime + Double(truncating: interval))
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handleSkipBackwardCommand: RemoteCommandHandler = { (event) in
|
||||
private func handleSkipBackwardDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let command = event.command as? MPSkipIntervalCommand,
|
||||
let interval = command.preferredIntervals.first,
|
||||
let audioPlayer = self.audioPlayer {
|
||||
do {
|
||||
try audioPlayer.seek(to: audioPlayer.currentTime - Double(truncating: interval))
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
catch let error {
|
||||
return self.getRemoteCommandHandlerStatus(forError: error)
|
||||
}
|
||||
audioPlayer.seek(to: audioPlayer.currentTime - Double(truncating: interval))
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handleChangePlaybackPositionCommand: RemoteCommandHandler = { (event) in
|
||||
private func handleChangePlaybackPositionCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let event = event as? MPChangePlaybackPositionCommandEvent,
|
||||
let audioPlayer = self.audioPlayer {
|
||||
do {
|
||||
try audioPlayer.seek(to: event.positionTime)
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
catch let error {
|
||||
return self.getRemoteCommandHandlerStatus(forError: error)
|
||||
}
|
||||
audioPlayer.seek(to: event.positionTime)
|
||||
return MPRemoteCommandHandlerStatus.success
|
||||
}
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handleNextTrackCommand: RemoteCommandHandler = { (event) in
|
||||
private func handleNextTrackCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let player = self.audioPlayer as? QueuedAudioPlayer {
|
||||
do {
|
||||
try player.next()
|
||||
@@ -186,7 +167,7 @@ public class RemoteCommandController {
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
}
|
||||
|
||||
lazy var handlePreviousTrackCommand: RemoteCommandHandler = { (event) in
|
||||
private func handlePreviousTrackCommandDefault(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
||||
if let player = self.audioPlayer as? QueuedAudioPlayer {
|
||||
do {
|
||||
try player.previous()
|
||||
@@ -200,13 +181,7 @@ public class RemoteCommandController {
|
||||
}
|
||||
|
||||
private func getRemoteCommandHandlerStatus(forError error: Error) -> MPRemoteCommandHandlerStatus {
|
||||
if let error = error as? APError.PlaybackError {
|
||||
switch error {
|
||||
case .noLoadedItem:
|
||||
return MPRemoteCommandHandlerStatus.noActionableNowPlayingItem
|
||||
}
|
||||
}
|
||||
else if let error = error as? APError.LoadError {
|
||||
if let error = error as? APError.LoadError {
|
||||
switch error {
|
||||
case .invalidSourceUrl(_):
|
||||
return MPRemoteCommandHandlerStatus.commandFailed
|
||||
@@ -1,30 +0,0 @@
|
||||
//
|
||||
// SimpleAudioPlayer.swift
|
||||
// SwiftAudio
|
||||
//
|
||||
// Created by Jørgen Henrichsen on 24/03/2018.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MediaPlayer
|
||||
|
||||
/**
|
||||
A simple audio player that keeps on item at a time.
|
||||
*/
|
||||
public class SimpleAudioPlayer: AudioPlayer {
|
||||
|
||||
public override init(infoCenter: MPNowPlayingInfoCenter = MPNowPlayingInfoCenter.default()) {
|
||||
super.init(infoCenter: infoCenter)
|
||||
}
|
||||
|
||||
/**
|
||||
Load an AudioItem into the manager.
|
||||
|
||||
- parameter item: The AudioItem to load. The info given in this item is the one used for the InfoCenter.
|
||||
- parameter playWhenReady: Immediately start playback when the item is ready. Default is `true`. If you disable this you have to call play() or togglePlay() when the `state` switches to `ready`.
|
||||
*/
|
||||
public func load(item: AudioItem, playWhenReady: Bool = true) throws {
|
||||
try self.loadItem(item, playWhenReady: playWhenReady)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user