├── .DS_Store ├── Frameworks ├── SpotifyAudioPlayback.framework │ ├── Headers │ │ ├── SPTAudioPlaybackTypes.h │ │ ├── SPTAudioStreamingController.h │ │ ├── SPTAudioStreamingController_ErrorCodes.h │ │ ├── SPTCircularBuffer.h │ │ ├── SPTCoreAudioController.h │ │ ├── SPTDiskCache.h │ │ ├── SPTDiskCaching.h │ │ ├── SPTPlaybackMetadata.h │ │ ├── SPTPlaybackState.h │ │ ├── SpPlaybackEvent.h │ │ └── SpotifyAudioPlayback.h │ ├── Info.plist │ └── SpotifyAudioPlayback ├── SpotifyAuthentication.framework │ ├── Headers │ │ ├── SPTAuth.h │ │ ├── SPTAuthViewController.h │ │ ├── SPTConnectButton.h │ │ ├── SPTEmbeddedImages.h │ │ ├── SPTSession.h │ │ ├── SPTStoreViewController.h │ │ └── SpotifyAuthentication.h │ ├── Info.plist │ └── SpotifyAuthentication └── SpotifyMetadata.framework │ ├── Headers │ ├── SPTAlbum.h │ ├── SPTArtist.h │ ├── SPTBrowse.h │ ├── SPTFeaturedPlaylistList.h │ ├── SPTFollow.h │ ├── SPTImage.h │ ├── SPTJSONDecoding.h │ ├── SPTListPage.h │ ├── SPTMetadataTypes.h │ ├── SPTPartialAlbum.h │ ├── SPTPartialArtist.h │ ├── SPTPartialObject.h │ ├── SPTPartialPlaylist.h │ ├── SPTPartialTrack.h │ ├── SPTPlaylistList.h │ ├── SPTPlaylistSnapshot.h │ ├── SPTPlaylistTrack.h │ ├── SPTRequest.h │ ├── SPTSavedTrack.h │ ├── SPTSearch.h │ ├── SPTTrack.h │ ├── SPTTrackProvider.h │ ├── SPTUser.h │ ├── SPTYourMusic.h │ └── SpotifyMetadata.h │ ├── Info.plist │ └── SpotifyMetadata ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ ├── fga.xcuserdatad │ │ └── xcschemes │ │ │ ├── Pods-Spotify2AppleMusic.xcscheme │ │ │ ├── Sweeft.xcscheme │ │ │ └── xcschememanagement.plist │ │ └── mathiasquintero.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-Spotify2AppleMusic.xcscheme │ │ ├── Sweeft.xcscheme │ │ └── xcschememanagement.plist ├── Sweeft │ ├── LICENSE │ ├── README.md │ └── Sources │ │ └── Sweeft │ │ ├── ConstraintSatisfaction │ │ ├── CSP.swift │ │ ├── Constraint.swift │ │ └── VariableInstance.swift │ │ ├── Defaultable.swift │ │ ├── Extensions │ │ ├── Array.swift │ │ ├── Bool.swift │ │ ├── Collection.swift │ │ ├── Data.swift │ │ ├── Date.swift │ │ ├── Dictionary.swift │ │ ├── Double.swift │ │ ├── Int.swift │ │ ├── Optional.swift │ │ ├── Set.swift │ │ ├── String.swift │ │ └── URL.swift │ │ ├── Graphs │ │ ├── Graph.swift │ │ └── Node.swift │ │ ├── Mutlithreading.swift │ │ ├── Networking │ │ ├── API.swift │ │ ├── APIEndpoint.swift │ │ ├── APIError.swift │ │ ├── Auth.swift │ │ ├── DataRepresentable.swift │ │ ├── Grant.swift │ │ ├── JSON.swift │ │ ├── Mappable.swift │ │ ├── OAuth.swift │ │ └── OAuthManager.swift │ │ ├── Observer Pattern │ │ ├── Binding.swift │ │ └── Observable.swift │ │ ├── Operators │ │ ├── ArrayOperators.swift │ │ ├── Assignment.swift │ │ ├── Functional.swift │ │ ├── Math.swift │ │ ├── MultithreadingOperators.swift │ │ └── ReducingParametersOperators.swift │ │ ├── Other.swift │ │ ├── PriorityQueue.swift │ │ ├── Promises │ │ ├── BulkPromise.swift │ │ ├── Promise.swift │ │ └── PromiseHandler.swift │ │ ├── ReducingParameters.swift │ │ ├── UserDefaults │ │ ├── ObjectStatus.swift │ │ ├── Status.swift │ │ ├── StatusFetcher.swift │ │ ├── StatusKey.swift │ │ └── StatusSerializable.swift │ │ └── ValueComparable.swift └── Target Support Files │ ├── Pods-Spotify2AppleMusic │ ├── Info.plist │ ├── Pods-Spotify2AppleMusic-acknowledgements.markdown │ ├── Pods-Spotify2AppleMusic-acknowledgements.plist │ ├── Pods-Spotify2AppleMusic-dummy.m │ ├── Pods-Spotify2AppleMusic-frameworks.sh │ ├── Pods-Spotify2AppleMusic-resources.sh │ ├── Pods-Spotify2AppleMusic-umbrella.h │ ├── Pods-Spotify2AppleMusic.debug.xcconfig │ ├── Pods-Spotify2AppleMusic.modulemap │ └── Pods-Spotify2AppleMusic.release.xcconfig │ └── Sweeft │ ├── Info.plist │ ├── Sweeft-dummy.m │ ├── Sweeft-prefix.pch │ ├── Sweeft-umbrella.h │ ├── Sweeft.modulemap │ └── Sweeft.xcconfig ├── README.md ├── Spotify2AppleMusic.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── fga.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── fga.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── Spotify2AppleMusic.xcscheme │ │ └── xcschememanagement.plist │ └── mathiasquintero.xcuserdatad │ └── xcschemes │ ├── Spotify2AppleMusic.xcscheme │ └── xcschememanagement.plist ├── Spotify2AppleMusic.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ ├── fga.xcuserdatad │ └── UserInterfaceState.xcuserstate │ └── mathiasquintero.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist └── Spotify2AppleMusic ├── .DS_Store ├── AppDelegate.swift ├── Assets.xcassets └── AppIcon.appiconset │ ├── Contents.json │ ├── icon@2x.png │ └── icon@3x.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── ImportSelection.swift ├── Info.plist ├── Itunes.swift ├── Song.swift ├── Spotify.swift ├── Spotify2AppleMusic-Bridging-Header.h ├── ViewController.h ├── ViewController.m └── ViewController.swift /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/.DS_Store -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SPTAudioPlaybackTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** Defines a callback block for an operation that has a chance of generating an error. 20 | 21 | If the operation was successful, `error` will be `nil`, otherwise it will contain an `NSError` 22 | object describing the failure.*/ 23 | typedef void (^SPTErrorableOperationCallback)(NSError *error); -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SPTAudioStreamingController_ErrorCodes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** The operation was successful. */ 20 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeNoError; 21 | 22 | /** The operation failed due to an unspecified issue. */ 23 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeFailed; 24 | 25 | /** Audio streaming could not be initialised. */ 26 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeInitFailed; 27 | 28 | /** Audio streaming could not be initialized because of an incompatible API version. */ 29 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeWrongAPIVersion; 30 | 31 | /** An unexpected NULL pointer was passed as an argument to a function. */ 32 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeNullArgument; 33 | 34 | /** An unexpected argument value was passed to a function. */ 35 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeInvalidArgument; 36 | 37 | /** Audio streaming has not yet been initialised for this application. */ 38 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeUninitialized; 39 | 40 | /** Audio streaming has already been initialised for this application. */ 41 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeAlreadyInitialized; 42 | 43 | /** Login to Spotify failed because of invalid credentials. */ 44 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeBadCredentials; 45 | 46 | /** The operation requires a Spotify Premium account. */ 47 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeNeedsPremium; 48 | 49 | /** The Spotify user is not allowed to log in from this country. */ 50 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeTravelRestriction; 51 | 52 | /** The application has been banned by Spotify. */ 53 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeApplicationBanned; 54 | 55 | /** An unspecified login error occurred. */ 56 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeGeneralLoginError; 57 | 58 | /** The operation is not supported. */ 59 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeUnsupported; 60 | 61 | /** The operation is not supported if the device is not the active playback device. */ 62 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeNotActiveDevice; 63 | 64 | /** An unspecified playback error occurred. */ 65 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeGeneralPlaybackError; 66 | 67 | /** The application is rate-limited if it requests the playback of too many tracks within a given amount of time. */ 68 | FOUNDATION_EXPORT NSInteger const SPTErrorCodePlaybackRateLimited; 69 | 70 | /** The track you're trying to play is unavailable for the current user, or was unable to start. */ 71 | FOUNDATION_EXPORT NSInteger const SPTErrorCodeTrackUnavailable; 72 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SPTCircularBuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** This class is a simple implementation of a circular buffer, designed to match the behaviour of the iOS SDK. 18 | 19 | This class gets around the problem of filling the buffer too far ahead by having a maximum size. Once that 20 | size is reached, you cannot add more data without reading some out or clearing it and starting again. When 21 | used with the iOS SDK, this isn't a problem as we can ask the library to re-deliver audio data at a later time. 22 | */ 23 | 24 | #import 25 | 26 | @interface SPTCircularBuffer : NSObject 27 | 28 | /** Initialize a new buffer. 29 | 30 | Initial size will be zero, with a maximum size as provided. 31 | 32 | @param size The maximum size of the buffer, in bytes. 33 | @return Returns the newly created SPTCircularBuffer. 34 | */ 35 | -(id)initWithMaximumLength:(NSUInteger)size; 36 | 37 | /** Clears all data from the buffer. */ 38 | -(void)clear; 39 | 40 | /** Attempt to copy new data into the buffer. 41 | 42 | Data is copied using the following heuristic: 43 | 44 | - If dataLength <= (maximumLength - length), copy all data. 45 | - Otherwise, copy (maximumLength - length) bytes. 46 | 47 | @param data A buffer containing the data to be copied in. 48 | @param dataLength The length of the data, in bytes. 49 | @return Returns the amount of data copied into the buffer, in bytes. If this number is 50 | smaller than dataLength, only this number of bytes was copied in from the start of the given buffer. 51 | */ 52 | -(NSUInteger)attemptAppendData:(const void *)data ofLength:(NSUInteger)dataLength; 53 | 54 | /** Attempt to copy new data into the buffer. 55 | 56 | Data is copied using the following heuristic: 57 | 58 | - If dataLength <= (maximumLength - length), copy all data. 59 | - Otherwise, copy (maximumLength - length) bytes. 60 | - Number of bytes copied will be rounded to the largest number less than dataLength that can be 61 | integrally be divided by chunkSize. 62 | 63 | @param data A buffer containing the data to be copied in. 64 | @param dataLength The length of the data, in bytes. 65 | @param chunkSize Ensures the number of bytes copies in is a multiple of this number. 66 | @return Returns the amount of data copied into the buffer, in bytes. If this number is 67 | smaller than dataLength, only this number of bytes was copied in from the start of the given buffer. 68 | */ 69 | -(NSUInteger)attemptAppendData:(const void *)data ofLength:(NSUInteger)dataLength chunkSize:(NSUInteger)chunkSize; 70 | 71 | /** Read data out of the buffer into a pre-allocated buffer. 72 | 73 | @param desiredLength The desired number of bytes to copy out. 74 | @param outBuffer A pointer to a buffer, which must be malloc'ed with at least `desiredLength` bytes. 75 | @return Returns the amount of data copied into the given buffer, in bytes. 76 | */ 77 | -(NSUInteger)readDataOfLength:(NSUInteger)desiredLength intoAllocatedBuffer:(void **)outBuffer; 78 | 79 | /** Returns the amount of data currently in the buffer, in bytes. */ 80 | @property (readonly) NSUInteger length; 81 | 82 | /** Returns the maximum amount of data that the buffer can hold, in bytes. */ 83 | @property (readonly, nonatomic) NSUInteger maximumLength; 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SPTDiskCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "SPTDiskCaching.h" 19 | 20 | /** 21 | * @brief The `SPTDiskCache` class implements the `SPTDiskCaching` protocol and provides a caching mechanism based on memory mapped files. 22 | */ 23 | @interface SPTDiskCache : NSObject 24 | 25 | /** 26 | * @brief Initialize the disk cache with capacity. 27 | * @param capacity The maximum capacity of the disk cache, in bytes. 28 | */ 29 | - (instancetype)initWithCapacity:(NSUInteger)capacity; 30 | 31 | @property (nonatomic, readonly) NSUInteger capacity; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SPTDiskCaching.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** 20 | @brief The `SPTDiskCaching` protocol is implemented by classes that handle caching of Spotify data to persistent storage. 21 | 22 | Methods of this protocol will be called from within Spotify SDK when it requires access to persistent storage for caching. 23 | 24 | */ 25 | @protocol SPTDiskCaching 26 | 27 | /** 28 | Creates a disk cache of certain size or changes the size of an existing cache. 29 | 30 | This method will be called when a new cache needs to be created or when the size of an existing cache needs to be changed. The cache should be accessible via other 'SPTDiskCaching' methods when using the same key as provided in this method. 31 | 32 | @param key An alphanumeric string, through which the cache is identified and accessed via 'SPTDiskCaching' methods. 33 | @param size The requested amount of bytes in the cache. 34 | @return `YES` if the cache of requested size has been allocated successfully, otherwise `NO`. 35 | */ 36 | - (BOOL)allocateCacheWithKey:(NSString *)key 37 | size:(NSUInteger)size; 38 | 39 | /** 40 | Reads data from the existing disk cache. 41 | 42 | This method will be called whenever a data needs to be read from the existing disk cache. The cache is identified by its key. 43 | 44 | @param key The identifier of the cache. 45 | @param length The amount of bytes to be read from the cache. 46 | @param offset The amount of bytes to be skipped from the beginning of the cache before reading starts. 47 | @return An instance of NSData containing the data read from the cache; 'nil' if reading failed. 48 | */ 49 | - (NSData *)readCacheDataWithKey:(NSString *)key 50 | length:(NSUInteger)length 51 | offset:(NSUInteger)offset; 52 | 53 | /** 54 | Writes data to the existing disk cache. 55 | 56 | This method will be called whenever a data needs to be written to the existing disk cache. The cache is identified by its key. 57 | 58 | @param key The identifier of the cache. 59 | @param data Bytes to be written to the cache. 60 | @param offset The amount of bytes to be skipped from the beginning of the cache before writing starts. 61 | @return `YES` if writing to the cache has been successful, otherwise 'NO'. 62 | */ 63 | - (BOOL)writeCacheDataWithKey:(NSString *)key 64 | data:(NSData *)data 65 | offset:(NSUInteger)offset; 66 | 67 | /** 68 | Closes the existing disk cache. 69 | 70 | This method will be called when a cache is no longer needed and can be deleted. 71 | 72 | @param key The identifier of the cache. 73 | 74 | */ 75 | - (void)closeCacheWithKey:(NSString *)key; 76 | 77 | @end 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SPTPlaybackMetadata.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | 20 | @interface SPTPlaybackTrack : NSObject 21 | 22 | @property (readonly, nonnull) NSString *name; 23 | @property (readonly, nonnull) NSString *uri; 24 | @property (readonly, nonnull) NSString *playbackSourceUri; 25 | @property (readonly, nonnull) NSString *playbackSourceName; 26 | @property (readonly, nonnull) NSString *artistName; 27 | @property (readonly, nonnull) NSString *artistUri; 28 | @property (readonly, nonnull) NSString *albumName; 29 | @property (readonly, nonnull) NSString *albumUri; 30 | 31 | /// The URL of the album cover art of the track. 32 | @property (nullable, nonatomic, copy, readonly) NSString *albumCoverArtURL; 33 | 34 | @property (readonly) NSTimeInterval duration; 35 | @property (readonly) NSUInteger indexInContext; 36 | 37 | - (instancetype _Nullable)initWithName:(NSString* _Nonnull)name 38 | uri:(NSString* _Nonnull)uri 39 | playbackSourceUri:(NSString* _Nonnull)playbackSourceUri 40 | playbackSourceName:(NSString* _Nonnull)playbackSourceName 41 | artistName:(NSString* _Nonnull)artistName 42 | artistUri:(NSString* _Nonnull)artistUri 43 | albumName:(NSString* _Nonnull)albumName 44 | albumUri:(NSString* _Nonnull)albumUri 45 | albumCoverArtURL:(NSString * _Nullable)albumCoverArtURL 46 | duration:(NSTimeInterval)duration 47 | indexInContext:(NSUInteger)indexInContext; 48 | 49 | @end 50 | 51 | 52 | @interface SPTPlaybackMetadata: NSObject 53 | 54 | @property (readonly, nullable) SPTPlaybackTrack *prevTrack; 55 | @property (readonly, nullable) SPTPlaybackTrack *currentTrack; 56 | @property (readonly, nullable) SPTPlaybackTrack *nextTrack; 57 | 58 | - (instancetype _Nullable)initWithPrevTrack:(SPTPlaybackTrack* _Nullable)prevTrack 59 | currentTrack:(SPTPlaybackTrack* _Nullable)currentTrack 60 | nextTrack:(SPTPlaybackTrack* _Nullable)nextTrack; 61 | 62 | @end -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SPTPlaybackState.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | Represent aggregated playar state. 20 | The next substates are exposed: 21 | -isPlaying 22 | -isRepeating 23 | -isShuffling 24 | -isActiveDevice 25 | -positionMs 26 | */ 27 | @interface SPTPlaybackState : NSObject 28 | 29 | @property (nonatomic, readonly) BOOL isPlaying; 30 | @property (nonatomic, readonly) BOOL isRepeating; 31 | @property (nonatomic, readonly) BOOL isShuffling; 32 | @property (nonatomic, readonly) BOOL isActiveDevice; 33 | @property (nonatomic, readonly) NSTimeInterval position; 34 | 35 | - (instancetype _Nullable)initWithIsPlaying:(BOOL)isPlaying 36 | isRepeating:(BOOL)isRepeating 37 | isShuffling:(BOOL)isShuffling 38 | isActiveDevice:(BOOL)isActiveDevice 39 | position:(NSTimeInterval)position; 40 | @end 41 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Headers/SpotifyAudioPlayback.h: -------------------------------------------------------------------------------- 1 | // 2 | // Spotify Audio Playback Framework.h 3 | // Spotify Audio Playback Framework 4 | // 5 | // Created by Dmytro Ankudinov on 27/09/16. 6 | // Copyright © 2016 Spotify AB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Spotify Audio Playback Framework. 12 | FOUNDATION_EXPORT double SpotifyAudioPlaybackVersionNumber; 13 | 14 | //! Project version string for Spotify Audio Playback Framework. 15 | FOUNDATION_EXPORT const unsigned char SpotifyAudioPlaybackVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | 25 | #if !TARGET_OS_IPHONE 26 | #import 27 | #endif 28 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Frameworks/SpotifyAudioPlayback.framework/Info.plist -------------------------------------------------------------------------------- /Frameworks/SpotifyAudioPlayback.framework/SpotifyAudioPlayback: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Frameworks/SpotifyAudioPlayback.framework/SpotifyAudioPlayback -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/Headers/SPTAuthViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import 19 | 20 | @class SPTAuth; 21 | @class SPTSession; 22 | @class SPTAuthViewController; 23 | 24 | /** A ViewController for managing the login flow inside your app. */ 25 | __deprecated_msg("Use `SFSafariViewController` or a `WKWebView` to present the `SPTAuth.loginURL`") 26 | @protocol SPTAuthViewDelegate 27 | 28 | /** 29 | The user logged in successfully. 30 | 31 | @param authenticationViewController The view controller. 32 | @param session The session object with the new credentials. (Note that the session object in 33 | the `SPTAuth` object passed upon initialization is also updated) 34 | */ 35 | - (void) authenticationViewController:(SPTAuthViewController *)authenticationViewController didLoginWithSession:(SPTSession *)session; 36 | 37 | /** 38 | An error occured while logging in 39 | 40 | @param authenticationViewController The view controller. 41 | @param error The error (Note that the session object in the `SPTAuth` object passed upon initialization 42 | is cleared.) 43 | */ 44 | - (void) authenticationViewController:(SPTAuthViewController *)authenticationViewController didFailToLogin:(NSError *)error; 45 | 46 | /** 47 | User closed the login dialog. 48 | @param authenticationViewController The view controller. 49 | */ 50 | - (void) authenticationViewControllerDidCancelLogin:(SPTAuthViewController *)authenticationViewController; 51 | 52 | @end 53 | 54 | /** 55 | A authentication view controller 56 | 57 | To present the authentication dialog on top of your view controller, do like this: 58 | 59 | ``` 60 | authvc.modalPresentationStyle = UIModalPresentationOverCurrentContext; 61 | authvc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; 62 | self.modalPresentationStyle = UIModalPresentationCurrentContext; 63 | self.definesPresentationContext = YES; 64 | [self presentViewController:authvc animated:NO completion:nil]; 65 | ``` 66 | */ 67 | __deprecated_msg("Use `SFSafariViewController` or a `WKWebView` to present the `SPTAuth.loginURL`") 68 | @interface SPTAuthViewController : UIViewController 69 | 70 | /** 71 | The delegate which will receive the result of the authentication. 72 | */ 73 | @property (nonatomic, assign) id delegate; 74 | 75 | /** 76 | Creates an authentication view controller for the default application using the authentication information from 77 | `SPTAuth.defaultInstance` 78 | 79 | @return The authentication view controller. 80 | */ 81 | + (SPTAuthViewController*) authenticationViewController; 82 | 83 | /** 84 | Creates an authentication view controller for a specific application. 85 | 86 | @param auth The authentication object, containing the app configuration, pass `nil` if you want to use the 87 | authentication information from `SPTAuth.defaultInstance` 88 | @return The authentication view controller. 89 | */ 90 | + (SPTAuthViewController*) authenticationViewControllerWithAuth:(SPTAuth *)auth; 91 | 92 | /** 93 | Removes all authentication related cookies from the UIWebView. 94 | 95 | @param callback Called when cookies are cleared. 96 | */ 97 | - (void) clearCookies:(void (^)())callback; 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/Headers/SPTConnectButton.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | @interface SPTConnectButton : UIControl 20 | @end 21 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/Headers/SPTEmbeddedImages.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "SPTConnectButton.h" 18 | 19 | @interface SPTEmbeddedImages : NSObject 20 | 21 | +(UIImage *)buttonImage; 22 | 23 | +(UIImage *)closeImage; 24 | 25 | +(UIImage *)newButtonImage; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/Headers/SPTSession.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** 20 | @brief SPTSession is a class that represents a user session authenticated through the Spotify OAuth service. 21 | @discussion For persisting the session, you may use `NSKeyedArchiver` to obtain an `NSData` instance, which can 22 | be stored securely using Keychain Services. 23 | @note A session is valid for a certain period of time, and may be renewed without user intervention using `SPTAuth`. 24 | @see SPTAuth 25 | */ 26 | @interface SPTSession : NSObject 27 | 28 | ///---------------------------- 29 | /// @name Initialisation 30 | ///---------------------------- 31 | 32 | /** 33 | @brief The deignated initializer for `SPTSession`. 34 | @param userName The username of the user. 35 | @param accessToken The access token of the user. 36 | @param expirationDate The expiration date of the access token. 37 | @return An initialized `SPTSession` object. 38 | */ 39 | - (instancetype)initWithUserName:(NSString *)userName accessToken:(NSString *)accessToken expirationDate:(NSDate *)expirationDate; 40 | 41 | /** 42 | @brief The deignated initializer for `SPTSession`. 43 | @param userName The username of the user. 44 | @param accessToken The access token of the user. 45 | @param encryptedRefreshToken The encrypted refresh token of the user. 46 | @param expirationDate The expiration date of the access token. 47 | @return An initialized `SPTSession` object. 48 | */ 49 | - (instancetype)initWithUserName:(NSString *)userName accessToken:(NSString *)accessToken encryptedRefreshToken:(NSString *)encryptedRefreshToken expirationDate:(NSDate *)expirationDate; 50 | 51 | /** 52 | @brief Initializer that takes an `NSTimeInterval` until the access token expires, instead of an `NSDate`. 53 | @param userName The username of the user. 54 | @param accessToken The access token of the user. 55 | @param timeInterval The time interval until the access token expires. 56 | @return An initialized `SPTSession` object. 57 | */ 58 | - (instancetype)initWithUserName:(NSString *)userName accessToken:(NSString *)accessToken expirationTimeInterval:(NSTimeInterval)timeInterval; 59 | 60 | ///---------------------------- 61 | /// @name Properties 62 | ///---------------------------- 63 | 64 | /** 65 | @brief Returns whether the session is still valid. 66 | @discussion Determining validity is done by comparing the current date and time with the expiration date of the `SPTSession` object. 67 | @return `YES` if valid, otherwise `NO`. 68 | */ 69 | - (BOOL)isValid; 70 | 71 | /** @brief The canonical username of the authenticated user. */ 72 | @property (nonatomic, copy, readonly) NSString *canonicalUsername; 73 | 74 | /** @brief The access token of the authenticated user. */ 75 | @property (nonatomic, copy, readonly) NSString *accessToken; 76 | 77 | /** @brief The encrypted refresh token. */ 78 | @property (nonatomic, copy, readonly) NSString *encryptedRefreshToken; 79 | 80 | /** @brief The expiration date of the access token. */ 81 | @property (nonatomic, copy, readonly) NSDate *expirationDate; 82 | 83 | /** @brief The access token type. */ 84 | @property (nonatomic, copy, readonly) NSString *tokenType; 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/Headers/SPTStoreViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import 19 | 20 | @protocol SPTStoreControllerDelegate; 21 | 22 | /** 23 | A Store View Controller to allow a user to download the Spotify iOS app. 24 | 25 | To present the store controller on top of your view controller: 26 | 27 | ``` 28 | SPTStoreViewController *storeVC = [[SPTStoreViewController alloc] initWithCampaignToken:@"your_campaign_token" storeDelegate:self]; 29 | [self presentViewController:storeVC animated:YES completion:nil]; 30 | ``` 31 | */ 32 | @interface SPTStoreViewController : SKStoreProductViewController 33 | 34 | /// The Spotify-provided campaign token. Must be less than 40-bytes. 35 | @property (nonatomic, copy, readonly) NSString *campaignToken; 36 | 37 | /* 38 | * The designated initializer. Returns an instance of `SPTStoreViewController` presenting the Spotify iOS app. 39 | * @param campaignToken The Spotify-provided campaign token. Must be less than 40-bytes. 40 | * @param storeDelegate The delegate which will be called after a dismiss action is taken in the store view controller. 41 | */ 42 | - (instancetype)initWithCampaignToken:(NSString *)campaignToken 43 | storeDelegate:(id)storeDelegate NS_DESIGNATED_INITIALIZER; 44 | 45 | /// Unavailable, use the designated initializer. 46 | - (instancetype)init NS_UNAVAILABLE; 47 | /// Unavailable, use the designated initializer. 48 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_UNAVAILABLE; 49 | /// Unavailable, use the designated initializer. 50 | - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; 51 | /// Unavailable, use the designated initializer. 52 | + (instancetype)new NS_UNAVAILABLE; 53 | 54 | @end 55 | 56 | 57 | @protocol SPTStoreControllerDelegate 58 | 59 | /** 60 | Called when the user requests the page to be dismissed. 61 | 62 | ``` 63 | - (void)productViewControllerDidFinish:(SPTStoreViewController *)viewController { 64 | [viewController dismissViewControllerAnimated:YES completion:nil]; 65 | } 66 | ``` 67 | */ 68 | - (void)productViewControllerDidFinish:(SPTStoreViewController *)viewController; 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/Headers/SpotifyAuthentication.h: -------------------------------------------------------------------------------- 1 | // 2 | // Spotify Authentication Framework.h 3 | // Spotify Authentication Framework 4 | // 5 | // Created by Dmytro Ankudinov on 26/09/16. 6 | // Copyright © 2016 Spotify AB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Spotify Authentication Framework. 12 | FOUNDATION_EXPORT double SpotifyAuthenticationVersionNumber; 13 | 14 | //! Project version string for Spotify Authentication Framework. 15 | FOUNDATION_EXPORT const unsigned char SpotifyAuthenticationVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | 22 | #if TARGET_OS_IPHONE 23 | #import 24 | #import 25 | #import 26 | #import 27 | #endif 28 | 29 | 30 | -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Frameworks/SpotifyAuthentication.framework/Info.plist -------------------------------------------------------------------------------- /Frameworks/SpotifyAuthentication.framework/SpotifyAuthentication: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Frameworks/SpotifyAuthentication.framework/SpotifyAuthentication -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTFeaturedPlaylistList.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "SPTListPage.h" 19 | 20 | /** This object represents a list of featured playlists created from the `SPTBrowse` class 21 | 22 | API Docs: https://developer.spotify.com/web-api/get-list-featured-playlists/ 23 | 24 | See: `SPTBrowse` 25 | */ 26 | @interface SPTFeaturedPlaylistList : SPTListPage 27 | 28 | 29 | 30 | 31 | 32 | ///----------------- 33 | /// @name Properties 34 | ///----------------- 35 | 36 | /** If there's a message associated with the paginated list. */ 37 | @property (nonatomic, readonly) NSString *message; 38 | 39 | 40 | 41 | 42 | 43 | 44 | ///--------------------------- 45 | /// @name API Response Parsers 46 | ///--------------------------- 47 | 48 | + (instancetype)featuredPlaylistListFromData:(NSData *)data 49 | withResponse:(NSURLResponse *)response 50 | error:(NSError **)error; 51 | 52 | + (instancetype)featuredPlaylistListFromDecodedJSON:(id)decodedObject 53 | error:(NSError **)error; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTImage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import 19 | 20 | /** This class represents an image from the Spotify service. It could be an 21 | album's cover art or a user image, for example. 22 | 23 | API Model: https://developer.spotify.com/web-api/object-model/#image-object 24 | */ 25 | @interface SPTImage : NSObject 26 | 27 | 28 | 29 | 30 | ///---------------------------- 31 | /// @name Properties 32 | ///---------------------------- 33 | 34 | /** The image's size as reported from the backed. 35 | 36 | @warning This property may be `CGSizeZero` if the size of the image is unknown 37 | by the backend. This is particularly the case with images not owned by Spotify, for 38 | example if a user's image is taken from their Facebook account. 39 | */ 40 | @property (nonatomic, readonly) CGSize size; 41 | 42 | /** The HTTP URL to the image. */ 43 | @property (nonatomic, readonly, copy) NSURL *imageURL; 44 | 45 | 46 | 47 | 48 | 49 | 50 | ///------------------------------- 51 | /// @name Response parsing methods 52 | ///------------------------------- 53 | 54 | + (instancetype)imageFromDecodedJSON:(id)decodedObject 55 | error:(NSError **)error; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTJSONDecoding.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** An object that supports decoding from JSON. */ 20 | @protocol SPTJSONObject 21 | 22 | /** Initialise the object with the given decoded JSON response from the web API 23 | (typically an `NSDictionary`, but not always). 24 | 25 | @param decodedObject The decoded representation of the object. 26 | @param error An error pointer that will contain an error if a problem occurred. 27 | @return Returns the initalised object, or `nil` if a problem occurred. 28 | */ 29 | -(id)initWithDecodedJSONObject:(id)decodedObject error:(NSError **)error; 30 | 31 | /** Returns the original decoded object (typically an `NSDictionary`, but not always) 32 | that was used to create the object. Useful for serialising. */ 33 | @property (nonatomic, readonly, copy) id decodedJSONObject; 34 | 35 | @end 36 | 37 | /** Helper class for decoding JSON from the Spotify web API. You shouldn't need to use this 38 | in your application — use `SPTRequest` instead. */ 39 | @interface SPTJSONDecoding : NSObject 40 | 41 | ///---------------------------- 42 | /// @name JSON Decoding 43 | ///---------------------------- 44 | 45 | /** Convert an object decoded from JSON into a Spotify SDK metadata object. 46 | 47 | @param decodedJson The object decoded from JSON. 48 | @param error A pointer to an error object that will be filled if an error occurs. 49 | @return The generated object, or `nil` if an error occurs. 50 | */ 51 | +(id)SPObjectFromDecodedJSON:(id)decodedJson error:(NSError **)error; 52 | 53 | /** Convert an object from the given JSON data into a Spotify SDK metadata object. 54 | 55 | @param json The JSON data. 56 | @param error A pointer to an error object that will be filled if an error occurs. 57 | @return The generated object, or `nil` if an error occurs. 58 | */ 59 | +(id)SPObjectFromEncodedJSON:(NSData *)json error:(NSError **)error; 60 | 61 | 62 | /** Convert an object decoded from JSON into a partial Spotify SDK metadata object. 63 | 64 | @param decodedJson The object decoded from JSON. 65 | @param error A pointer to an error object that will be filled if an error occurs. 66 | @return The generated object, or `nil` if an error occurs. 67 | */ 68 | +(id)partialSPObjectFromDecodedJSON:(id)decodedJson error:(NSError **)error; 69 | 70 | /** Convert an object from the given JSON data into a partial Spotify SDK metadata object. 71 | 72 | @param json The JSON data. 73 | @param error A pointer to an error object that will be filled if an error occurs. 74 | @return The generated object, or `nil` if an error occurs. 75 | */ 76 | +(id)partialSPObjectFromEncodedJSON:(NSData *)json error:(NSError **)error; 77 | 78 | @end 79 | 80 | /** Base object for JSON based models. */ 81 | @interface SPTJSONObjectBase : NSObject 82 | 83 | @property (nonatomic, readwrite, copy) id decodedJSONObject; 84 | 85 | @end 86 | 87 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTMetadataTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** Defines a callback block for an operation that has a chance of generating an error. 20 | 21 | If the operation was successful, `error` will be `nil`, otherwise it will contain an `NSError` 22 | object describing the failure.*/ 23 | typedef void (^SPTMetadataErrorableOperationCallback)(NSError *error); -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTPartialAlbum.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "SPTPartialObject.h" 19 | #import "SPTJSONDecoding.h" 20 | 21 | /// Defines the various types albums can be in relation to a given artist. 22 | typedef NS_ENUM(NSUInteger, SPTAlbumType) { 23 | /// Specifies that the given album is a "standard" album. 24 | SPTAlbumTypeAlbum, 25 | /// Specifies that the given album is a single. 26 | SPTAlbumTypeSingle, 27 | /// Specifies that the given album is a compilation album. 28 | SPTAlbumTypeCompilation, 29 | /// Specifies that the given album is an "appears on" album that the artist appears on, but didn't author. 30 | SPTAlbumTypeAppearsOn 31 | }; 32 | 33 | @class SPTImage; 34 | 35 | /** Represents a "partial" album on the Spotify service. You can promote this to a full album object using `SPTAlbum`. 36 | 37 | API Model: https://developer.spotify.com/web-api/object-model/#album-object-simplified 38 | */ 39 | @interface SPTPartialAlbum : SPTJSONObjectBase 40 | 41 | 42 | 43 | 44 | 45 | ///---------------------------- 46 | /// @name Properties 47 | ///---------------------------- 48 | 49 | /** The id of the track. */ 50 | @property (nonatomic, readonly, copy) NSString *identifier; 51 | 52 | /** The name of the album. */ 53 | @property (nonatomic, readonly, copy) NSString *name; 54 | 55 | /** The Spotify URI of the album. */ 56 | @property (nonatomic, readonly, copy) NSURL *uri; 57 | 58 | /** A playable Spotify URI for this album. */ 59 | @property (nonatomic, readonly, copy) NSURL *playableUri; 60 | 61 | /** The HTTP open.spotify.com URL of the album. */ 62 | @property (nonatomic, readonly, copy) NSURL *sharingURL; 63 | 64 | /** Returns a list of album covers in various sizes, as `SPTImage` objects. */ 65 | @property (nonatomic, readonly, copy) NSArray *covers; 66 | 67 | /** Convenience method that returns the smallest available cover image. */ 68 | @property (nonatomic, readonly) SPTImage *smallestCover; 69 | 70 | /** Convenience method that returns the largest available cover image. */ 71 | @property (nonatomic, readonly) SPTImage *largestCover; 72 | 73 | /** Returns the album type of this album. */ 74 | @property (nonatomic, readonly) SPTAlbumType type; 75 | 76 | /** An array of ISO 3166 country codes in which the album is available. */ 77 | @property (nonatomic, readonly, copy) NSArray *availableTerritories; 78 | 79 | 80 | 81 | 82 | 83 | ///------------------------------ 84 | /// @name Parsers / Deserializers 85 | ///------------------------------ 86 | 87 | + (instancetype)partialAlbumFromDecodedJSON:(id)decodedObject 88 | error:(NSError **)error; 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTPartialArtist.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "SPTPartialObject.h" 19 | #import "SPTJSONDecoding.h" 20 | 21 | /** Represents a "partial" artist on the Spotify service. You can promote this 22 | to a full artist object using `SPTArtist`. 23 | 24 | API Model: https://developer.spotify.com/web-api/object-model/#artist-object-simplified 25 | */ 26 | @interface SPTPartialArtist : SPTJSONObjectBase 27 | 28 | 29 | 30 | 31 | 32 | ///----------------- 33 | /// @name Properties 34 | ///----------------- 35 | 36 | /** The id of the artist. */ 37 | @property (nonatomic, readonly, copy) NSString *identifier; 38 | 39 | /** A playable Spotify URI for this artist. */ 40 | @property (nonatomic, readonly, copy) NSURL *playableUri; 41 | 42 | /** The HTTP open.spotify.com URL of the artist. */ 43 | @property (nonatomic, readonly, copy) NSURL *sharingURL; 44 | 45 | 46 | 47 | 48 | 49 | ///------------------------------ 50 | /// @name Parsers / Deserializers 51 | ///------------------------------ 52 | 53 | + (instancetype)partialArtistFromDecodedJSON:(id)decodedObject 54 | error:(NSError **)error; 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTPartialObject.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** Represents the base class of a "partial" object on the Spotify service.. */ 20 | @protocol SPTPartialObject 21 | 22 | ///---------------------------- 23 | /// @name Properties 24 | ///---------------------------- 25 | 26 | /** The name of the object. */ 27 | @property (nonatomic, readonly, copy) NSString *name; 28 | 29 | /** The Spotify URI of the object. */ 30 | @property (nonatomic, readonly, copy) NSURL *uri; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTPartialPlaylist.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "SPTPartialObject.h" 19 | #import "SPTJSONDecoding.h" 20 | #import "SPTTrackProvider.h" 21 | #import "SPTImage.h" 22 | 23 | @class SPTUser; 24 | 25 | /** Represents a "partial" playlist on the Spotify service. You can promote this 26 | to a full playlist object using `SPTPlaylistSnapshot`. 27 | 28 | API Model: https://developer.spotify.com/web-api/object-model/#playlist-object-simplified 29 | 30 | Playlist Guide: https://developer.spotify.com/web-api/working-with-playlists/ 31 | */ 32 | @interface SPTPartialPlaylist : SPTJSONObjectBase 33 | 34 | 35 | 36 | 37 | 38 | ///---------------------------- 39 | /// @name Properties 40 | ///---------------------------- 41 | 42 | /** The name of the playlist. */ 43 | @property (nonatomic, readonly, copy) NSString *name; 44 | 45 | /** The Spotify URI of the playlist. */ 46 | @property (nonatomic, readonly, copy) NSURL *uri; 47 | 48 | /** The playable Spotify URI for the playlist. */ 49 | @property (nonatomic, readonly, copy) NSURL *playableUri; 50 | 51 | /** The owner of the playlist. */ 52 | @property (nonatomic, readonly) SPTUser *owner; 53 | 54 | /** `YES` if the playlist is collaborative (i.e., can be modified by anyone), otherwise `NO`. */ 55 | @property (nonatomic, readonly) BOOL isCollaborative; 56 | 57 | /** `YES` if the playlist is public (i.e., can be seen by anyone), otherwise `NO`. */ 58 | @property (nonatomic, readonly) BOOL isPublic; 59 | 60 | /** The number of tracks in the playlist. */ 61 | @property (nonatomic, readonly) NSUInteger trackCount; 62 | 63 | /** Returns a list of playlist image in various sizes, as `SPTImage` objects. 64 | 65 | Will be `nil` if the playlist doesn't have a custom image. 66 | */ 67 | @property (nonatomic, readonly, copy) NSArray *images; 68 | 69 | /** Convenience method that returns the smallest available playlist image. 70 | 71 | Will be `nil` if the playlist doesn't have a custom image. 72 | */ 73 | @property (nonatomic, readonly) SPTImage *smallestImage; 74 | 75 | /** Convenience method that returns the largest available playlist image. 76 | 77 | Will be `nil` if the playlist doesn't have a custom image. 78 | */ 79 | @property (nonatomic, readonly) SPTImage *largestImage; 80 | 81 | 82 | 83 | 84 | ///------------------------------ 85 | /// @name Parsers / Deserializers 86 | ///------------------------------ 87 | 88 | + (instancetype)partialPlaylistFromDecodedJSON:(id)decodedObject 89 | error:(NSError **)error; 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTPartialTrack.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "SPTJSONDecoding.h" 19 | #import "SPTPartialObject.h" 20 | #import "SPTTrackProvider.h" 21 | #import "SPTPartialAlbum.h" 22 | 23 | /** Represents a "partial" track on the Spotify service. You can promote this 24 | to a full track object using `SPTTrack`. 25 | 26 | API Model: https://developer.spotify.com/web-api/object-model/#track-object-simplified 27 | 28 | API Docs: https://developer.spotify.com/web-api/track-endpoints/ 29 | */ 30 | @interface SPTPartialTrack : SPTJSONObjectBase 31 | 32 | 33 | 34 | 35 | 36 | ///---------------------------- 37 | /// @name Properties 38 | ///---------------------------- 39 | 40 | /** The id of the track. */ 41 | @property (nonatomic, readonly, copy) NSString *identifier; 42 | 43 | /** The name of the track. */ 44 | @property (nonatomic, readonly, copy) NSString *name; 45 | 46 | /** A playable Spotify URI for this track. */ 47 | @property (nonatomic, readonly, copy) NSURL *playableUri; 48 | 49 | /** The HTTP open.spotify.com URL of the track. */ 50 | @property (nonatomic, readonly, copy) NSURL *sharingURL; 51 | 52 | /** The duration of the track. */ 53 | @property (nonatomic, readonly) NSTimeInterval duration; 54 | 55 | /** The artists of the track, as `SPTPartialArtist` objects. */ 56 | @property (nonatomic, readonly, copy) NSArray *artists; 57 | 58 | /** The disc number of the track. I.e., if it's the first disc on the album this will be `1`. */ 59 | @property (nonatomic, readonly) NSInteger discNumber; 60 | 61 | /** Returns `YES` if the track is flagged as explicit, otherwise `NO`. */ 62 | @property (nonatomic, readonly) BOOL flaggedExplicit; 63 | 64 | /** Returns `YES` if the track is flagged as playable, otherwise `NO`, if no market is passed to the api call, this will default to `YES`. */ 65 | @property (nonatomic, readonly) BOOL isPlayable; 66 | 67 | /** Returns `YES` if the track has a playable status, only available if market passed to the api call. */ 68 | @property (nonatomic, readonly) BOOL hasPlayable; 69 | 70 | /** An array of ISO 3166 country codes in which the album is available. */ 71 | @property (nonatomic, readonly, copy) NSArray *availableTerritories; 72 | 73 | /** The HTTP URL of a 30-second preview MP3 of the track. */ 74 | @property (nonatomic, readonly, copy) NSURL *previewURL; 75 | 76 | /** The track number of the track. I.e., if it's the first track on the album this will be `1`. */ 77 | @property (nonatomic, readonly) NSInteger trackNumber; 78 | 79 | /** The album this track belongs to. */ 80 | @property (nonatomic, readonly, strong) SPTPartialAlbum *album; 81 | 82 | 83 | 84 | ///------------------------------- 85 | /// @name Response parsing methods 86 | ///------------------------------- 87 | 88 | /** 89 | Convert a parsed HTTP response into an SPTPartialTrack object 90 | 91 | @param decodedObject The decoded JSON object structure. 92 | @param error An optional pointer to an `NSError` that will receive the error code if operation failed. 93 | */ 94 | + (instancetype)partialTrackFromDecodedJSON:(id)decodedObject 95 | error:(NSError **)error; 96 | 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTPlaylistTrack.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "SPTTrack.h" 18 | 19 | @class SPTUser; 20 | 21 | /** Represents a track in a playlist on the Spotify service. */ 22 | @interface SPTPlaylistTrack : SPTTrack 23 | 24 | /** The date when the track was added. */ 25 | @property (nonatomic, readonly, copy) NSDate *addedAt; 26 | 27 | /** The user who added the track. */ 28 | @property (nonatomic, readonly) SPTUser *addedBy; 29 | @end 30 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTSavedTrack.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "SPTTrack.h" 19 | 20 | /** This class represents a track in the Your Music Library. 21 | 22 | API Model: https://developer.spotify.com/web-api/object-model/#saved-track-object 23 | */ 24 | @interface SPTSavedTrack : SPTTrack 25 | 26 | ///---------------------------- 27 | /// @name Properties 28 | ///---------------------------- 29 | 30 | /** The date when the track was saved. */ 31 | @property (nonatomic, readonly, copy) NSDate *addedAt; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SPTTrackProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Spotify AB 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | 19 | /** This protocol defines an object that can be played by `SPTAudioStreamingController`. */ 20 | @protocol SPTTrackProvider 21 | 22 | /** Returns the tracks for playback if no player-supported URI. */ 23 | -(NSArray *)tracksForPlayback; 24 | 25 | /** Returns the URI to this set of tracks, nil if not supported by player. */ 26 | -(NSURL *)playableUri; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Headers/SpotifyMetadata.h: -------------------------------------------------------------------------------- 1 | // 2 | // Spotify Metadata Framework.h 3 | // Spotify Metadata Framework 4 | // 5 | // Created by Dmytro Ankudinov on 27/09/16. 6 | // Copyright © 2016 Spotify AB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Spotify Metadata Framework. 12 | FOUNDATION_EXPORT double SpotifyMetadataVersionNumber; 13 | 14 | //! Project version string for Spotify Metadata Framework. 15 | FOUNDATION_EXPORT const unsigned char SpotifyMetadataVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | #import 34 | #import 35 | #import 36 | #import 37 | #import 38 | #import 39 | #import 40 | #import 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Frameworks/SpotifyMetadata.framework/Info.plist -------------------------------------------------------------------------------- /Frameworks/SpotifyMetadata.framework/SpotifyMetadata: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Frameworks/SpotifyMetadata.framework/SpotifyMetadata -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Finn Gaida 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '10.0' 3 | 4 | target 'Spotify2AppleMusic' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | pod 'Sweeft' 9 | 10 | end 11 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Sweeft (0.5.5) 3 | 4 | DEPENDENCIES: 5 | - Sweeft 6 | 7 | SPEC CHECKSUMS: 8 | Sweeft: ff11d10f27281257a35e13ad29813bd70348aee6 9 | 10 | PODFILE CHECKSUM: 2576cdc381382563ac9f34e2dd7a4476702d3905 11 | 12 | COCOAPODS: 1.2.1 13 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Sweeft (0.5.5) 3 | 4 | DEPENDENCIES: 5 | - Sweeft 6 | 7 | SPEC CHECKSUMS: 8 | Sweeft: ff11d10f27281257a35e13ad29813bd70348aee6 9 | 10 | PODFILE CHECKSUM: 2576cdc381382563ac9f34e2dd7a4476702d3905 11 | 12 | COCOAPODS: 1.2.1 13 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/fga.xcuserdatad/xcschemes/Pods-Spotify2AppleMusic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/fga.xcuserdatad/xcschemes/Sweeft.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/fga.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-Spotify2AppleMusic.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Sweeft.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 4F1A260EB3128F11410FEC9160BB1BC6 21 | 22 | primary 23 | 24 | 25 | 53444D25B6AB810CCA0E8A394DA7177A 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/mathiasquintero.xcuserdatad/xcschemes/Pods-Spotify2AppleMusic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/mathiasquintero.xcuserdatad/xcschemes/Sweeft.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/mathiasquintero.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-Spotify2AppleMusic.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | Sweeft.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 1 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | 4F1A260EB3128F11410FEC9160BB1BC6 25 | 26 | primary 27 | 28 | 29 | 53444D25B6AB810CCA0E8A394DA7177A 30 | 31 | primary 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Pods/Sweeft/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Mathias Quintero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/ConstraintSatisfaction/CSP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CSP.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/8/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Moddels a Constraint Statisfaction Problem 12 | public struct CSP { 13 | 14 | public typealias VariableValueSpec = (name: Variable, possible: [Value]) 15 | 16 | let variables: [VariableValueSpec] 17 | let constraints: [Constraint] 18 | 19 | public init(variables: [VariableValueSpec], constraints: [Constraint]) { 20 | self.variables = variables 21 | self.constraints = constraints 22 | } 23 | } 24 | 25 | public extension CSP where Value: CSPValue { 26 | 27 | public init(variables: [Variable], constraints: [Constraint]) { 28 | self.init(variables: variables => { (name: $0, possible: Value.all) }, 29 | constraints: constraints) 30 | } 31 | 32 | public init(constraints: [Constraint]) { 33 | let variables = constraints.flatMap { $0.variables } 34 | self.init(variables: variables.noDuplicates, 35 | constraints: constraints) 36 | } 37 | 38 | } 39 | 40 | extension CSP { 41 | 42 | typealias Instance = VariableInstance 43 | 44 | func constraints(concerning variables: Variable...) -> [Constraint] { 45 | return self.constraints |> { variables.and(conjunctUsing: $0.variables.contains) } 46 | } 47 | 48 | func neighbours(of variable: Variable) -> [Variable] { 49 | let variables = constraints(concerning: variable).flatMap { $0.variables } |> { $0 != variable } 50 | return variables.noDuplicates 51 | } 52 | 53 | private func bestInstance(for instances: [Instance]) -> Instance? { 54 | let left = instances |> { !$0.isSolved } 55 | if left.count == instances.count { 56 | return left.argmin { self.neighbours(of: $0.variable).count } 57 | } else { 58 | return left.argmin { $0.values.count } 59 | } 60 | } 61 | 62 | private func removeImposibleValues(in instances: [Instance]) -> [Instance] { 63 | var solved = instances |> { $0.isSolved } 64 | let count = solved.count 65 | let instances = instances => { $0.removeImpossibleValues(regarding: instances, 66 | and: self.constraints(concerning: $0.variable)) } 67 | solved = instances |> { $0.isSolved } 68 | if count == solved.count { 69 | return instances 70 | } else { 71 | return removeImposibleValues(in: instances) 72 | } 73 | 74 | } 75 | 76 | private func solve(instances: [Instance]) -> [Instance]? { 77 | if instances.and(conjunctUsing: { $0.isSolved }) { 78 | return instances 79 | } 80 | guard let current = bestInstance(for: instances) else { 81 | return nil 82 | } 83 | let instances = instances |> { $0 != current } 84 | return current.values ==> nil ** { result, value in 85 | if let result = result { 86 | return result 87 | } 88 | let instances = self.removeImposibleValues(in: instances + .solved(variable: current.variable, value: value)) 89 | return self.solve(instances: instances) 90 | } 91 | } 92 | 93 | /// Find a Solution for the problem 94 | public func solution() -> [Variable:Value]? { 95 | let instances = variables => Instance.unsolved 96 | let solution = solve(instances: instances as [Instance]) 97 | return solution?.dictionaryWithoutOptionals { ($0.variable, $0.values.first) } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/ConstraintSatisfaction/Constraint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constraint.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/8/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol CSPValue { 12 | static var all: [Self] { get } 13 | } 14 | 15 | /// Models a Constraint in a CSP 16 | public enum Constraint { 17 | case unary(Variable, constraint: (Value) -> Bool) 18 | case binary(Variable, Variable, constraint: (Value, Value) -> Bool) 19 | case collection([Constraint]) 20 | } 21 | 22 | extension Constraint where Value: Equatable { 23 | 24 | static func allDiff(_ variables: [Variable]) -> Constraint { 25 | let constraints = variables.flatMap { a in 26 | return variables.map { Constraint.binary(a, $0, constraint: (!=)) } 27 | } 28 | return .collection(constraints) 29 | } 30 | 31 | } 32 | 33 | extension Constraint { 34 | 35 | var variables: [Variable] { 36 | switch self { 37 | case .unary(let variable, _): 38 | return [variable] 39 | case .binary(let lhs, let rhs, _): 40 | return [lhs, rhs] 41 | case .collection(let constraints): 42 | return constraints 43 | .flatMap { $0.variables } 44 | .noDuplicates 45 | } 46 | } 47 | 48 | } 49 | 50 | extension Constraint { 51 | 52 | private func values(for variable: Variable, in array: [VariableInstance]) -> [Value] { 53 | let matching = array >>= { $0.variable } <+> { $0.values } 54 | return matching[variable].? 55 | } 56 | 57 | func works(with instances: [VariableInstance]) -> Bool { 58 | switch self { 59 | case .unary(let variable, let constraint): 60 | return values(for: variable, in: instances).and(conjunctUsing: constraint) 61 | case .binary(let lhs, let rhs, let constraint): 62 | let lhs = values(for: lhs, in: instances) 63 | let rhs = values(for: rhs, in: instances) 64 | return lhs.or(disjunctUsing: { constraint ** $0 } >>> rhs.or) 65 | case .collection(let constraints): 66 | return constraints.and { $0.works(with: instances) } 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/ConstraintSatisfaction/VariableInstance.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConstraintSatisfaction.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/8/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | enum VariableInstance { 12 | case solved(variable: Variable, value: Value) 13 | case unsolved(variable: Variable, possible: [Value]) 14 | case impossible(variable: Variable) 15 | } 16 | 17 | extension VariableInstance { 18 | 19 | var simplified: VariableInstance { 20 | switch self { 21 | case .unsolved(let name, let values): 22 | if values.isEmpty { 23 | return .impossible(variable: name) 24 | } 25 | if values.count == 1 { 26 | return .solved(variable: name, value: values[0]) 27 | } 28 | return self 29 | default: 30 | return self 31 | } 32 | } 33 | 34 | var variable: Variable { 35 | switch self { 36 | case .solved(let name, _): 37 | return name 38 | case .unsolved(let name, _): 39 | return name 40 | case .impossible(let name): 41 | return name 42 | } 43 | } 44 | 45 | var isSolved: Bool { 46 | switch simplified { 47 | case .solved: 48 | return true 49 | default: 50 | return false 51 | } 52 | } 53 | 54 | var values: [Value] { 55 | switch self { 56 | case .impossible: 57 | return .empty 58 | case .solved(_, let value): 59 | return [value] 60 | case .unsolved(_, let values): 61 | return values 62 | } 63 | } 64 | 65 | } 66 | 67 | extension VariableInstance { 68 | 69 | func removeImpossibleValues(regarding instances: [VariableInstance], 70 | and constraints: [Constraint]) -> VariableInstance { 71 | 72 | let name = self.variable 73 | let values = self.values.filter { 74 | let solved = VariableInstance.solved(variable: name, value: $0.0) 75 | let instances = instances |> { $0 != solved } + solved 76 | return constraints.and { $0.works(with: instances) } 77 | } 78 | let instance: VariableInstance = .unsolved(variable: name, possible: values) 79 | return instance.simplified 80 | } 81 | 82 | } 83 | 84 | extension VariableInstance: Hashable { 85 | 86 | var hashValue: Int { 87 | return variable.hashValue 88 | } 89 | 90 | } 91 | 92 | func ==(_ lhs: VariableInstance, _ rhs: VariableInstance) -> Bool { 93 | return lhs.variable == rhs.variable 94 | } 95 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Defaultable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Defaultable.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | /// A Type with a Default Value 11 | public protocol Defaultable { 12 | 13 | /// Default Value for Type 14 | static var defaultValue: Self { get } 15 | } 16 | 17 | public extension Defaultable { 18 | 19 | /// Will return an empty array 20 | static var array: [Self] { 21 | return array() 22 | } 23 | 24 | /** 25 | Will generate an array populated with default values 26 | 27 | - Parameter size: size of the array. (Default: 0) 28 | */ 29 | static func array(of size: Int = 0) -> [Self] { 30 | return defaultValue.array(of: size) 31 | } 32 | 33 | /** 34 | Will generate an array populated with the same value 35 | 36 | - Parameter size: size of the array. (Default: 0) 37 | */ 38 | func array(of size: Int = 0) -> [Self] { 39 | return size.range => returning(self) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Bool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bool.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension Bool { 11 | 12 | /// Will toggle the value of self 13 | @discardableResult mutating func toggle() -> Bool { 14 | self = !self 15 | return self 16 | } 17 | 18 | } 19 | 20 | extension Bool: Defaultable { 21 | 22 | /// Default Value 23 | public static let defaultValue = false 24 | 25 | } 26 | 27 | extension Bool: Serializable { 28 | 29 | public var json: JSON { 30 | return .bool(self) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/12/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Data { 12 | 13 | static var empty: Data { 14 | return Data() 15 | } 16 | 17 | /// Will get any structure representable as data using type inference 18 | private func get() -> T? { 19 | return T(data: self) 20 | } 21 | 22 | /// String using the utf8 encoding format 23 | var string: String? { 24 | return get() 25 | } 26 | 27 | /// JSON object contained in the data 28 | var json: JSON? { 29 | return get() 30 | } 31 | 32 | } 33 | 34 | extension Data: Defaultable { 35 | 36 | /// Default value is an empty set of data 37 | public static var defaultValue: Data { 38 | return Data() 39 | } 40 | 41 | } 42 | 43 | extension Data: DataRepresentable { 44 | 45 | public init?(data: Data) { 46 | self = data 47 | } 48 | 49 | } 50 | 51 | extension Data: DataSerializable { 52 | 53 | public var data: Data? { 54 | return self 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Date.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Weekday: Int { 11 | case sunday, monday, tuesday, wednesday, thursday, friday, saturday 12 | 13 | var index: Int { 14 | return rawValue 15 | } 16 | } 17 | 18 | extension Weekday: Defaultable { 19 | 20 | /// Default Value 21 | public static var defaultValue: Weekday = .sunday 22 | 23 | } 24 | 25 | public enum Month: Int { 26 | case january, february, march, april, may, june, july, august, september, october, november, december 27 | 28 | var index: Int { 29 | return rawValue 30 | } 31 | } 32 | 33 | extension Month: Defaultable { 34 | 35 | /// Default Value 36 | public static var defaultValue: Month = .january 37 | 38 | } 39 | 40 | public extension Date { 41 | 42 | /** 43 | Will turn a Date into a readable format 44 | 45 | - Parameter format: format in which you want the date (Optional: default is "dd.MM.yyyy hh:mm:ss a") 46 | 47 | - Returns: String representation of the date 48 | */ 49 | func string(using format: String = "dd.MM.yyyy hh:mm:ss a") -> String { 50 | let dateFormatter = DateFormatter() 51 | dateFormatter.dateFormat = format 52 | return dateFormatter.string(from: self) 53 | } 54 | 55 | private func getValue(for unit: Calendar.Component) -> Int { 56 | let calendar = Calendar(identifier: .gregorian) 57 | let value = calendar.dateComponents([unit], from: self).value(for: unit) 58 | return value.? 59 | } 60 | 61 | static var now: Date { 62 | return Date() 63 | } 64 | 65 | var nanosecond: Int { 66 | return getValue(for: .nanosecond) 67 | } 68 | 69 | var second: Int { 70 | return getValue(for: .second) 71 | } 72 | 73 | var minute: Int { 74 | return getValue(for: .minute) 75 | } 76 | 77 | var hour: Int { 78 | return getValue(for: .hour) 79 | } 80 | 81 | var day: Int { 82 | return getValue(for: .day) 83 | } 84 | 85 | var weekday: Weekday { 86 | return Weekday.init(rawValue: getValue(for: .weekday) - 1).? 87 | } 88 | 89 | var week: Int { 90 | return getValue(for: .weekOfYear) 91 | } 92 | 93 | var weekOfMonth: Int { 94 | return getValue(for: .weekOfMonth) 95 | } 96 | 97 | var month: Month { 98 | return Month.init(rawValue: getValue(for: .month) - 1).? 99 | } 100 | 101 | var year: Int { 102 | return getValue(for: .year) 103 | } 104 | 105 | } 106 | 107 | extension Date: Defaultable { 108 | 109 | /// Default Value 110 | public static var defaultValue: Date { 111 | return .now 112 | } 113 | 114 | } 115 | 116 | /// Struct representing a difference between two dates 117 | public struct DateDifference { 118 | 119 | /// Left date 120 | let first: Date 121 | /// Right date 122 | let second: Date 123 | 124 | private func difference(by granularity: Calendar.Component) -> Int { 125 | let calendar = Calendar(identifier: .gregorian) 126 | let set = [granularity].set 127 | let components = calendar.dateComponents(set, from: second, to: first) 128 | return components.value(for: granularity).? 129 | } 130 | 131 | /// Regular TimeInterval between the two dates 132 | public var timeInterval: TimeInterval { 133 | return first.timeIntervalSince(second) 134 | } 135 | 136 | /// The change in timezones 137 | public var timeZones: Int { 138 | return difference(by: .timeZone) 139 | } 140 | 141 | /// The difference in nanoseconds 142 | public var nanoSeconds: Int { 143 | return difference(by: .nanosecond) 144 | } 145 | 146 | /// The difference in seconds 147 | public var seconds: Int { 148 | return difference(by: .second) 149 | } 150 | 151 | /// The difference in minutes 152 | public var minutes: Int { 153 | return difference(by: .minute) 154 | } 155 | 156 | /// The difference in hours 157 | public var hours: Int { 158 | return difference(by: .hour) 159 | } 160 | 161 | /// The difference in days 162 | public var days: Int { 163 | return difference(by: .day) 164 | } 165 | 166 | /// The difference in weeks 167 | public var weeks: Int { 168 | return difference(by: .weekOfYear) 169 | } 170 | 171 | /// The difference in years 172 | public var years: Int { 173 | return difference(by: .year) 174 | } 175 | 176 | /// The difference in millenia. For some reason 177 | public var millenia: Int { 178 | return years / 1000 179 | } 180 | 181 | 182 | } 183 | 184 | extension Date: Serializable { 185 | 186 | /// JSON Value 187 | public var json: JSON { 188 | return .string(string()) 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Dictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/5/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Dictionary { 12 | 13 | /// Map Only Values 14 | func mapValues(_ map: @escaping (Value) -> T) -> [Key:T] { 15 | return self >>= mapLast(with: map) 16 | } 17 | 18 | /** 19 | Will find a match for in the dictionary by key 20 | 21 | - Parameter handler: function that will determine by the key if it's the desired item 22 | 23 | - Returns: value found 24 | */ 25 | func match(_ handler: @escaping (Key) -> Bool) -> Value? { 26 | return self ==> nil ** { result, item in 27 | if handler(item.key) { 28 | return item.value 29 | } else { 30 | return result 31 | } 32 | } 33 | } 34 | 35 | } 36 | 37 | public extension Dictionary where Key: CustomStringConvertible { 38 | 39 | /** 40 | Will find a match for in the dictionary by key, checking if 41 | the description of the key contains the query 42 | 43 | - Parameter handler: query we're looking for in the key 44 | 45 | - Returns: value found 46 | */ 47 | func match(containing query: String) -> Value? { 48 | return match(describe >>> String.contains ** query) 49 | } 50 | 51 | } 52 | 53 | public extension Dictionary where Value: Hashable { 54 | 55 | /// Returns a flipped mapping of the Dictionary. 56 | var flipped: [Value:Key] { 57 | return self >>= flipArguments 58 | } 59 | 60 | } 61 | 62 | extension Dictionary: Defaultable { 63 | 64 | /// Default Value 65 | public static var defaultValue: [Key:Value] { 66 | return .empty 67 | } 68 | 69 | } 70 | 71 | /// TODO: Find a way to inherit from this particular dict 72 | extension Dictionary where Key: CustomStringConvertible, Value: Serializable { 73 | 74 | /// JSON Value 75 | public var json: JSON { 76 | return .dict(self >>= describe <*> JSON.init) 77 | } 78 | 79 | } 80 | 81 | public extension ExpressibleByDictionaryLiteral { 82 | 83 | static var empty: Self { 84 | return Self() 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Double.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension Double { 11 | 12 | /// Will say if the string representation is a palindrome. (Without signing or dots.) 13 | var isPalindrome: Bool { 14 | return abs(self).description.replacingOccurrences(of: ".", with: .empty).isPalindrome 15 | } 16 | 17 | } 18 | 19 | extension Double: Defaultable { 20 | 21 | /// Default Value 22 | public static let defaultValue = 0.0 23 | 24 | } 25 | 26 | extension Double: Serializable { 27 | 28 | /// JSON Value 29 | public var json: JSON { 30 | return .double(self) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Int.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension Int { 11 | 12 | /// Will return all the prime factors of the number 13 | var primeFactors: [Int] { 14 | if self < 0 { 15 | return [-1] + (-self).primeFactors 16 | } else if self <= 2 { 17 | return [self] 18 | } 19 | let bound = Int(sqrt(Double(self))) + 1 20 | guard let firstPrime = (2...bound) |> { self % $0 == 0 } | 0 else { 21 | return [self] 22 | } 23 | return [firstPrime] + (self / firstPrime).primeFactors 24 | } 25 | 26 | /// Returns the factorial of self 27 | var factorial: Int { 28 | return range => inc ==> 1 ** (*) 29 | } 30 | 31 | /// Creates a range from 0 till n - 1 32 | var range: CountableRange? { 33 | if self < 1 { 34 | return nil 35 | } 36 | return (0..((-self).range) => negative 43 | } 44 | return range.array 45 | } 46 | 47 | /// Determines if the value is even 48 | var isEven: Bool { 49 | return self & 1 == 0 50 | } 51 | 52 | /// Determines if the value is odd 53 | var isOdd: Bool { 54 | return !isEven 55 | } 56 | 57 | /// Gives an Array of the digits in the number 58 | var digits: [Int] { 59 | return self.description.characters => describe ==> { Int($0) } 60 | } 61 | 62 | /// Will say it is prime 63 | var isPrime: Bool { 64 | if self < 2 { 65 | return false 66 | } 67 | return primeFactors.count == 1 68 | } 69 | 70 | /// Will say if the string representation is a palindrome. (Without signing) 71 | var isPalindrome: Bool { 72 | return abs(self).description.isPalindrome 73 | } 74 | 75 | /// Will return a reversed version of the integer 76 | var reversed: Int { 77 | return self | { Int($0.description.reversed).? } 78 | } 79 | 80 | /// Loop n times 81 | func times(do handler: @escaping () -> (V)) { 82 | range => **handler** 83 | } 84 | 85 | /// Loop n times 86 | func times(do handler: @escaping (Int) -> (V)) { 87 | range => handler** 88 | } 89 | 90 | } 91 | 92 | extension Int: Defaultable { 93 | 94 | /// Default Value 95 | public static let defaultValue = 0 96 | 97 | } 98 | 99 | extension Int: Serializable { 100 | 101 | /// JSON Value 102 | public var json: JSON { 103 | return .double(Double(self)) 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Optional.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Optional.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/27/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension Optional where Wrapped: Serializable { 12 | 13 | var json: JSON { 14 | switch self { 15 | case .some(let item): 16 | return item.json 17 | default: 18 | return .null 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/Set.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Set.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/16/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension Set: Defaultable { 12 | 13 | /// Default Value 14 | public static var defaultValue: Set { 15 | return [].set 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Swift.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension String { 11 | 12 | /// Will say if the String is a palindrome 13 | var isPalindrome: Bool { 14 | return <>self == self 15 | } 16 | 17 | /// Will return the string reversed 18 | var reversed: String { 19 | return String(<>characters) 20 | } 21 | 22 | /// Returns the decoded representation of the string 23 | var base64Decoded: String? { 24 | guard let data = Data(base64Encoded: self) else { 25 | return nil 26 | } 27 | return data.string 28 | } 29 | 30 | /// Returns the base 64 encoded representation of the string 31 | var base64Encoded: String? { 32 | return data?.base64EncodedString() 33 | } 34 | 35 | /// Encodes the string by escaping unallowed characters 36 | var urlEncoded: String { 37 | return self.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? self 38 | } 39 | 40 | /// Turns any string into a url 41 | var url: URL? { 42 | return URL(string: urlEncoded) 43 | } 44 | 45 | /** 46 | Turns any string into a possible API 47 | 48 | - Parameter baseHeaders: Headers that should be included into every single request 49 | - Parameter baseQueries: Queries that should be included into every single request 50 | 51 | - Returns: API using the string as base url 52 | */ 53 | func api(baseHeaders: [String : String] = .empty, baseQueries: [String: String]) -> GenericAPI { 54 | return V.api(with: self, baseHeaders: baseHeaders, baseQueries: baseQueries) 55 | } 56 | 57 | /** 58 | Will try to decipher the Date coded into a string 59 | 60 | - Parameter format: format in which the date is coded (Optional: default is "dd.MM.yyyy hh:mm:ss a") 61 | 62 | - Returns: Date object for the time 63 | */ 64 | func date(using format: String = "dd.MM.yyyy hh:mm:ss a") -> Date? { 65 | let dateFormatter = DateFormatter() 66 | dateFormatter.dateFormat = format 67 | return dateFormatter.date(from: self) 68 | } 69 | 70 | } 71 | 72 | public extension String { 73 | 74 | /** 75 | Will say if a String matches a RegEx 76 | 77 | - Parameter pattern: RegEx you want to match 78 | - Parameter options: Extra options (Optional: Default is .empty) 79 | 80 | - Returns: Whether or not the string matches 81 | */ 82 | func matches(pattern: String, options: NSRegularExpression.Options = []) throws -> Bool { 83 | let regex = try NSRegularExpression(pattern: pattern, options: options) 84 | return regex.numberOfMatches(in: self, options: [], range: NSRange(location: 0, length: 0.distance(to: utf16.count))) != 0 85 | } 86 | 87 | } 88 | 89 | extension String: Defaultable { 90 | 91 | /// Default Value 92 | public static let defaultValue: String = .empty 93 | 94 | } 95 | 96 | extension String: DataRepresentable { 97 | 98 | public init?(data: Data) { 99 | self.init(data: data, encoding: .utf8) 100 | } 101 | 102 | /// Data resulting by encoding using utf8 103 | public var data: Data? { 104 | return data(using: .utf8) 105 | } 106 | 107 | } 108 | 109 | extension String: Serializable { 110 | 111 | /// JSON Value 112 | public var json: JSON { 113 | return .string(self) 114 | } 115 | 116 | } 117 | 118 | public extension ExpressibleByStringLiteral where StringLiteralType == String { 119 | 120 | static var empty: Self { 121 | return Self(stringLiteral: "") 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Extensions/URL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/26/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public extension URL { 12 | 13 | /** 14 | Will add a query to the url 15 | 16 | - Parameter key: Key of the query 17 | - Parameter value: Value encoded into the query 18 | 19 | - Returns: URL with the query 20 | */ 21 | func appendingQuery(key: String, value: String) -> URL { 22 | let newQuery = "\(key)=\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed).? 23 | let string = self.absoluteString + (??query ? "&" : "?") + newQuery 24 | return URL(string: string)! 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Graphs/Node.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Node.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/22/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Connection { 12 | case simple(to: Identifier) 13 | case cost(to: Identifier, cost: Double) 14 | } 15 | 16 | extension Connection { 17 | 18 | var identifier: Identifier { 19 | switch self { 20 | case .simple(let identifier): 21 | return identifier 22 | case .cost(let identifier, _): 23 | return identifier 24 | } 25 | } 26 | 27 | var cost: Double { 28 | switch self { 29 | case .cost(_, let cost): 30 | return cost 31 | default: 32 | return 1 33 | } 34 | } 35 | 36 | } 37 | 38 | public protocol NodeProvider { 39 | associatedtype Node 40 | associatedtype Identifier 41 | func node(for identifier: Identifier) -> Node? 42 | } 43 | 44 | public protocol GraphNode { 45 | associatedtype Identifier: Hashable 46 | var identifier: Identifier { get } 47 | func neighbours() -> ResultPromise<[Connection]> 48 | func neighbours(in provider: P) -> ResultPromise<[(Self, Double)]> where P.Node == Self, P.Identifier == Identifier 49 | } 50 | 51 | extension GraphNode { 52 | 53 | public func neighbours(in provider: P) -> ResultPromise<[(Self, Double)]> where P.Node == Self, P.Identifier == Self.Identifier { 54 | 55 | return self.neighbours().nested { 56 | return $0.flatMap({ (provider.node(for: $0.identifier), $0.cost) } >>> iff) 57 | } 58 | } 59 | 60 | } 61 | 62 | 63 | public protocol HashableNode: GraphNode, Hashable { 64 | func neighbours() -> ResultPromise<[Connection]> 65 | } 66 | 67 | public extension HashableNode { 68 | 69 | typealias Identifier = Self 70 | 71 | public var identifier: Self { 72 | return self 73 | } 74 | 75 | } 76 | 77 | public extension HashableNode { 78 | 79 | func neighbours(in provider: P) -> ResultPromise<[(Self, Double)]> 80 | where P.Node == Self, P.Identifier == Self.Identifier { 81 | 82 | return neighbours().nested { 83 | $0.map { ($0.identifier, $0.cost) } 84 | } 85 | } 86 | 87 | } 88 | 89 | public extension HashableNode { 90 | 91 | func bfs(to destination: Self) -> ResultPromise<[Self.Identifier]?> { 92 | return bfs { $0 == destination.identifier } 93 | } 94 | 95 | func bfs(until isFinal: @escaping (Self.Identifier) -> Bool) -> ResultPromise<[Self.Identifier]?> { 96 | let graph = Graph() 97 | return graph.bfs(from: self, until: isFinal) 98 | } 99 | 100 | func dfs(to destination: Self) -> ResultPromise<[Self.Identifier]?> { 101 | return dfs { $0 == destination.identifier } 102 | } 103 | 104 | func dfs(until isFinal: @escaping (Self.Identifier) -> Bool) -> ResultPromise<[Self.Identifier]?> { 105 | let graph = Graph() 106 | return graph.dfs(from: self, until: isFinal) 107 | } 108 | 109 | func shortestPath(with euristics: @escaping (Self.Identifier) -> Double = **{ 0 }, 110 | to destination: Self) -> ResultPromise<[Self.Identifier]?> { 111 | 112 | return shortestPath(with: euristics, until: { $0 == destination.identifier }) 113 | } 114 | 115 | func shortestPath(with euristics: @escaping (Self.Identifier) -> Double = **{ 0 }, 116 | until isFinal: @escaping (Self.Identifier) -> Bool) -> ResultPromise<[Self.Identifier]?> { 117 | 118 | let graph = Graph() 119 | return graph.shortestPath(from: self, with: euristics, until: isFinal) 120 | } 121 | 122 | } 123 | 124 | public protocol SimpleNode: GraphNode { 125 | func neighbourIdentifiers() -> ResultPromise<[Identifier]> 126 | } 127 | 128 | public extension SimpleNode { 129 | 130 | public func neighbours() -> Promise<[Connection], AnyError> { 131 | 132 | return neighbourIdentifiers().nested { identifiers in 133 | identifiers => Connection.simple 134 | } 135 | } 136 | } 137 | 138 | public protocol SyncNode: GraphNode { 139 | var neighbours: [Connection] { get } 140 | } 141 | 142 | extension SyncNode { 143 | 144 | public func neighbours() -> Promise<[Connection], AnyError> { 145 | let promise = Promise<[Connection], AnyError>() 146 | promise.success(with: neighbours) 147 | return promise 148 | } 149 | 150 | } 151 | 152 | public protocol SimpleSyncNode: SyncNode { 153 | var neighbourIdentifiers: [Identifier] { get } 154 | } 155 | 156 | extension SimpleSyncNode { 157 | 158 | public var neighbours: [Connection] { 159 | return neighbourIdentifiers => Connection.simple 160 | } 161 | 162 | } 163 | 164 | public struct GenericNode: SyncNode { 165 | 166 | public typealias Identifier = T 167 | 168 | public var identifier: T { 169 | return data 170 | } 171 | 172 | public let data: T 173 | public let neighbours: [Connection] 174 | } 175 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Mutlithreading.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Multithreading.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | Runs a closure after a time interval 12 | 13 | - Parameter time: time interval 14 | - Parameter queue: Queue the code should run in. (Optional. Main is the default) 15 | - Parameter handler: function you want to run later 16 | */ 17 | public func after(_ time: TimeInterval = 0.0, in queue: DispatchQueue = .main, handler: @escaping () -> ()) { 18 | queue.asyncAfter(deadline: .now() + time) { 19 | handler() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/APIEndpoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIDescription.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/21/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Description for an Endpoint in an API 12 | public protocol APIEndpoint { 13 | /// Raw value string for the key 14 | var rawValue: String { get } 15 | } 16 | 17 | extension APIEndpoint { 18 | 19 | /// For the lazy. Creates a simple API object for your reference 20 | public static func api(with url: String, baseHeaders: [String:String] = .empty, baseQueries: [String:String] = .empty) -> GenericAPI { 21 | return GenericAPI(baseURL: url, baseHeaders: baseHeaders, baseQueries: baseQueries) 22 | } 23 | 24 | } 25 | 26 | /// Generic easy to make API from an Endpoint Description 27 | public struct GenericAPI: API { 28 | 29 | public typealias Endpoint = E 30 | public let baseURL: String 31 | public let baseHeaders: [String:String] 32 | public let baseQueries: [String:String] 33 | 34 | init(baseURL: String, baseHeaders: [String:String] = .empty, baseQueries: [String:String] = .empty) { 35 | self.baseURL = baseURL 36 | self.baseHeaders = baseHeaders 37 | self.baseQueries = baseQueries 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/APIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIError.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/26/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Errors that might happen in an API Call 12 | public enum APIError: Error { 13 | case noData /// There's no underlying data 14 | case timeout /// Connection has timed out 15 | case invalidStatus(code: Int, data: Data?) /// The http status code represents an unsuccesfull transaction 16 | case invalidData(data: Data) /// The Data does not represent the expected information 17 | case mappingError(json: JSON) /// Failed mapping the JSON Object to the respective Deserializable object 18 | case unknown(error: Error) /// Another error ocurred 19 | } 20 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/Auth.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Auth.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/29/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Authentication protocol 12 | public protocol Auth { 13 | func apply(to request: inout URLRequest) 14 | } 15 | 16 | /// Object that doesn't do anything to authenticate the user 17 | public struct NoAuth: Auth { 18 | 19 | /// Shared instance 20 | public static let standard = NoAuth() 21 | 22 | public func apply(to request: inout URLRequest) { 23 | // Do Nothing 24 | } 25 | 26 | } 27 | 28 | /// Basic Http Auth 29 | public struct BasicAuth { 30 | 31 | fileprivate let username: String 32 | fileprivate let password: String 33 | 34 | public init(username: String, password: String) { 35 | self.username = username 36 | self.password = password 37 | } 38 | 39 | } 40 | 41 | extension BasicAuth: Auth { 42 | 43 | /// Adds authorization header 44 | public func apply(to request: inout URLRequest) { 45 | let string = ("\(username):\(password)".base64Encoded).? 46 | let auth = "Basic \(string)" 47 | request.addValue(auth, forHTTPHeaderField: "Authorization") 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/DataRepresentable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataRepresentable.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/29/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Any object that can be fetched throught http as Data 12 | public protocol DataRepresentable { 13 | static var accept: String? { get } 14 | init?(data: Data) 15 | } 16 | 17 | public extension DataRepresentable { 18 | 19 | /// Result of a Single Object 20 | public typealias Result = Response 21 | /// Result of an Array of Objects 22 | public typealias Results = Response<[Self]> 23 | 24 | } 25 | 26 | public extension DataRepresentable { 27 | 28 | static var accept: String? { 29 | return nil 30 | } 31 | 32 | } 33 | 34 | /// Any object that can be sent through http as Data 35 | public protocol DataSerializable { 36 | var contentType: String? { get } 37 | var data: Data? { get } 38 | } 39 | 40 | public extension DataSerializable { 41 | 42 | var contentType: String? { 43 | return nil 44 | } 45 | 46 | } 47 | 48 | public extension DataSerializable { 49 | 50 | public func send(using api: T, 51 | method: HTTPMethod, 52 | at endpoint: T.Endpoint, 53 | arguments: [String:CustomStringConvertible] = .empty, 54 | headers: [String:CustomStringConvertible] = .empty, 55 | queries: [String:CustomStringConvertible] = .empty, 56 | auth: Auth = NoAuth.standard) -> Response { 57 | 58 | return api.doRepresentedRequest(with: method, to: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth, body: self) 59 | } 60 | 61 | public func put(using api: T, 62 | at endpoint: T.Endpoint, 63 | arguments: [String:CustomStringConvertible] = .empty, 64 | headers: [String:CustomStringConvertible] = .empty, 65 | queries: [String:CustomStringConvertible] = .empty, 66 | auth: Auth = NoAuth.standard) -> Response { 67 | 68 | return send(using: api, method: .put, at: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth) 69 | } 70 | 71 | public func post(using api: T, 72 | at endpoint: T.Endpoint, 73 | arguments: [String:CustomStringConvertible] = .empty, 74 | headers: [String:CustomStringConvertible] = .empty, 75 | queries: [String:CustomStringConvertible] = .empty, 76 | auth: Auth = NoAuth.standard) -> Response { 77 | 78 | return send(using: api, method: .post, at: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/Grant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Grant.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/27/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | enum Grant { 12 | case password(username: String, password: String, scope: String?) 13 | case refreshToken(token: String?) 14 | case authorizationCode(code: String) 15 | 16 | var dict: [String : String?] { 17 | switch self { 18 | case .password(let username, let password, let scope): 19 | return [ 20 | "grant_type": "password", 21 | "username": username, 22 | "password": password, 23 | "scope": scope 24 | ] 25 | case .refreshToken(let token): 26 | return [ 27 | "grant_type": "refresh_token", 28 | "refresh_token": token 29 | ] 30 | case .authorizationCode(let code): 31 | return [ 32 | "grant_type": "authoriation_code", 33 | "code": code 34 | ] 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/Mappable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mappable.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/21/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Deserializable: DataRepresentable { 12 | 13 | /// Initialize from json 14 | init?(from json: JSON) 15 | 16 | } 17 | 18 | public extension Deserializable { 19 | 20 | /// Initialize from Data 21 | public init?(data: Data) { 22 | guard let json = JSON(data: data) else { 23 | return nil 24 | } 25 | self.init(from: json) 26 | } 27 | 28 | } 29 | 30 | extension Deserializable { 31 | 32 | /// Create an Initializer by using a path 33 | public static func initializer(for path: [String]) -> (JSON) -> Self? { 34 | return JSON.get ** path 35 | } 36 | 37 | /// Create an Initializer by using a path 38 | public static func initializer(for path: String...) -> (JSON) -> Self? { 39 | return initializer(for: path) 40 | } 41 | 42 | } 43 | 44 | extension Deserializable { 45 | 46 | public static func get(using api: T, 47 | method: HTTPMethod = .get, 48 | at endpoint: T.Endpoint, 49 | arguments: [String:CustomStringConvertible] = .empty, 50 | headers: [String:CustomStringConvertible] = .empty, 51 | queries: [String:CustomStringConvertible] = .empty, 52 | auth: Auth = NoAuth.standard, 53 | for path: String...) -> Result { 54 | 55 | return api.doObjectRequest(with: method, to: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth, body: nil, at: path) 56 | } 57 | 58 | public static func getAll(using api: T, 59 | method: HTTPMethod = .get, 60 | at endpoint: T.Endpoint, 61 | arguments: [String:CustomStringConvertible] = .empty, 62 | headers: [String:CustomStringConvertible] = .empty, 63 | queries: [String:CustomStringConvertible] = .empty, 64 | auth: Auth = NoAuth.standard, 65 | for path: String..., 66 | using internalPath: [String] = .empty) -> Results { 67 | 68 | return api.doObjectsRequest(with: method, to: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth, body: nil, at: path) 69 | } 70 | 71 | } 72 | 73 | public protocol Serializable { 74 | var json: JSON { get } 75 | } 76 | 77 | extension Serializable { 78 | 79 | public func send(using api: T, 80 | method: HTTPMethod, 81 | at endpoint: T.Endpoint, 82 | arguments: [String:CustomStringConvertible] = .empty, 83 | headers: [String:CustomStringConvertible] = .empty, 84 | queries: [String:CustomStringConvertible] = .empty, 85 | auth: Auth = NoAuth.standard) -> JSON.Result { 86 | 87 | return api.doJSONRequest(with: method, to: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth, body: json) 88 | } 89 | 90 | public func put(using api: T, 91 | at endpoint: T.Endpoint, 92 | arguments: [String:CustomStringConvertible] = .empty, 93 | headers: [String:CustomStringConvertible] = .empty, 94 | queries: [String:CustomStringConvertible] = .empty, 95 | auth: Auth = NoAuth.standard) -> JSON.Result { 96 | 97 | return send(using: api, method: .put, at: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth) 98 | } 99 | 100 | public func post(using api: T, 101 | at endpoint: T.Endpoint, 102 | arguments: [String:CustomStringConvertible] = .empty, 103 | headers: [String:CustomStringConvertible] = .empty, 104 | queries: [String:CustomStringConvertible] = .empty, 105 | auth: Auth = NoAuth.standard) -> JSON.Result { 106 | 107 | return send(using: api, method: .post, at: endpoint, arguments: arguments, headers: headers, queries: queries, auth: auth) 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/OAuth.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OAuth.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 1/4/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct OAuth: Auth { 12 | 13 | let token: String 14 | let tokenType: String 15 | let refreshToken: String? 16 | let expirationDate: Date? 17 | 18 | var isExpired: Bool { 19 | guard let expirationDate = expirationDate else { 20 | return false 21 | } 22 | return expirationDate < .now 23 | } 24 | 25 | public func apply(to request: inout URLRequest) { 26 | request.addValue("\(tokenType) \(token)", forHTTPHeaderField: "Authorization") 27 | } 28 | 29 | } 30 | 31 | extension OAuth: Deserializable { 32 | 33 | public init?(from json: JSON) { 34 | guard let token = json["access_token"].string, 35 | let tokenType = json["token_type"].string else { 36 | return nil 37 | } 38 | self.init(token: token, tokenType: tokenType, 39 | refreshToken: json["refresh_token"].string, expirationDate: json["expiration"].date()) 40 | } 41 | 42 | } 43 | 44 | extension OAuth: StatusSerializable { 45 | 46 | public init?(from status: [String : Any]) { 47 | guard let token = status["token"] as? String, 48 | let tokenType = status["tokenType"] as? String else { 49 | return nil 50 | } 51 | self.init(token: token, tokenType: tokenType, refreshToken: (status["refresh"] as? String), 52 | expirationDate: (status["expiration"] as? String)?.date()) 53 | } 54 | 55 | public var serialized: [String : Any] { 56 | var dict = [ 57 | "token": token, 58 | "tokenType": tokenType 59 | ] 60 | dict["refresh"] <- refreshToken 61 | dict["expiration"] <- expirationDate?.string() 62 | return dict 63 | } 64 | 65 | } 66 | 67 | extension OAuth { 68 | 69 | public func store(using key: String) { 70 | OAuthStatus.key = OAUTHStatusKey(name: key) 71 | OAuthStatus.value = self 72 | } 73 | 74 | public static func stored(with key: String) -> OAuth? { 75 | OAuthStatus.key = OAUTHStatusKey(name: key) 76 | return OAuthStatus.value 77 | } 78 | 79 | } 80 | 81 | struct OAUTHStatusKey: StatusKey { 82 | let name: String 83 | 84 | var rawValue: String { 85 | return "OAUTH-\(name)" 86 | } 87 | } 88 | 89 | struct OAuthStatus: OptionalStatus { 90 | typealias Value = OAuth 91 | static var key = OAUTHStatusKey(name: "shared") 92 | } 93 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Networking/OAuthManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/27/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct OAuthManager: API { 12 | 13 | public typealias Endpoint = V 14 | public let baseURL: String 15 | public let clientID: String 16 | public let secret: String 17 | public let useBasicHttp: Bool 18 | public let useJSON: Bool 19 | 20 | public init(baseURL: String, clientID: String, secret: String, useBasicHttp: Bool = true, useJSON: Bool = false) { 21 | self.baseURL = baseURL 22 | self.clientID = clientID 23 | self.secret = secret 24 | self.useBasicHttp = useBasicHttp 25 | self.useJSON = useJSON 26 | } 27 | 28 | func auth() -> Auth { 29 | if useBasicHttp { 30 | return BasicAuth(username: clientID, password: secret) 31 | } else { 32 | return NoAuth.standard 33 | } 34 | } 35 | 36 | func body(with grant: Grant) -> [String:String] { 37 | var dict = grant.dict.dictionaryWithoutOptionals(byDividingWith: id) 38 | if !useBasicHttp { 39 | dict["client_id"] = clientID 40 | dict["client_secret"] = secret 41 | } 42 | return dict 43 | } 44 | 45 | private func jsonRequest(to endpoint: Endpoint, auth: Auth, body: [String: String]) -> OAuth.Result { 46 | return doObjectRequest(with: .post, 47 | to: endpoint, 48 | auth: auth, 49 | body: body.mapValues({ $0.json }).json) 50 | } 51 | 52 | private func queriedRequest(to endpoint: Endpoint, auth: Auth, body: [String:String]) -> OAuth.Result { 53 | return doObjectRequest(with: .post, to: endpoint, queries: body, auth: auth) 54 | } 55 | 56 | private func requestAuth(to endpoint: Endpoint, with grant: Grant) -> OAuth.Result { 57 | let auth = self.auth() 58 | let body = self.body(with: grant) 59 | if useJSON { 60 | return jsonRequest(to: endpoint, auth: auth, body: body) 61 | } else { 62 | return queriedRequest(to: endpoint, auth: auth, body: body) 63 | } 64 | } 65 | 66 | public func refresh(at endpoint: Endpoint, with auth: OAuth) -> OAuth.Result { 67 | return requestAuth(to: endpoint, with: .refreshToken(token: auth.refreshToken)) 68 | } 69 | 70 | public func authenticate(at endpoint: Endpoint, authorizationCode code: String) -> OAuth.Result { 71 | return requestAuth(to: endpoint, with: .authorizationCode(code: code)) 72 | } 73 | 74 | public func authenticate(at endpoint: Endpoint, username: String, password: String, scope: String...) -> OAuth.Result { 75 | let scope = scope.isEmpty ? nil : scope.join(with: " ") 76 | let grant: Grant = .password(username: username, password: password, scope: scope) 77 | return requestAuth(to: endpoint, with: grant) 78 | } 79 | 80 | } 81 | 82 | public extension API { 83 | 84 | func oauthManager(clientID: String, secret: String, useBasicHttp: Bool = true, useJSON: Bool = false) -> OAuthManager { 85 | return OAuthManager(baseURL: self.baseURL, clientID: clientID, secret: secret, useBasicHttp: useBasicHttp, useJSON: useJSON) 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Observer Pattern/Binding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Binding.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/26/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Binding between an observable and a mapping for it's characteristics 12 | public struct Binding { 13 | 14 | var value: T 15 | let mapping: (T) -> O 16 | 17 | /// Subscribe to the observable using the mapper 18 | public mutating func apply(to handler: @escaping (O) -> ()) { 19 | let mapping = self.mapping 20 | value | mapping | handler 21 | value.onChange(do: mapping >>> handler) 22 | } 23 | 24 | } 25 | 26 | extension Binding { 27 | 28 | /// Create Binding from a container 29 | init(container: C, mapping: @escaping (T) -> (O)) where C.ObservableItem == T { 30 | self.init(value: container.observable, mapping: mapping) 31 | } 32 | 33 | } 34 | 35 | /// Create a binding 36 | public func **(_ value: T?, _ mapping: @escaping (T) -> (O)) -> Binding? { 37 | guard let value = value else { 38 | return nil 39 | } 40 | return Binding(value: value, mapping: mapping) 41 | } 42 | 43 | /// Create a binding 44 | public func **(_ container: C?, _ mapping: @escaping (C.ObservableItem) -> (O)) -> Binding? { 45 | guard let container = container else { 46 | return nil 47 | } 48 | return Binding(container: container, mapping: mapping) 49 | } 50 | 51 | /// Apply handler to binding 52 | public func >>>(_ binding: Binding?, _ handler: @escaping (O) -> ()) { 53 | var binding = binding 54 | binding?.apply(to: handler) 55 | } 56 | 57 | /// Apply handler to observable 58 | public func >>>(_ value: T?, _ handler: @escaping (T) -> ()) { 59 | value ** id >>> handler 60 | } 61 | 62 | /// Apply handler to observable cointainer 63 | public func >>>(_ value: C?, _ handler: @escaping (C.ObservableItem) -> ()) { 64 | value ** id >>> handler 65 | } 66 | 67 | /// Apply handler to collection of observables 68 | public func >>>(_ items: C, _ handler: @escaping (C.Iterator.Element) -> ()) where C.Iterator.Element: Observable { 69 | items => { $0 >>> handler } 70 | } 71 | 72 | /// Apply handler to collection of observable containers 73 | public func >>>(_ items: C, _ handler: @escaping (C.Iterator.Element.ObservableItem) -> ()) where C.Iterator.Element: ObservableContainer { 74 | items => { $0 >>> handler } 75 | } 76 | 77 | /// Apply handlers to observables by indexing 78 | public func >>>(_ items: [T], _ handlers: [(T) -> ()]) { 79 | guard items.count == handlers.count else { 80 | return 81 | } 82 | handlers => { 83 | (items | $1) >>> $0 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Observer Pattern/Observable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Observable.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/26/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ObservableContainer { 12 | associatedtype ObservableItem: Observable 13 | var observable: ObservableItem { get } 14 | } 15 | 16 | public protocol Observable { 17 | var listeners: [Listener] { get set } 18 | } 19 | 20 | public extension Observable { 21 | 22 | typealias ListeningHandler = (Self) -> () 23 | typealias Listener = (handler: ListeningHandler, queue: DispatchQueue) 24 | 25 | public mutating func onChange(in completionQueue: DispatchQueue = .main, do handler: @escaping (Self) -> ()) { 26 | // Add listener somehow 27 | listeners.append((handler, completionQueue)) 28 | } 29 | 30 | public func hasChanged() { 31 | listeners => { $0.queue >>> $0.handler ** self } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Operators/Assignment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Assignment.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | infix operator <-: AssignmentPrecedence 11 | 12 | /** 13 | nil-proof Assignment. Will only assign the value if the value is not nil 14 | 15 | - Parameter variable: variable you want to assign it to 16 | - Parameter value: value you want to assign 17 | 18 | */ 19 | public func <-(_ variable: inout V?, _ value: V?) { 20 | variable = value ?? variable 21 | } 22 | 23 | /** 24 | nil-proof Assignment. Will only assign the value if the value is not nil 25 | 26 | - Parameter variable: variable you want to assign it to 27 | - Parameter value: value you want to assign 28 | 29 | */ 30 | public func <-(_ variable: inout V, _ value: V?) { 31 | variable = value ?? variable 32 | } 33 | 34 | /** 35 | Map assignment. Will assign the Result of map 36 | 37 | - Parameter items: array 38 | - Parameter handler: mapping function 39 | 40 | */ 41 | public func <-(_ items: inout [T], _ handler: (T) -> (T)) { 42 | items = items => handler 43 | } 44 | 45 | /** 46 | Map assignment. Will assign the Result of map 47 | 48 | - Parameter items: array 49 | - Parameter handler: mapping function 50 | 51 | */ 52 | public func <-(_ items: inout [T], _ handler: (T, Int) -> (T)) { 53 | items = items => handler 54 | } 55 | 56 | /** 57 | FlatMap assignment. Will assign the Result of flatMap 58 | 59 | - Parameter items: array 60 | - Parameter handler: flatMapping function 61 | 62 | */ 63 | public func <-(_ items: inout [T], _ handler: (T) -> (T?)) { 64 | items = items ==> handler 65 | } 66 | 67 | infix operator <|: AssignmentPrecedence 68 | 69 | /** 70 | Filter assignment. Will assign the Result of filter 71 | 72 | - Parameter items: array 73 | - Parameter handler: isIncluded function 74 | 75 | */ 76 | public func <|(_ items: inout [T], _ handler: (T) -> Bool) { 77 | items = items |> handler 78 | } 79 | 80 | /** 81 | Filter assignment. Will assign the Result of filter 82 | 83 | - Parameter items: array 84 | - Parameter handler: isIncluded function 85 | */ 86 | public func <|(_ items: inout [T], _ handler: (T, Int) -> Bool) { 87 | items = items |> handler 88 | } 89 | 90 | infix operator <=>: AssignmentPrecedence 91 | 92 | /** 93 | Swap. Will swap the two elements 94 | 95 | - Parameter a: first Element 96 | - Parameter b: second Element 97 | */ 98 | public func <=>(_ a: inout V, _ b: inout V) { 99 | swap(&a, &b) 100 | } 101 | 102 | postfix operator .? 103 | 104 | /** 105 | Unwrap with default. Will safely unwrap the value and return the default value of the type when nil 106 | 107 | - Parameter value: Value 108 | 109 | - Returns: Value when not nil and type default when nil 110 | */ 111 | public postfix func .?(_ value: V?) -> V { 112 | return value ?? .defaultValue 113 | } 114 | 115 | /** 116 | Unwrap array with default. 117 | 118 | - Parameter items: array 119 | 120 | - Returns: array with all the elements unwrapped with default. 121 | */ 122 | public postfix func .?(_ items: C) -> [T] where C.Iterator.Element == T? { 123 | return items => (.?) 124 | } 125 | 126 | prefix operator ?? 127 | 128 | /** 129 | nil-Check 130 | 131 | - Parameter value: Value 132 | 133 | - Returns: Whether or not it's nil 134 | */ 135 | public prefix func ??(_ value: V?) -> Bool { 136 | return value != nil 137 | } 138 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Operators/Math.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Functional.swift 3 | // 4 | // Created by Mathias Quintero on 11/20/16. 5 | // Copyright © 2016 Mathias Quintero. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | precedencegroup PowerPrecedence { 11 | associativity: right 12 | higherThan: MultiplicationPrecedence 13 | } 14 | 15 | infix operator **: PowerPrecedence 16 | 17 | /** 18 | Exponentiates 19 | 20 | - Parameter a: Base 21 | - Parameter b: Exponent 22 | 23 | - Returns: a to the b 24 | */ 25 | public func **(_ a: Double, _ b: Double) -> Double { 26 | return pow(a, b) 27 | } 28 | 29 | /** 30 | Exponentiates 31 | 32 | - Parameter a: Base 33 | - Parameter b: Exponent 34 | 35 | - Returns: a to the b 36 | */ 37 | public func **(_ a: Int, _ b: Int) -> Int { 38 | return Int(Double(a) ** Double(b)) 39 | } 40 | 41 | /** 42 | Remainder 43 | 44 | - Parameter a: number 45 | - Parameter b: divider 46 | 47 | - Returns: remainder of a after dividing by b 48 | */ 49 | public func %(_ a: Double, _ b: Double) -> Double { 50 | return a.remainder(dividingBy: b) 51 | } 52 | 53 | prefix operator | 54 | 55 | /** 56 | Abs 57 | 58 | - Parameter value: Number 59 | 60 | - Returns: absolut value of the input 61 | */ 62 | public prefix func |(_ value: Int) -> Int { 63 | return abs(value) 64 | } 65 | 66 | 67 | infix operator ~~: ComparisonPrecedence 68 | 69 | /** 70 | Matches 71 | 72 | - Parameter eft: String 73 | - Parameter ight: Pattern 74 | 75 | - Returns: does the string match the pattern 76 | */ 77 | public func ~~(left: String, right: String) -> Bool { 78 | return ??(try? left.matches(pattern: right, options: [])) 79 | } 80 | 81 | /** 82 | Get difference between two dates 83 | 84 | - Parameter left: firstDate 85 | - Parameter right: secondDate 86 | 87 | - Returns: DateDifference instance 88 | */ 89 | public func -(_ left: Date, _ right: Date) -> DateDifference { 90 | return DateDifference(first: left, second: right) 91 | } 92 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Operators/MultithreadingOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultithreadingOperators.swift 3 | // 4 | // Created by Mathias Quintero on 12/2/16. 5 | // 6 | // 7 | 8 | import Foundation 9 | 10 | infix operator >>>: MultiplicationPrecedence 11 | 12 | /** 13 | Runs a closure in a specific queue 14 | 15 | - Parameter queue: Queue the code should run in. 16 | - Parameter handler: function you want to run later 17 | 18 | */ 19 | public func >>>(_ queue: DispatchQueue,_ handler: @escaping () -> ()) { 20 | queue.async(execute: handler) 21 | } 22 | 23 | /** 24 | Runs a closure in a specific queue 25 | 26 | - Parameter qos: qos for the Queue the code should run in. 27 | - Parameter handler: function you want to run later 28 | 29 | */ 30 | public func >>>(_ qos: DispatchQoS,_ handler: @escaping () -> ()) { 31 | let queue = DispatchQueue(label: String(describing: qos), qos: qos) 32 | queue >>> handler 33 | } 34 | 35 | /** 36 | Runs a closure in a specific queue 37 | 38 | - Parameter label: label for the Queue the code should run in. 39 | - Parameter handler: function you want to run later 40 | 41 | */ 42 | public func >>>(_ label: String,_ handler: @escaping () -> ()) { 43 | let queue = DispatchQueue(label: label) 44 | queue >>> handler 45 | } 46 | 47 | /** 48 | Runs a closure after a time interval 49 | 50 | - Parameter time: time interval 51 | - Parameter handler: function you want to run later 52 | 53 | */ 54 | public func >>>(_ time: Double, handler: @escaping () -> ()) { 55 | after(time, handler: handler) 56 | } 57 | 58 | /** 59 | Runs a closure after a time interval 60 | 61 | - Parameter conditions: queue and time interval 62 | - Parameter handler: function you want to run later 63 | 64 | */ 65 | public func >>>(_ conditions: (DispatchQueue, Double), handler: @escaping () -> ()) { 66 | after(conditions.1, in: conditions.0, handler: handler) 67 | } 68 | 69 | /** 70 | Runs a closure after a time interval 71 | 72 | - Parameter conditions: label for queue and time interval 73 | - Parameter handler: function you want to run later 74 | 75 | */ 76 | public func >>>(_ conditions: (String, Double), handler: @escaping () -> ()) { 77 | let queue = DispatchQueue(label: conditions.0) 78 | (queue, conditions.1) >>> handler 79 | } 80 | 81 | /** 82 | Runs a closure after a time interval 83 | 84 | - Parameter c: time interval and queue 85 | - Parameter handler: function you want to run later 86 | 87 | */ 88 | public func >>>(_ c: (Double, DispatchQueue), handler: @escaping () -> ()) { 89 | (c.1, c.0) >>> handler 90 | } 91 | 92 | /** 93 | Runs a closure after a time interval 94 | 95 | - Parameter c: time interval and label for the queue 96 | - Parameter handler: function you want to run later 97 | 98 | */ 99 | public func >>>(_ c: (Double, String), handler: @escaping () -> ()) { 100 | let queue = DispatchQueue(label: c.1) 101 | (queue, c.0) >>> handler 102 | } 103 | 104 | /** 105 | Chain closures. Will Chain two closures and make the output of the first one the input to the second one. 106 | 107 | - Parameter funA: first function 108 | - Parameter funB: second function 109 | 110 | */ 111 | public func >>>(_ funA: @escaping (A) -> (B), _ funB: @escaping (B) -> (C)) -> (A) -> (C) { 112 | return { input in 113 | input | funA | funB 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Operators/ReducingParametersOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReducingParametersOperators.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/7/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Creates a Reducing Parameter 13 | 14 | - Parameters: 15 | - nextPartialResult: nextPartialResult Handler for reduce 16 | - initialResult: initial result for reduce 17 | 18 | - Returns: Reducing Parameter 19 | */ 20 | public func **(_ nextPartialResult: P.NextPartialResultHandler, _ initialResult: P.Result) -> P { 21 | return P(initialResult: initialResult, nextPartialResult: nextPartialResult) 22 | } 23 | 24 | /** 25 | Creates a Reducing Parameter 26 | 27 | - Parameter initialResult: initial result for reduce 28 | - Parameter nextPartialResult: nextPartialResult Handler for reduce 29 | 30 | - Returns: Reducing Parameter 31 | */ 32 | public func **(_ initialResult: P.Result, _ nextPartialResult: P.NextPartialResultHandler) -> P { 33 | return nextPartialResult ** initialResult 34 | } 35 | 36 | /** 37 | Reduce 38 | 39 | - Parameter items: collection 40 | - Parameter reducingParameters: Reducing Parameters 41 | 42 | - Returns: Result of reduce 43 | */ 44 | public func ==>(_ items: C, _ reducingParameters: RegularReducingParameters) -> R { 45 | return items.reduce(reducingParameters.initialResult, reducingParameters.nextPartialResult) 46 | } 47 | 48 | /** 49 | Reduce with index 50 | 51 | - Parameter items: collection 52 | - Parameter reducingParameters: Reducing Parameters with index 53 | 54 | - Returns: Result of reduce 55 | */ 56 | public func ==>(_ items: [I], _ reducingParameters: ReducingParametersWithIndex) -> R { 57 | return items.reduce(reducingParameters.initialResult, reducingParameters.nextPartialResult) 58 | } 59 | 60 | prefix operator > 61 | 62 | /** 63 | Default Reducing Parameters. Creates them with the default value for the type of the result 64 | 65 | - Parameter nextPartialResult: nextPartialResult Handler for reduce 66 | 67 | - Returns: Result of reduce 68 | */ 69 | public prefix func >(_ nextPartialResult: P.NextPartialResultHandler) -> P where P.Result: Defaultable { 70 | return P.Result.defaultValue ** nextPartialResult 71 | } 72 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/PriorityQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PriorityQueue.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 2/22/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public class PriorityQueue { 12 | 13 | var itemPositions: [T:Int] = .empty 14 | var items: [(item: T, priority: P)] = .empty 15 | 16 | public var count: Int { 17 | return items.count 18 | } 19 | 20 | public var isEmpty: Bool { 21 | return count < 1 22 | } 23 | 24 | private func swap(_ a: Int, _ b: Int) { 25 | items[a] <=> items[b] 26 | itemPositions[items[a].item] = a 27 | itemPositions[items[b].item] = b 28 | } 29 | 30 | private func childrenOfItem(at index: Int) -> [(element: (item: T, priority: P), index: Int)] { 31 | let indexes = [2 * index + 1, 2 * index + 2] 32 | return self.items.withIndex | indexes 33 | } 34 | 35 | private func siftUp(at index: Int) { 36 | guard index > 0 else { 37 | return 38 | } 39 | let parentIndex = (index - 1)/2 40 | let parent = items[parentIndex] 41 | let current = items[index] 42 | if parent.priority > current.priority { 43 | self.swap(parentIndex, index) 44 | siftUp(at: parentIndex) 45 | } 46 | } 47 | 48 | private func siftDown(at index: Int) { 49 | let children = childrenOfItem(at: index) 50 | guard let child = children.argmin({ $0.element.priority }) else { 51 | return 52 | } 53 | if child.element.priority < items[index].priority { 54 | self.swap(child.index, index) 55 | siftDown(at: child.index) 56 | } 57 | } 58 | 59 | public func priority(for item: T) -> P? { 60 | guard let position = itemPositions[item] else { 61 | return nil 62 | } 63 | return items[position].priority 64 | } 65 | 66 | public func update(_ item: T, with priority: P) { 67 | guard let position = itemPositions[item] else { 68 | return 69 | } 70 | items[position].priority = priority 71 | siftUp(at: position) 72 | if let position = itemPositions[item] { 73 | siftUp(at: position) 74 | } 75 | } 76 | 77 | public func add(_ item: T, with priority: P) { 78 | items.append((item, priority)) 79 | itemPositions[item] = items.lastIndex 80 | siftUp(at: items.lastIndex) 81 | } 82 | 83 | public func popWithPriority() -> (T, P)? { 84 | if count > 1 { 85 | swap(0, items.lastIndex) 86 | let item = items.removeLast() 87 | itemPositions[item.item] = nil 88 | siftDown(at: 0) 89 | return item 90 | } else { 91 | itemPositions = .empty 92 | let item = items.first 93 | items = .empty 94 | return item 95 | } 96 | } 97 | 98 | public func pop() -> T? { 99 | return popWithPriority()?.0 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Promises/BulkPromise.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BulkPromise.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/29/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// A promise that represent a collection of other promises and waits for them all to be finished 12 | public final class BulkPromise: Promise<[T], O> { 13 | 14 | private let count: Int 15 | private var results: [(Int, T)] = .empty { 16 | didSet { 17 | if results.count == count { 18 | success(with: results.sorted(ascending: firstArgument) => lastArgument) 19 | } 20 | } 21 | } 22 | 23 | public init(promises: [Promise], completionQueue: DispatchQueue = .main) { 24 | count = promises.count 25 | super.init(completionQueue: completionQueue) 26 | promises => { promise, index in 27 | promise.nest(to: self) { result in 28 | self.results.append((index, result)) 29 | } 30 | } 31 | } 32 | 33 | } 34 | 35 | extension BulkPromise where T: Collection { 36 | 37 | var flattened: Promise<[T.Iterator.Element], O> { 38 | return nested { result in 39 | return result.flatMap(id) 40 | } 41 | } 42 | 43 | } 44 | 45 | extension BulkPromise: ExpressibleByArrayLiteral { 46 | 47 | public convenience init(arrayLiteral elements: Promise...) { 48 | self.init(promises: elements) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/Promises/PromiseHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PromiseHandler.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/11/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Enum For Representing an Empty Error Domain 12 | public enum NoError: Error {} 13 | public enum AnyError: Error { 14 | case error(Error) 15 | } 16 | 17 | /// Structure that allows us to nest callbacks more nicely 18 | public struct PromiseSuccessHandler { 19 | 20 | typealias Handler = (T) -> R 21 | 22 | private weak var promise: Promise! 23 | private var handler: Handler 24 | 25 | init(promise: Promise, handler: @escaping Handler) { 26 | self.promise = promise 27 | self.handler = handler 28 | promise.successHandlers.append(handler**) 29 | promise.state.result | handler** 30 | } 31 | 32 | /// Add an action after the current item 33 | @discardableResult public func then(_ handler: @escaping (R) -> (O)) -> PromiseSuccessHandler { 34 | _ = promise.successHandlers.popLast() 35 | return PromiseSuccessHandler(promise: promise, handler: self.handler >>> handler) 36 | } 37 | 38 | /// Add a success Handler 39 | @discardableResult public func and(call handler: @escaping (T) -> (O)) -> PromiseSuccessHandler { 40 | return promise.onSuccess(call: handler) 41 | } 42 | 43 | /// Add an error Handler 44 | @discardableResult public func onError(call handler: @escaping (E) -> (O)) -> PromiseErrorHandler { 45 | return promise.onError(call: handler) 46 | } 47 | 48 | } 49 | 50 | public extension PromiseSuccessHandler where R: PromiseBody { 51 | 52 | /// Promise returned by the handler 53 | public var future: Promise { 54 | let promise = Promise() 55 | then { 56 | $0.nest(to: promise, using: id) 57 | } 58 | return promise 59 | } 60 | 61 | } 62 | 63 | /// Structure that allows us to nest callbacks more nicely 64 | public struct PromiseErrorHandler { 65 | 66 | typealias Handler = (E) -> R 67 | 68 | private var promise: Promise 69 | private var handler: Handler 70 | 71 | init(promise: Promise, handler: @escaping Handler) { 72 | self.promise = promise 73 | self.handler = handler 74 | promise.errorHandlers.append(handler**) 75 | promise.state.error | handler** 76 | } 77 | 78 | /// Add an action to be done afterwards 79 | @discardableResult public func then(_ handler: @escaping (R) -> (O)) -> PromiseErrorHandler { 80 | _ = promise.errorHandlers.popLast() 81 | return PromiseErrorHandler(promise: promise, handler: self.handler >>> handler) 82 | } 83 | 84 | /// Add a success Handler 85 | @discardableResult public func onSuccess(call handler: @escaping (T) -> (O)) -> PromiseSuccessHandler { 86 | return promise.onSuccess(call: handler) 87 | } 88 | 89 | /// Add an error Handler 90 | @discardableResult public func and(call handler: @escaping (E) -> (O)) -> PromiseErrorHandler { 91 | return promise.onError(call: handler) 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/ReducingParameters.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // ReducingParameters.swift 4 | // Pods 5 | // 6 | // Created by Mathias Quintero on 12/7/16. 7 | // 8 | // 9 | 10 | import Foundation 11 | 12 | /// Wrapper for parameters in reduce 13 | public protocol ReducingParameters { 14 | 15 | /// Input of the collection 16 | associatedtype Input 17 | /// Result of the reducing function 18 | associatedtype Result 19 | /// Format of the nextPartialResult handler 20 | associatedtype NextPartialResultHandler 21 | 22 | /// Initial value given to the result 23 | var initialResult: Result { get } 24 | /// Reducing function 25 | var nextPartialResult: NextPartialResultHandler { get } 26 | 27 | 28 | /// Initializer 29 | init(initialResult: Result, nextPartialResult: NextPartialResultHandler) 30 | } 31 | 32 | /// Wrapper for the regular reduce 33 | public struct RegularReducingParameters: ReducingParameters { 34 | 35 | public typealias Input = I 36 | /// Result of the reducing function 37 | public typealias Result = R 38 | /// Format of the nextPartialResult handler 39 | public typealias NextPartialResultHandler = (Result, Input) -> (Result) 40 | 41 | /// Initial value given to the result 42 | public let initialResult: Result 43 | /// Reducing function 44 | public let nextPartialResult: NextPartialResultHandler 45 | 46 | /// Initializer 47 | public init(initialResult: Result, nextPartialResult: @escaping NextPartialResultHandler) { 48 | self.initialResult = initialResult 49 | self.nextPartialResult = nextPartialResult 50 | } 51 | 52 | } 53 | 54 | /// Wrapper for the reduce with index 55 | public struct ReducingParametersWithIndex: ReducingParameters { 56 | 57 | /// Input of the collection 58 | public typealias Input = I 59 | /// Result of the reducing function 60 | public typealias Result = R 61 | /// Format of the nextPartialResult handler 62 | public typealias NextPartialResultHandler = (Result, Input, Int) -> (Result) 63 | 64 | /// Initial value given to the result 65 | public let initialResult: Result 66 | /// Reducing function 67 | public let nextPartialResult: NextPartialResultHandler 68 | 69 | /// Initializer 70 | public init(initialResult: Result, nextPartialResult: @escaping NextPartialResultHandler) { 71 | self.initialResult = initialResult 72 | self.nextPartialResult = nextPartialResult 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/UserDefaults/ObjectStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectStatus.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/11/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Wrapper for User Defaults to store an object 12 | public protocol ObjectStatus { 13 | /// Value type 14 | associatedtype Value: StatusSerializable 15 | /// Key type 16 | associatedtype Key: StatusKey 17 | 18 | /// Key for defaults 19 | static var key: Key { get } 20 | /// Default value in case it's not defined 21 | static var defaultValue: Value { get } 22 | } 23 | 24 | public extension ObjectStatus { 25 | 26 | /// Value in Defaults for your Status 27 | static var value: Value { 28 | get { 29 | return Value.init(from: DictionaryStatus(key: key).value) ?? defaultValue 30 | } 31 | set { 32 | var status = DictionaryStatus(key: key) 33 | status.value = newValue.serialized 34 | } 35 | } 36 | 37 | /// Reset the value 38 | static func reset() { 39 | value = defaultValue 40 | } 41 | 42 | } 43 | 44 | /// If the value is Defaultable you don't have to specify a default value anymore 45 | public extension ObjectStatus where Value: Defaultable { 46 | 47 | static var defaultValue: Value { 48 | return .defaultValue 49 | } 50 | 51 | } 52 | 53 | /// Wrapper for User Defaults to store an Array of Objects 54 | public protocol CollectionStatus { 55 | /// Value type 56 | associatedtype Value: StatusSerializable 57 | /// Key type 58 | associatedtype Key: StatusKey 59 | 60 | /// Key for defaults 61 | static var key: Key { get } 62 | /// Default value in case it's not defined 63 | static var defaultValue: [Value] { get } 64 | /// Determines if it should fail it at least one of the items isn't deserializable 65 | static var allOrNothing: Bool { get } 66 | } 67 | 68 | public extension CollectionStatus { 69 | 70 | static var values: [Value] { 71 | get { 72 | guard let array = SimpleStatus(key: key, defaultValue: nil).value else { 73 | return defaultValue 74 | } 75 | let items = array ==> Value.init 76 | if allOrNothing { 77 | guard items.count == array.count else { 78 | return defaultValue 79 | } 80 | } 81 | return items 82 | } 83 | set { 84 | var status = SimpleStatus(key: key, defaultValue: nil) 85 | status.value = newValue => { $0.serialized } 86 | } 87 | } 88 | 89 | static var defaultValue: [Value] { 90 | return .empty 91 | } 92 | 93 | static var allOrNothing: Bool { 94 | return true 95 | } 96 | 97 | } 98 | 99 | enum OptionalObjectStatusSerialized { 100 | case none 101 | case some(value: V) 102 | 103 | init(from value: V?) { 104 | if let value = value { 105 | self = .some(value: value) 106 | } else { 107 | self = .none 108 | } 109 | } 110 | 111 | var value: V? { 112 | switch self { 113 | case .some(let value): 114 | return value 115 | default: 116 | return nil 117 | } 118 | } 119 | } 120 | 121 | extension OptionalObjectStatusSerialized: StatusSerializable { 122 | 123 | init?(from serialized: [String:Any]) { 124 | guard let value = V.init(from: serialized) else { 125 | self = .none 126 | return 127 | } 128 | self = .some(value: value) 129 | } 130 | 131 | var serialized: [String : Any] { 132 | switch self { 133 | case .some(let value): 134 | return value.serialized 135 | default: 136 | return .empty 137 | } 138 | } 139 | 140 | } 141 | 142 | extension OptionalObjectStatusSerialized: Defaultable { 143 | 144 | /// Default Value 145 | static var defaultValue: OptionalObjectStatusSerialized { 146 | return .none 147 | } 148 | 149 | } 150 | 151 | /// Wrapper for User Defaults to store an object that can be an optional 152 | public protocol OptionalStatus { 153 | 154 | /// Value type 155 | associatedtype Value: StatusSerializable 156 | /// Key type 157 | associatedtype Key: StatusKey 158 | 159 | /// Key for defaults 160 | static var key: Key { get } 161 | /// Default value in case it's not defined 162 | static var defaultValue: Value? { get } 163 | 164 | } 165 | 166 | public extension OptionalStatus { 167 | 168 | static var value: Value? { 169 | get { 170 | let value = DictionaryStatus(key: key).value 171 | return OptionalObjectStatusSerialized(from: value)?.value ?? defaultValue 172 | } 173 | set { 174 | var status = DictionaryStatus(key: key) 175 | status.value = OptionalObjectStatusSerialized(from: newValue).serialized 176 | } 177 | } 178 | 179 | static var defaultValue: Value? { 180 | return nil 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/UserDefaults/Status.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleStatus.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/11/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Wrapper for User Defaults to store a value 12 | public protocol Status { 13 | /// Valuetype 14 | associatedtype Value 15 | /// Keytype 16 | associatedtype Key: StatusKey 17 | 18 | /// Key for defaults 19 | static var key: Key { get } 20 | /// Default value in case the value is not defined 21 | static var defaultValue: Value { get } 22 | } 23 | 24 | public extension Status { 25 | 26 | /// Value stored in defaults 27 | static var value: Value { 28 | get { 29 | return SimpleStatus(key: key, defaultValue: defaultValue).value 30 | } 31 | set { 32 | var status = SimpleStatus(key: key, defaultValue: defaultValue) 33 | status.value = newValue 34 | } 35 | } 36 | 37 | /// Reset the value back to the default 38 | static func reset() { 39 | value = defaultValue 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/UserDefaults/StatusFetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultSaving.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/11/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | protocol StatusFetcher { 12 | associatedtype Key: StatusKey 13 | associatedtype Value 14 | 15 | var key: Key { get } 16 | var defaultValue: Value { get } 17 | } 18 | 19 | extension StatusFetcher { 20 | 21 | var value: Value { 22 | get { 23 | return UserDefaults.standard.object(forKey: key.userDefaultsKey) as? Value ?? defaultValue 24 | } 25 | set { 26 | UserDefaults.standard.set(newValue, forKey: key.userDefaultsKey) 27 | } 28 | } 29 | 30 | } 31 | 32 | struct SimpleStatus: StatusFetcher { 33 | let key: K 34 | let defaultValue: V 35 | } 36 | 37 | extension SimpleStatus where V: Defaultable { 38 | 39 | init(key: K) { 40 | self.key = key 41 | self.defaultValue = V.defaultValue 42 | } 43 | 44 | } 45 | 46 | typealias DictionaryStatus = SimpleStatus 47 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/UserDefaults/StatusKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultStatusKey.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/11/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Key usable to store a status 12 | public protocol StatusKey { 13 | /// Raw value string for the key 14 | var rawValue: String { get } 15 | } 16 | 17 | extension StatusKey { 18 | 19 | var userDefaultsKey: String { 20 | return String(describing: Self.self) + "." + rawValue 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/UserDefaults/StatusSerializable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultStatusSerializable.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 12/11/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Item that is serializable for UserDefaults 12 | public protocol StatusSerializable { 13 | /// Serialized into dictionary 14 | var serialized: [String:Any] { get } 15 | /// get from serialized dictionary 16 | init?(from status: [String:Any]) 17 | } 18 | -------------------------------------------------------------------------------- /Pods/Sweeft/Sources/Sweeft/ValueComparable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValueComparable.swift 3 | // Pods 4 | // 5 | // Created by Mathias Quintero on 1/4/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ValueComparable: Comparable { 12 | associatedtype ComparableValue: Comparable 13 | var comparable: ComparableValue { get } 14 | } 15 | 16 | public func ==(_ lhs: C, _ rhs: C) -> Bool { 17 | return lhs.comparable == rhs.comparable 18 | } 19 | 20 | public func <(_ lhs: C, _ rhs: C) -> Bool { 21 | return lhs.comparable < rhs.comparable 22 | } 23 | 24 | public func <=(_ lhs: C, _ rhs: C) -> Bool { 25 | return lhs.comparable <= rhs.comparable 26 | } 27 | 28 | public func >(_ lhs: C, _ rhs: C) -> Bool { 29 | return lhs.comparable > rhs.comparable 30 | } 31 | 32 | public func >=(_ lhs: C, _ rhs: C) -> Bool { 33 | return lhs.comparable >= rhs.comparable 34 | } 35 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Sweeft 5 | 6 | MIT License 7 | 8 | Copyright (c) 2016 Mathias Quintero 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2016 Mathias Quintero 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | Sweeft 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Spotify2AppleMusic : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Spotify2AppleMusic 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/Sweeft/Sweeft.framework" 93 | fi 94 | if [[ "$CONFIGURATION" == "Release" ]]; then 95 | install_framework "$BUILT_PRODUCTS_DIR/Sweeft/Sweeft.framework" 96 | fi 97 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 98 | wait 99 | fi 100 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_Spotify2AppleMusicVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_Spotify2AppleMusicVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Sweeft" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Sweeft/Sweeft.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Sweeft" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Spotify2AppleMusic { 2 | umbrella header "Pods-Spotify2AppleMusic-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Spotify2AppleMusic/Pods-Spotify2AppleMusic.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Sweeft" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Sweeft/Sweeft.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Sweeft" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Sweeft/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.5.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Sweeft/Sweeft-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Sweeft : NSObject 3 | @end 4 | @implementation PodsDummy_Sweeft 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Sweeft/Sweeft-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Sweeft/Sweeft-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double SweeftVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SweeftVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Sweeft/Sweeft.modulemap: -------------------------------------------------------------------------------- 1 | framework module Sweeft { 2 | umbrella header "Sweeft-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Sweeft/Sweeft.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Sweeft 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "Foundation" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Sweeft 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spotify to  Music Converter 2 | ------ 3 | Import your Spotify Playlists to Apple Music! 4 | 5 | ## Cool! How do I use this though? 6 | 7 | 1. Get a Mac, this won't run on Windows 8 | 2. Download [Xcode](https://itunes.apple.com/de/app/xcode/id497799835?mt=12) and open it once to install all it's shit stuff 9 | 3. Download this repo as a `.zip` or clone it in git and open up the Xcode project, connect your phone, select your **developer account** for signing and hit Run. 10 | 10. This will install the app onto your phone, press the humongous ***"GO"*** button, log in and select the playlist you want to sync 11 | 11. ??? 12 | 12. Profit 13 | 14 | ## But why? 15 | Some people like me wanted to switch from Spotify to Apple Music, but didn't want to lose all their playlists, so here ya go 16 | 17 | ## Can I use that? 18 | I don't know if you can, but you may, this project is licensed [MIT](LICENSE), so you can even redistribute it, as long as you mention me somehow. 19 | 20 | ## Credits 21 | Go to my friend Felix, who had the idea for this and to [mathiasquintero](https://github.com/mathiasquintero) who did a huge pull request adding Spotify integration and more convenience. 22 | The icon was made by [Skirmantas Raila](https://dribbble.com/shots/1783674-iTunes). 23 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcodeproj/project.xcworkspace/xcuserdata/fga.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Spotify2AppleMusic.xcodeproj/project.xcworkspace/xcuserdata/fga.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcodeproj/xcuserdata/fga.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcodeproj/xcuserdata/fga.xcuserdatad/xcschemes/Spotify2AppleMusic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcodeproj/xcuserdata/fga.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Spotify2AppleMusic.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C53617AE1E658E8E00D82BA4 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcodeproj/xcuserdata/mathiasquintero.xcuserdatad/xcschemes/Spotify2AppleMusic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcodeproj/xcuserdata/mathiasquintero.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Spotify2AppleMusic.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C53617AE1E658E8E00D82BA4 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcworkspace/xcuserdata/fga.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Spotify2AppleMusic.xcworkspace/xcuserdata/fga.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcworkspace/xcuserdata/mathiasquintero.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Spotify2AppleMusic.xcworkspace/xcuserdata/mathiasquintero.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Spotify2AppleMusic.xcworkspace/xcuserdata/mathiasquintero.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Spotify2AppleMusic/.DS_Store -------------------------------------------------------------------------------- /Spotify2AppleMusic/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Finn Gaida on 28/02/2017. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Sweeft 11 | import SafariServices 12 | 13 | @UIApplicationMain 14 | class AppDelegate: UIResponder, UIApplicationDelegate { 15 | 16 | var window: UIWindow? 17 | 18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 19 | 20 | // Override point for customization after application launch. 21 | return true 22 | } 23 | 24 | func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { 25 | return Spotify.shared.callback(url: url) 26 | } 27 | 28 | func applicationWillResignActive(_ application: UIApplication) { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 31 | } 32 | 33 | func applicationDidEnterBackground(_ application: UIApplication) { 34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | func applicationWillEnterForeground(_ application: UIApplication) { 39 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 40 | } 41 | 42 | func applicationDidBecomeActive(_ application: UIApplication) { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | func applicationWillTerminate(_ application: UIApplication) { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "20x20", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "20x20", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "29x29", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "idiom" : "ipad", 77 | "size" : "76x76", 78 | "scale" : "1x" 79 | }, 80 | { 81 | "idiom" : "ipad", 82 | "size" : "76x76", 83 | "scale" : "2x" 84 | }, 85 | { 86 | "idiom" : "ipad", 87 | "size" : "83.5x83.5", 88 | "scale" : "2x" 89 | } 90 | ], 91 | "info" : { 92 | "version" : 1, 93 | "author" : "xcode" 94 | } 95 | } -------------------------------------------------------------------------------- /Spotify2AppleMusic/Assets.xcassets/AppIcon.appiconset/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Spotify2AppleMusic/Assets.xcassets/AppIcon.appiconset/icon@2x.png -------------------------------------------------------------------------------- /Spotify2AppleMusic/Assets.xcassets/AppIcon.appiconset/icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finngaida/spotify2applemusic/e0ee816b276e5ec11601bc1f57af007ba116b846/Spotify2AppleMusic/Assets.xcassets/AppIcon.appiconset/icon@3x.png -------------------------------------------------------------------------------- /Spotify2AppleMusic/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/ImportSelection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImportSelection.swift 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Mathias Quintero on 3/1/17. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | import Sweeft 10 | 11 | enum ImportSelection { 12 | case all 13 | case playlist(SPTPartialPlaylist) 14 | } 15 | 16 | extension ImportSelection { 17 | 18 | var description: String { 19 | switch self { 20 | case .all: 21 | return "All Your Music" 22 | case .playlist(let playlist): 23 | return playlist.name 24 | } 25 | } 26 | 27 | } 28 | 29 | extension ImportSelection { 30 | 31 | func songs() -> Promise<[SpotifySong], APIError> { 32 | switch self { 33 | case .all: 34 | return Spotify.shared.fetchSongs() 35 | case .playlist(let playlist): 36 | return Spotify.shared.songs(in: playlist) 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleURLTypes 20 | 21 | 22 | CFBundleTypeRole 23 | Editor 24 | CFBundleURLName 25 | io.quintero.Spotify2AppleMusic.spotify-auth 26 | CFBundleURLSchemes 27 | 28 | spotify2appleMusic 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSRequiresIPhoneOS 35 | 36 | NSAppleMusicUsageDescription 37 | Bla 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIMainStoryboardFile 41 | Main 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/Itunes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Itunes.swift 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Mathias Quintero on 3/1/17. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | import Sweeft 10 | 11 | enum ItunesEndpoint: String, APIEndpoint { 12 | case search = "search" 13 | } 14 | 15 | struct Itunes: API { 16 | typealias Endpoint = ItunesEndpoint 17 | let baseURL: String = "https://itunes.apple.com/WebObjects/MZStore.woa/wa/" 18 | } 19 | 20 | extension JSON { 21 | 22 | var isSong: Bool { 23 | let offers = self["offers"].array => { $0["price"].double } |> { $0 == 0.0 } 24 | return self["kind"].string == "song" && !offers.isEmpty 25 | } 26 | 27 | } 28 | 29 | extension Itunes { 30 | 31 | func search(for song: SpotifySong) -> Promise { 32 | return doJSONRequest(to: .search, 33 | headers: ["X-Apple-Store-Front" : "143446-10,32 ab:rSwnYxS0 t:music2", "X-Apple-Tz" : "7200"], 34 | queries: ["clientApplication": "MusicPlayer", "term": song.term, "entity": "song"]).nested { json, promise in 35 | 36 | 37 | let songs = json["storePlatformData"]["lockup"]["results"].dict => lastArgument |> { $0.isSong } 38 | let possibleSongs = songs |> song.artistMatches 39 | let matchingSongs = possibleSongs |> song.nameMatches 40 | let albumMatchingSongs = matchingSongs |> song.albumMatches 41 | let dict = albumMatchingSongs.first ?? matchingSongs.first 42 | if dict == nil { 43 | print("Didn't find \(song.term)") 44 | } 45 | let song = dict?["id"].string | Song.initializer(for: song) 46 | promise.success(with: song) 47 | } 48 | } 49 | 50 | func search(for songs: [SpotifySong]) -> Promise<[Song], APIError> { 51 | return BulkPromise(promises: songs => { self.search(for: $0) }).nested { 52 | return $0.flatMap { $0 } 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/Song.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Song.swift 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Mathias Quintero on 2/28/17. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | import Sweeft 10 | 11 | struct SpotifySong { 12 | let name: String 13 | let artists: [String] 14 | let album: String 15 | } 16 | 17 | extension SpotifySong { 18 | 19 | init(from track: SPTTrack) { 20 | let artists = track.artists.flatMap { $0 as? SPTPartialArtist } 21 | self.init(name: track.name, artists: artists => { $0.name }, album: track.album.name) 22 | } 23 | 24 | } 25 | 26 | extension SpotifySong { 27 | 28 | var term: String { 29 | return "\(name) \(artists.first.?)" 30 | } 31 | 32 | } 33 | 34 | extension SpotifySong { 35 | 36 | func artistMatches(json: JSON) -> Bool { 37 | guard let artist = json["artistName"].string?.lowercased() else { 38 | return false 39 | } 40 | let artists = self.artists => { $0.lowercased() } 41 | return artists.join(with: " ").contains(artist) || !(artists |> artist.contains).isEmpty 42 | } 43 | 44 | func nameMatches(json: JSON) -> Bool { 45 | guard let name = json["name"].string?.songFormatted else { 46 | return false 47 | } 48 | let own = self.name.songFormatted 49 | return own.contains(name) || name.contains(own) 50 | } 51 | 52 | func albumMatches(json: JSON) -> Bool { 53 | guard let album = json["collectinName"].string?.songFormatted else { 54 | return false 55 | } 56 | return self.album.songFormatted.contains(album) || album.contains(self.album.songFormatted) 57 | } 58 | 59 | } 60 | 61 | extension String { 62 | 63 | var songFormatted: String { 64 | return lowercased().replacingOccurrences(of: " - ", with: " ").replacingOccurrences(of: "(", with: .empty).replacingOccurrences(of: ")", with: "").replacingOccurrences(of: " single", with: "") 65 | } 66 | 67 | } 68 | 69 | struct Song { 70 | let name: String 71 | let artist: String 72 | let id: String 73 | } 74 | 75 | extension Song { 76 | 77 | static func initializer(for song: SpotifySong) -> (String) -> Song { 78 | return { 79 | return Song(name: song.name, artist: song.artists.first.?, id: $0) 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/Spotify.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Spotify.swift 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Mathias Quintero on 2/28/17. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SafariServices 11 | import Sweeft 12 | 13 | class Spotify { 14 | 15 | static var shared = Spotify() 16 | 17 | lazy var auth: SPTAuth! = { 18 | let auth = SPTAuth.defaultInstance() 19 | auth?.clientID = "987ecaba09cd47b1af5ecc13ad675aa9" 20 | let url = URL(string: "spotify2appleMusic://login/") 21 | auth?.redirectURL = url 22 | auth?.sessionUserDefaultsKey = "spotify session" 23 | auth?.requestedScopes = [SPTAuthUserLibraryReadScope] 24 | return auth 25 | }() 26 | 27 | var authViewController: UIViewController? 28 | 29 | var isLoggedIn: Bool { 30 | return auth.session?.isValid() ?? false 31 | } 32 | 33 | private init() { 34 | } 35 | 36 | private func handlePages(_ page: SPTListPage) -> Promise<[Item], APIError> { 37 | let promise = Promise<[Item], APIError>() 38 | let items = page.items.flatMap { $0 as? Item } 39 | if page.hasNextPage { 40 | page.requestNextPage(withAccessToken: auth.session.accessToken) { error, result in 41 | guard let result = result as? SPTListPage, error == nil else { 42 | promise.error(with: .unknown(error: error!)) 43 | return 44 | } 45 | self.handlePages(result).nest(to: promise) { songs in 46 | return items + songs 47 | } 48 | } 49 | } else { 50 | promise.success(with: items) 51 | } 52 | return promise 53 | } 54 | 55 | func playlists() -> Promise<[ImportSelection], APIError> { 56 | let promise = Promise<[ImportSelection], APIError>() 57 | guard let session = auth.session else { 58 | promise.error(with: .noData) 59 | return promise 60 | } 61 | SPTPlaylistList.playlists(forUser: session.canonicalUsername, withAccessToken: session.accessToken) { error, result in 62 | guard let result = result as? SPTListPage, error == nil else { 63 | promise.error(with: .unknown(error: error!)) 64 | return 65 | } 66 | self.handlePages(result).nested({ $0 => ImportSelection.playlist }).nest(to: promise, using: id) 67 | } 68 | return promise 69 | } 70 | 71 | func songs(in playlist: SPTPartialPlaylist) -> Promise<[SpotifySong], APIError> { 72 | let promise = Promise<[SpotifySong], APIError>() 73 | SPTPlaylistSnapshot.playlist(withURI: playlist.uri, accessToken: auth.session.accessToken) { error, result in 74 | guard let result = result as? SPTPlaylistSnapshot, error == nil else { 75 | promise.error(with: .unknown(error: error!)) 76 | return 77 | } 78 | let tracks = result.tracksForPlayback().flatMap { $0 as? SPTPlaylistTrack } 79 | let songs = tracks => SpotifySong.init(from:) 80 | promise.success(with: songs) 81 | } 82 | return promise 83 | } 84 | 85 | func fetchSongs() -> Promise<[SpotifySong], APIError> { 86 | let promise = Promise<[SpotifySong], APIError>() 87 | guard let session = auth.session else { 88 | promise.error(with: .noData) 89 | return promise 90 | } 91 | SPTYourMusic.savedTracksForUser(withAccessToken: session.accessToken) { error, result in 92 | guard let result = result as? SPTListPage, error == nil else { 93 | promise.error(with: .unknown(error: error!)) 94 | return 95 | } 96 | self.handlePages(result).nested({ $0 => SpotifySong.init(from:) }).nest(to: promise, using: id) 97 | } 98 | return promise 99 | } 100 | 101 | func login(ifNeeded: Bool, viewController: UIViewController) { 102 | guard let session = auth.session, session.isValid() && ifNeeded else { 103 | let url = auth.spotifyWebAuthenticationURL() 104 | authViewController = SFSafariViewController(url: url!) 105 | guard let vc = authViewController else { 106 | return 107 | } 108 | viewController.present(vc, animated: true) 109 | return 110 | } 111 | } 112 | 113 | func callback(url: URL) -> Bool { 114 | auth.handleAuthCallback(withTriggeredAuthURL: url) { error, session in 115 | self.authViewController?.dismiss(animated: true) 116 | } 117 | return true 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/Spotify2AppleMusic-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Spotify2AppleMusic-Bridging-Header.h 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Mathias Quintero on 2/28/17. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Mathias Quintero on 2/28/17. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Mathias Quintero on 2/28/17. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Spotify2AppleMusic/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Spotify2AppleMusic 4 | // 5 | // Created by Finn Gaida on 28/02/2017. 6 | // Copyright © 2017 Finn Gaida. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Sweeft 11 | import MediaPlayer 12 | 13 | class ViewController: UIViewController { 14 | 15 | @IBOutlet weak var pickerView: UIPickerView! 16 | @IBOutlet weak var statusLabel: UILabel! 17 | @IBOutlet weak var loader: UIView! 18 | 19 | var selections: [ImportSelection] = [.all] 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | loader.frame = CGRect(x: 0, y: self.view.frame.height - 5, width: self.view.frame.width, height: 5) 24 | statusLabel.text = "" 25 | } 26 | 27 | override func viewDidAppear(_ animated: Bool) { 28 | super.viewDidAppear(animated) 29 | Spotify.shared.login(ifNeeded: true, viewController: self) 30 | Spotify.shared.playlists().onSuccess { selections in 31 | self.selections = [.all] + selections 32 | self.pickerView.reloadComponent(0) 33 | } 34 | } 35 | 36 | @IBAction func relogin(_ sender: Any) { 37 | Spotify.shared.login(ifNeeded: false, viewController: self) 38 | } 39 | 40 | func setProgress(p: CGFloat) { 41 | if p == 1.0 { 42 | statusLabel.text = "" 43 | } 44 | UIView.animate(withDuration: 0.5) { 45 | self.loader.frame = CGRect(x: 0, y: self.loader.frame.origin.y, width: self.view.frame.width * p, height: self.loader.frame.height) 46 | } 47 | } 48 | 49 | @IBAction func go() { 50 | statusLabel.text = "Fetching Songs from Spotify" 51 | self.setProgress(p: 0) 52 | let index = pickerView.selectedRow(inComponent: 0) 53 | selections[index].songs().onSuccess { songs -> Promise<[Song], APIError> in 54 | self.statusLabel.text = "Finding Counterparts in Itunes" 55 | return Itunes().search(for: songs) 56 | } 57 | .future 58 | .onSuccess(call: self.handle) 59 | } 60 | 61 | func handle(songs: [Song]) { 62 | statusLabel.text = "Found \(songs.count) songs" 63 | MPMediaLibrary.requestAuthorization { (status) in 64 | guard status == MPMediaLibraryAuthorizationStatus.authorized else { return print("not authorized") } 65 | let myPlaylistsQuery = MPMediaQuery.playlists() 66 | guard let playlists = myPlaylistsQuery.collections else { return } 67 | 68 | func continueWith(index: Int) { 69 | .main >>> { 70 | self.statusLabel.text = "Adding Songs to your Library" 71 | } 72 | if let plist = playlists[index] as? MPMediaPlaylist { 73 | var added = 0 74 | songs => { song, index in 75 | .userInitiated >>> { 76 | plist.addItem(withProductID: song.id, completionHandler: { (error) in 77 | .main >>> { 78 | added += 1 79 | if let error = error { 80 | self.statusLabel.text = "Failed to add \(song.name) by \(song.artist): \(error.localizedDescription)" 81 | } else { 82 | self.statusLabel.text = "Added \(song.name) by \(song.artist)" 83 | } 84 | self.setProgress(p: CGFloat(added) / CGFloat(songs.count)) 85 | } 86 | }) 87 | } 88 | } 89 | } 90 | } 91 | 92 | 93 | let action = UIAlertController(title: "Choose your playlist", message: "add \(songs.count) tracks", preferredStyle: .actionSheet) 94 | for (i, playlist) in playlists.enumerated() { 95 | let name = playlist.value(forProperty: MPMediaPlaylistPropertyName) ?? "no name" 96 | 97 | action.addAction(UIAlertAction(title: "\(name)", style: .default, handler: { _ in 98 | continueWith(index: i) 99 | })) 100 | } 101 | action.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) 102 | self.present(action, animated: true, completion: nil) 103 | } 104 | } 105 | 106 | } 107 | 108 | extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource { 109 | 110 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 111 | return 1 112 | } 113 | 114 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 115 | return selections.count 116 | } 117 | 118 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 119 | return selections[row].description 120 | } 121 | 122 | } 123 | 124 | --------------------------------------------------------------------------------