diff --git a/.gitignore b/.gitignore index 3b31d9ccb..400885609 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ docs/ Frameworks Examples/authentication-backend-example/bin Examples/authentication-backend-example/node_modules +Credentials_full.plist diff --git a/.travis.yml b/.travis.yml index 42cb87a0c..49eaba29e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,19 +5,28 @@ osx_image: xcode7.2 #xcode_scheme: WatsonDeveloperCloud #xcode_sdk: iphonesimulator9.2 +# whitelist +branches: + only: + - develop + #xcode_scheme: WatsonCore before_install: - brew update - brew install carthage - +- openssl aes-256-cbc -K $encrypted_1ae11c268751_key -iv $encrypted_1ae11c268751_iv -in WatsonDeveloperCloudTests/Credentials_full.plist.enc -out WatsonDeveloperCloudTests/Credentials.plist -d before_script: - carthage update --platform iOS # after_success: # - bash <(curl -s https://codecov.io/bash) +after_success: +- bash <(curl -s https://codecov.io/bash) + #script: # - xcodebuild clean build -scheme WatsonDeveloperCloud -enableCodeCoverage YES -destination 'platform=iOS Simulator,name=iPhone 6,OS=9.0' GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES script: - - xcodebuild clean build -scheme WatsonDeveloperCloud -destination 'platform=iOS Simulator,name=iPhone 6,OS=9.1' + - xcodebuild clean build test -scheme WatsonDeveloperCloud -destination 'platform=iOS Simulator,name=iPhone 6,OS=9.1' -enableCodeCoverage YES +# - xctool -scheme WatsonDeveloperCloud build test -sdk iphonesimulator GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00910e766..9adb995bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ We don't use GitHub as a support forum. For any usage questions that are not spe # Reporting Issues -If you encounter a bug with the iOS Watson SDK, please submit a detailed [issue](https://github.com/IBM-MIL/Watson-iOS-SDK/issues) so that it can be addressed quickly. We always appreciate a well-written, thorough bug report. +If you encounter a bug with the Watson Developer Cloud iOS SDK, please submit a detailed [issue](https://github.com/IBM-MIL/Watson-iOS-SDK/issues) so that it can be addressed quickly. We always appreciate a well-written, thorough bug report. Please check that the project issues database doesn't already include that problem or suggestion before submitting an issue. If you find a match, add a quick "+1" or "I have this problem too." Doing so helps prioritize the most common problems and requests. diff --git a/Quickstart.md b/Quickstart.md index 38a0407a9..05c58781c 100644 --- a/Quickstart.md +++ b/Quickstart.md @@ -6,7 +6,7 @@ This is a quick walkthrough to demonstrate how to create an iOS app that uses Wa Before beginning to create the iOS application, make sure you set up a BlueMix application and create a Text To Speech service. First, sign up for a Bluemix account. Next, create a new Bluemix application, it can either be Node JS or Liberty application (it does not matter in this example since we will not be deploying any server-side code). Next, setup an instance of the Watson Text to Speech service for that application. When a service gets bound to a Bluemix application, new credentials are automatically generated for making calls to the service. These credentials will be used as part of this getting started guide, and can be found once the service is started by clicking on the “Show Credentials” link on the service. For more information about creating Bluemix applications and attaching Bluemix and Watson services read [Bluemix getting started](https://developer.ibm.com/bluemix/#gettingstarted). -In addition, this quick guide uses Carthage to fetch the Watson iOS SDK and its dependencies. [Carthage](https://github.com/Carthage/Carthage) provides instruction for [Installing Carthage](https://github.com/Carthage/Carthage#installing-carthage). +In addition, this quick guide uses Carthage to fetch the Watson Developer Cloud iOS SDK and its dependencies. [Carthage](https://github.com/Carthage/Carthage) provides instruction for [Installing Carthage](https://github.com/Carthage/Carthage#installing-carthage). ###Create a Text to Speech App @@ -120,4 +120,4 @@ github "watson-developer-cloud/ios-sdk" You can review the different voices and languages [here](https://github.com/watson-developer-cloud/ios-sdk#text-to-speech). -You can download all the source code for the Watson iOS SDK [here](https://github.com/watson-developer-cloud/ios-sdk). +You can download all the source code for the Watson Developer Cloud iOS SDK [here](https://github.com/watson-developer-cloud/ios-sdk). diff --git a/README.md b/README.md index 8fb681c1e..d120d6f20 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,29 @@ -# Watson iOS SDK +# Watson Developer Cloud iOS SDK [![Build Status](https://travis-ci.org/watson-developer-cloud/ios-sdk.svg?branch=master)](https://travis-ci.org/watson-developer-cloud/ios-sdk) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![codecov.io](https://codecov.io/github/watson-developer-cloud/ios-sdk/coverage.svg?branch=develop)](https://codecov.io/github/watson-developer-cloud/ios-sdk?branch=develop) +[![Docs](https://img.shields.io/badge/Docs-0.1-green.svg?style=flat)](http://watson-developer-cloud.github.io/ios-sdk/) +[![Swift 2.1.1](https://img.shields.io/badge/Swift-2.1.1-orange.svg?style=flat)](https://developer.apple.com/swift/) -The Watson iOS SDK is a collection of services to allow developers to quickly add Watson Cognitive Computing services to their Swift iOS applications. +The Watson Developer Cloud iOS SDK is a collection of services to allow developers to quickly add Watson Cognitive Computing services to their Swift iOS applications. Visit our [Quickstart Guide](Quickstart.md) to build your first iOS app with Watson! -> *The Watson iOS SDK is currently in beta.* +> *The Watson Developer Cloud iOS SDK is currently in beta.* ## Table of Contents * [Installation](#installation) * [IBM Watson Services](#ibm-watson-services) - - [Alchemy Language](#alchemy-language) - - [Alchemy Vision](#alchemy-vision) - - [Dialog](#dialog) - - [Language Translation](#language-translation) - - [Natural Language Classifier](#natural-language-classifier) - - [Personality Insights](#personality-insights) - - [Speech to Text](#speech-to-text) - - [Text to Speech](#text-to-speech) + - [Alchemy Language](#alchemy-language) + - [Alchemy Vision](#alchemy-vision) + - [Dialog](#dialog) + - [Language Translation](#language-translation) + - [Natural Language Classifier](#natural-language-classifier) + - [Personality Insights](#personality-insights) + - [Speech to Text](#speech-to-text) + - [Text to Speech](#text-to-speech) * [Authentication](#authentication) * [Building and Testing](#build--test) * [Open Source @ IBM](#open-source--ibm) @@ -28,8 +31,10 @@ Visit our [Quickstart Guide](Quickstart.md) to build your first iOS app with Wat * [Contributing](#contributing) ## Installation - -The Watson iOS SDK requires third-party dependencies such as ObjectMapper and Alamofire. The dependency management tool Carthage is used to help manage those frameworks. There are two main methods to install Carthage. The first method is to download and run the Carthage.pkg installer. You can locate the latest release [here.](https://github.com/Carthage/Carthage/releases) + +The Watson Developer Cloud iOS SDK requires third-party dependencies such as ObjectMapper and Alamofire. The dependency management tool Carthage is used to help manage those frameworks. The recommended version of Carthage is v0.11 or higher. + +There are two main methods to install Carthage. The first method is to download and run the Carthage.pkg installer. You can locate the latest release [here.](https://github.com/Carthage/Carthage/releases) The second method of installing is using Homebrew for the download and installation of carthage with the following commands @@ -37,7 +42,7 @@ The second method of installing is using Homebrew for the download and installat brew update && brew install carthage ``` -Once the dependency manager is installed, the next step is to download the needed frameworks for the SDK to the project path. Make sure you are in the root of the project directory and run the following command. +Once the dependency manager is installed, the next step is to download the needed frameworks for the SDK to the project path. Make sure you are in the root of the project directory and run the following command. All of the frameworks can be found on the filesystem directory at location ./Carthage/Build/iOS/ ``` shell carthage update --platform iOS @@ -48,11 +53,11 @@ For more details on using the iOS SDK in your application, please review the [Qu **Frameworks Used:** * [Alamofire](https://github.com/Alamofire/Alamofire) -* [ObjectMapper](https://github.com/Hearst-DD/ObjectMapper) +* [ObjectMapper](https://github.com/Hearst-DD/ObjectMapper) * [AlamofireObjectMapper](https://github.com/tristanhimmelman/AlamofireObjectMapper/releases) * [Starscream](https://github.com/daltoniam/Starscream) -* [HTTPStatusCodes](https://github.com/rhodgkins/SwiftHTTPStatusCodes) -* [XCGLogger](https://github.com/DaveWoodCom/XCGLogger) +* [HTTPStatusCodes](https://github.com/rhodgkins/SwiftHTTPStatusCodes) +* [XCGLogger](https://github.com/DaveWoodCom/XCGLogger) @@ -60,7 +65,7 @@ For more details on using the iOS SDK in your application, please review the [Qu **Getting started with Watson Developer Cloud and Bluemix** -The IBM Watson™ Developer Cloud (WDC) offers a variety of services for developing cognitive applications. Each Watson service provides a Representational State Transfer (REST) Application Programming Interface (API) for interacting with the service. Some services, such as the Speech to Text service, provide additional interfaces. +The IBM Watson™ Developer Cloud (WDC) offers a variety of services for developing cognitive applications. Each Watson service provides a Representational State Transfer (REST) Application Programming Interface (API) for interacting with the service. Some services, such as the Speech to Text service, provide additional interfaces. IBM Bluemix™ is the cloud platform in which you deploy applications that you develop with Watson Developer Cloud services. The Watson Developer Cloud documentation provides information for developing applications with Watson services in Bluemix. You can learn more about Bluemix from the following links: @@ -108,7 +113,7 @@ let alchemyLanguageInstance = AlchemyLanguage(tokenAuthenticationStrategy: token API calls are instance methods, and model class instances are returned as part of our callback. -e.g. +e.g. ```swift @@ -147,7 +152,7 @@ let alchemyVisionInstance = AlchemyVision(apiKey: String) API calls are instance methods, and model class instances are returned as part of our callback. -e.g. +e.g. ```swift serviceVision.recognizeFaces(VisionConstants.ImageFacesType.FILE, @@ -256,13 +261,13 @@ The following links provide more information about the Natural Language Classifi ### Personality Insights -The IBM Watson™ Personality Insights service provides an Application Programming Interface (API) that enables applications to derive insights from social media, enterprise data, or other digital communications. The service uses linguistic analytics to infer personality and social characteristics, including Big Five, Needs, and Values, from text. +The IBM Watson™ Personality Insights service provides an Application Programming Interface (API) that enables applications to derive insights from social media, enterprise data, or other digital communications. The service uses linguistic analytics to infer personality and social characteristics, including Big Five, Needs, and Values, from text. ```swift let service = PersonalityInsights(username: "yourusername", password: "yourpassword") service!.getProfile("Some text here") { profile, error in - + // code here } ``` @@ -279,7 +284,7 @@ The IBM Watson Speech to Text service uses speech recognition capabilities to co Create a SpeechToText service: -```swift +```swift let stt = SpeechToText(authStrategy: strategy) ``` @@ -288,10 +293,10 @@ You can create an AVAudioRecorder with the necessary settings: ```swift let filePath = NSURL(fileURLWithPath: "\(NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0])/SpeechToTextRecording.wav") - + let session = AVAudioSession.sharedInstance() var settings = [String: AnyObject]() - + settings[AVSampleRateKey] = NSNumber(float: 44100.0) settings[AVNumberOfChannelsKey] = NSNumber(int: 1) do { @@ -306,18 +311,18 @@ To make a call for transcription, use: ```swift let data = NSData(contentsOfURL: recorder.url) - + if let data = data { - + sttService.transcribe(data , format: .WAV, oncompletion: { - + response, error in - + // code here } } ``` - + The following links provide additional information about the IBM Speech to Text service: @@ -340,10 +345,10 @@ service.setUsernameAndPassword(username: "yourname", password: "yourpass") To call the service to synthesize text: -```swift +```swift service.synthesize("Hello World", oncompletion: { - data, error in - + data, error in + if let data = data { // code here } @@ -366,14 +371,14 @@ The Watson TTS service contains support for many voices with different genders, service.listVoices({ voices, error in // code here - + }) ``` The following voices can be used: Voice | Language | Gender ------------- | ----------- | --------------- +------------ | ----------- | --------------- de-DE_BirgitVoice | German | Female de-DE_DieterVoice | German | Male en-GB_KateVoice | English (British) | Female @@ -389,8 +394,8 @@ To use the voice, such as Kate's, specify the voice identifier in the synthesize ```swift service.synthesize("Hello World", voice: "en-GB_KateVoice", "oncompletion: { - data, error in - + data, error in + if let data = data { // code here } @@ -407,7 +412,7 @@ The following links provide more information about the Text To Speech service: IBM Watson Services are hosted in the Bluemix platform. Before you can use each service in the SDK, the service must first be created in Bluemix, bound to an Application, and you must have the credentials that Bluemix generates for that service. Alchemy services use a single API key, and all the other Watson services use a username and password credential. For the services that have username and password credentials, a web service is used to grant a temporary Watson token to the client that can be used for subsequent calls. -It is not advisable in a full production app to embed the username and passwords in your application, since the application could be decompiled to extract those credentials. Instead, these credentials should remain on a deployed server, and should handle fetching the Watson token on behalf of the mobile application. Since there could be many strategies one could take to authenticate with Bluemix, we abstract the mechanism with a collection of classes that use the protocol *AuthenticationStrategy*. +It is not advisable in a full production app to embed the username and passwords in your application, since the application could be decompiled to extract those credentials. Instead, these credentials should remain on a deployed server, and should handle fetching the Watson token on behalf of the mobile application. Since there could be many strategies one could take to authenticate with Bluemix, we abstract the mechanism with a collection of classes that use the protocol *AuthenticationStrategy*. To quickly get started with the SDK, you can use a *BasicAuthenticationStrategy* when you create a service. You can specify the username and password, and it automatically handles fetching a temporary key from the token server. If the token expires, the strategy will fetch a new one. @@ -456,5 +461,3 @@ See [CONTRIBUTING](CONTRIBUTING.md) on how to help out. [sentiment_analysis]: http://www.alchemyapi.com/products/alchemylanguage/sentiment-analysis [alchemy_vision]: http://www.alchemyapi.com/products/alchemyvision [alchemy_data_news]: http://www.alchemyapi.com/products/alchemydata-news - - diff --git a/WatsonDeveloperCloud.xcodeproj/project.pbxproj b/WatsonDeveloperCloud.xcodeproj/project.pbxproj index 1e8311b8c..fe167605c 100644 --- a/WatsonDeveloperCloud.xcodeproj/project.pbxproj +++ b/WatsonDeveloperCloud.xcodeproj/project.pbxproj @@ -124,7 +124,7 @@ 7A0F97101C232B96004C01C0 /* WatsonError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F967C1C232B96004C01C0 /* WatsonError.swift */; }; 7A0F97111C232B96004C01C0 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F967D1C232B96004C01C0 /* Validation.swift */; }; 7A0F97121C232B96004C01C0 /* WatsonCoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F967E1C232B96004C01C0 /* WatsonCoreConstants.swift */; }; - 7A0F97131C232B96004C01C0 /* WatsonManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F967F1C232B96004C01C0 /* WatsonManager.swift */; }; + 7A0F97131C232B96004C01C0 /* WatsonGateway.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F967F1C232B96004C01C0 /* WatsonGateway.swift */; }; 7A0F97141C232B96004C01C0 /* WatsonRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F96801C232B96004C01C0 /* WatsonRequest.swift */; }; 7A0F97151C232B96004C01C0 /* WatsonService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F96811C232B96004C01C0 /* WatsonService.swift */; }; 7A0F97161C232B9C004C01C0 /* AlchemyLanguageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F95F01C232B96004C01C0 /* AlchemyLanguageTests.swift */; }; @@ -344,7 +344,7 @@ 7A0F967C1C232B96004C01C0 /* WatsonError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatsonError.swift; sourceTree = ""; }; 7A0F967D1C232B96004C01C0 /* Validation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validation.swift; sourceTree = ""; }; 7A0F967E1C232B96004C01C0 /* WatsonCoreConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatsonCoreConstants.swift; sourceTree = ""; }; - 7A0F967F1C232B96004C01C0 /* WatsonManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatsonManager.swift; sourceTree = ""; }; + 7A0F967F1C232B96004C01C0 /* WatsonGateway.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatsonGateway.swift; sourceTree = ""; }; 7A0F96801C232B96004C01C0 /* WatsonRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatsonRequest.swift; sourceTree = ""; }; 7A0F96811C232B96004C01C0 /* WatsonService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatsonService.swift; sourceTree = ""; }; 7A0F97311C232D02004C01C0 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; @@ -789,7 +789,7 @@ 7A0F96751C232B96004C01C0 /* Utilities */, 7A0F967D1C232B96004C01C0 /* Validation.swift */, 7A0F967E1C232B96004C01C0 /* WatsonCoreConstants.swift */, - 7A0F967F1C232B96004C01C0 /* WatsonManager.swift */, + 7A0F967F1C232B96004C01C0 /* WatsonGateway.swift */, 7A0F96801C232B96004C01C0 /* WatsonRequest.swift */, 7A0F96811C232B96004C01C0 /* WatsonService.swift */, ); @@ -1043,7 +1043,7 @@ 7A0F97041C232B96004C01C0 /* NSError.swift in Sources */, 7A0F96DF1C232B96004C01C0 /* SpeechToTextResult.swift in Sources */, 7A0F96FF1C232B96004C01C0 /* APIKeyAuthenticationStrategy.swift in Sources */, - 7A0F97131C232B96004C01C0 /* WatsonManager.swift in Sources */, + 7A0F97131C232B96004C01C0 /* WatsonGateway.swift in Sources */, 7A0F96951C232B96004C01C0 /* Keywords.swift in Sources */, 7A0F96DD1C232B96004C01C0 /* SpeechToTextDelegate.swift in Sources */, 7A0F96A51C232B96004C01C0 /* AlchemyCombineDictionaryUtil.swift in Sources */, diff --git a/WatsonDeveloperCloud.xcodeproj/xcshareddata/xcschemes/WatsonDeveloperCloud.xcscheme b/WatsonDeveloperCloud.xcodeproj/xcshareddata/xcschemes/WatsonDeveloperCloud.xcscheme index bcaa61f81..d47f1a1f1 100644 --- a/WatsonDeveloperCloud.xcodeproj/xcshareddata/xcschemes/WatsonDeveloperCloud.xcscheme +++ b/WatsonDeveloperCloud.xcodeproj/xcshareddata/xcschemes/WatsonDeveloperCloud.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/WatsonDeveloperCloud.xcodeproj/xcshareddata/xcschemes/WatsonDeveloperCloudTests.xcscheme b/WatsonDeveloperCloud.xcodeproj/xcshareddata/xcschemes/WatsonDeveloperCloudTests.xcscheme new file mode 100644 index 000000000..7cfd10a57 --- /dev/null +++ b/WatsonDeveloperCloud.xcodeproj/xcshareddata/xcschemes/WatsonDeveloperCloudTests.xcscheme @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WatsonDeveloperCloud/AlchemyLanguage/AlchemyLanguage.swift b/WatsonDeveloperCloud/AlchemyLanguage/AlchemyLanguage.swift index b6c315848..c3f1cba2c 100644 --- a/WatsonDeveloperCloud/AlchemyLanguage/AlchemyLanguage.swift +++ b/WatsonDeveloperCloud/AlchemyLanguage/AlchemyLanguage.swift @@ -130,7 +130,7 @@ public extension AlchemyLanguage { url: String?, text: String?, entitiesParameters ep: GetEntitiesParameters = GetEntitiesParameters(), - completionHandler: (error: NSError, returnValue: Entities)->() ) { + completionHandler: (error: NSError, returnValue: Entities) -> Void) { let accessString = AlchemyLanguageConstants.GetEntities(fromRequestType: rt) let endpoint = getEndpoint(accessString) @@ -205,7 +205,7 @@ public extension AlchemyLanguage { text: String?, sentimentType: alcs.SentimentType = alcs.SentimentType.Normal, sentimentParameters sp: GetSentimentParameters = GetSentimentParameters(), - completionHandler: (error: NSError, returnValue: SentimentResponse)->() ) { + completionHandler: (error: NSError, returnValue: SentimentResponse) -> Void) { var accessString: String! @@ -293,7 +293,7 @@ public extension AlchemyLanguage { url: String?, text: String?, keywordsParameters kp: GetKeywordsParameters = GetKeywordsParameters(), - completionHandler: (error: NSError, returnValue: Keywords)->() ) { + completionHandler: (error: NSError, returnValue: Keywords) -> Void) { let accessString = AlchemyLanguageConstants.GetRankedKeywords(fromRequestType: rt) let endpoint = getEndpoint(accessString) @@ -368,7 +368,7 @@ public extension AlchemyLanguage { url: String?, text: String?, conceptsParameters pd: GetRankedConceptsParameters = GetRankedConceptsParameters(), - completionHandler: (error: NSError, returnValue: ConceptResponse)->() ) { + completionHandler: (error: NSError, returnValue: ConceptResponse) -> Void) { let accessString = AlchemyLanguageConstants.GetRankedConcepts(fromRequestType: rt) let endpoint = getEndpoint(accessString) @@ -449,7 +449,7 @@ public extension AlchemyLanguage { url: String?, text: String?, relationsParameters pd: GetRelationsParameters = GetRelationsParameters(), - completionHandler: (error: NSError, returnValue: SAORelations)->() ) { + completionHandler: (error: NSError, returnValue: SAORelations) -> Void) { let accessString = AlchemyLanguageConstants.GetRelations(fromRequestType: rt) let endpoint = getEndpoint(accessString) @@ -520,7 +520,7 @@ public extension AlchemyLanguage { url: String?, text: String?, taxonomyParameters pd: GetRankedTaxonomyParameters = GetRankedTaxonomyParameters(), - completionHandler: (error: NSError, returnValue: Taxonomies)->() ) { + completionHandler: (error: NSError, returnValue: Taxonomies) -> Void) { let accessString = AlchemyLanguageConstants.GetRankedTaxonomy(fromRequestType: rt) let endpoint = getEndpoint(accessString) @@ -576,7 +576,7 @@ public extension AlchemyLanguage { public func getAuthors(requestType rt: AlchemyLanguageConstants.RequestType, html: String?, url: String?, - completionHandler: (error: NSError, returnValue: DocumentAuthors)->() ) { + completionHandler: (error: NSError, returnValue: DocumentAuthors) -> Void) { var parameters = commonParameters @@ -644,7 +644,7 @@ public extension AlchemyLanguage { url: String?, text: String?, languageParameters pd: GetLanguageParameters = GetLanguageParameters(), - completionHandler: (error: NSError, returnValue: Language)->() ) { + completionHandler: (error: NSError, returnValue: Language) -> Void) { let accessString = AlchemyLanguageConstants.GetLanguage(fromRequestType: rt) let endpoint = getEndpoint(accessString) @@ -723,7 +723,7 @@ public extension AlchemyLanguage { url: String?, textType: alcs.TextType = alcs.TextType.Normal, textParameters pd: GetTextParameters = GetTextParameters(), - completionHandler: (error: NSError, text: DocumentText, title: DocumentTitle)->() ) { + completionHandler: (error: NSError, text: DocumentText, title: DocumentTitle) -> Void) { var accessString: String! @@ -794,7 +794,7 @@ public extension AlchemyLanguage { public func getMicroformatData(requestType rt: AlchemyLanguageConstants.RequestType, html: String?, url: String?, - completionHandler: (error: NSError, returnValue: Microformats)->() ) { + completionHandler: (error: NSError, returnValue: Microformats) -> Void) { let accessString = AlchemyLanguageConstants.GetMicroformatData(fromRequestType: rt) let endpoint = getEndpoint(accessString) @@ -849,7 +849,7 @@ public extension AlchemyLanguage { public func getFeedLinks(requestType rt: AlchemyLanguageConstants.RequestType, html: String?, url: String?, - completionHandler: (error: NSError, returnValue: Feeds)->() ) { + completionHandler: (error: NSError, returnValue: Feeds) -> Void) { let accessString = AlchemyLanguageConstants.GetFeedLinks(fromRequestType: rt) let endpoint = getEndpoint(accessString) diff --git a/WatsonDeveloperCloud/AlchemyVision/AlchemyVision.swift b/WatsonDeveloperCloud/AlchemyVision/AlchemyVision.swift index 9be89858b..eeb13acd1 100644 --- a/WatsonDeveloperCloud/AlchemyVision/AlchemyVision.swift +++ b/WatsonDeveloperCloud/AlchemyVision/AlchemyVision.swift @@ -53,7 +53,7 @@ public class AlchemyVision: AlchemyService { - parameter inputString: The string that contains the URL or the HTML text - parameter completionHandler: ImageLink object is returned in the completionHandler */ - public func getImageLink(inputType: VisionConstants.ImageLinkType, inputString: String, completionHandler: (ImageLink?, NSError?) ->() ) { + public func getImageLink(inputType: VisionConstants.ImageLinkType, inputString: String, completionHandler: (ImageLink?, NSError?) -> Void) { var endPoint = VisionConstants.ImageLinkExtraction.HTMLGetImage.rawValue var visionUrl = "" @@ -91,7 +91,7 @@ public class AlchemyVision: AlchemyService { - parameter knowledgeGraph: Possible values: 0 (default), 1 - parameter callback: Callback with ImageKeyWords through the completion handler */ - public func getImageKeywords(inputType: VisionConstants.ImageKeywordType, stringURL: String? = nil, image: UIImage? = nil, forceShowAll: Bool = false, knowledgeGraph: Int8 = 0, completionHandler: (ImageKeyWords?, NSError?) ->() ) { + public func getImageKeywords(inputType: VisionConstants.ImageKeywordType, stringURL: String? = nil, image: UIImage? = nil, forceShowAll: Bool = false, knowledgeGraph: Int8 = 0, completionHandler: (ImageKeyWords?, NSError?) -> Void) { var endPoint = VisionConstants.ImageTagging.URLGetRankedImageKeywords.rawValue var visionUrl = "" @@ -164,7 +164,7 @@ public class AlchemyVision: AlchemyService { - parameter knowledgeGraph: Possible values: 0 (default), 1 - parameter callback: Callback with ImageKeyWords through the completion handler */ - public func recognizeFaces(inputType: VisionConstants.ImageFacesType, stringURL: String? = nil, image: UIImage? = nil, forceShowAll: Bool = false, knowledgeGraph: Int8 = 0, completionHandler: (ImageFaceTags?, NSError?) ->() ) { + public func recognizeFaces(inputType: VisionConstants.ImageFacesType, stringURL: String? = nil, image: UIImage? = nil, forceShowAll: Bool = false, knowledgeGraph: Int8 = 0, completionHandler: (ImageFaceTags?, NSError?) -> Void) { var endPoint = VisionConstants.ImageLinkExtraction.HTMLGetImage.rawValue var visionUrl = "" diff --git a/WatsonDeveloperCloud/Dialog/Tests/DialogTests.swift b/WatsonDeveloperCloud/Dialog/Tests/DialogTests.swift index d5562c368..42ddfaee3 100644 --- a/WatsonDeveloperCloud/Dialog/Tests/DialogTests.swift +++ b/WatsonDeveloperCloud/Dialog/Tests/DialogTests.swift @@ -22,16 +22,16 @@ class DialogTests: XCTestCase { // MARK: - Parameters and constants // the Dialog service - var service: Dialog? + var service: Dialog! // the Dialog application that will be created for testing let dialogName = "pizza-watsonsdk-ios" - var dialogID: Dialog.DialogID? + var dialogID: Dialog.DialogID! // the Dialog file that will be used to create the application let dialogFileName = "pizza_sample" let dialogFileType = "xml" - var dialogFile: NSURL? = nil + var dialogFile: NSURL! = nil // the initial node and response from Watson let initialNode = "OUTPUT(200000)" @@ -45,8 +45,8 @@ class DialogTests: XCTestCase { let response2 = "What toppings are you in the mood for? (Limit 4)" // the conversation parameters - var conversationID: Int? - var clientID: Int? + var conversationID: Int! + var clientID: Int! let input = "I would like a medium, please." let response = "What toppings are you in the mood for? (Limit 4)" @@ -62,9 +62,6 @@ class DialogTests: XCTestCase { // timeout for asynchronous completion handlers private let timeout: NSTimeInterval = 30.0 - // the separator to use between sets of output - let separator = "----------" - // MARK: - Dialog Tests /// Test the Dialog service by executing each operation with valid parameters. @@ -79,6 +76,9 @@ class DialogTests: XCTestCase { // create the Dialog application createDialogApp() + // ensure Dialog application is created before sending queries + sleep(3) + // verify the content of the initial response node verifyInitialNode(initialResponse) @@ -225,7 +225,9 @@ class DialogTests: XCTestCase { } // instantiate the service - service = Dialog(username: username, password: password) + if service == nil { + service = Dialog(username: username, password: password) + } } override func tearDown() { @@ -248,7 +250,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // get existing Dialog applications - service!.getDialogs() { dialogs, error in + service.getDialogs() { dialogs, error in // verify expected response XCTAssertNotNil(dialogs) @@ -258,7 +260,7 @@ class DialogTests: XCTestCase { let conflicts = dialogs?.filter { $0.name == self.dialogName } if let conflicts = conflicts { conflicts.forEach() { dialog in - self.service!.deleteDialog(dialog.dialogID!) { error in + self.service.deleteDialog(dialog.dialogID!) { error in // verify expected response XCTAssertNil(error) @@ -295,7 +297,7 @@ class DialogTests: XCTestCase { } // create Dialog application - service!.createDialog(dialogName, fileURL: dialogFile) { dialogID, error in + service.createDialog(dialogName, fileURL: dialogFile) { dialogID, error in // verify expected response XCTAssertNotNil(dialogID) @@ -315,7 +317,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // retrieve all nodes - service!.getContent(dialogID!) { nodes, error in + service.getContent(dialogID) { nodes, error in // verify expected response XCTAssertNotNil(nodes) @@ -344,7 +346,7 @@ class DialogTests: XCTestCase { let nodes = [Dialog.Node(content: initialResponse, node: initialNode)] // update the initial node - service!.updateContent(dialogID!, nodes: nodes) { error in + service.updateContent(dialogID, nodes: nodes) { error in // verify expected response XCTAssertNil(error) @@ -360,7 +362,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // list the Dialog applications associated with the service instance - service!.getDialogs() { dialogs, error in + service.getDialogs() { dialogs, error in // verify expected response XCTAssertNotNil(dialogs) @@ -384,7 +386,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // download the Dialog file - service!.getDialogFile(dialogID!, format: format) { file, error in + service.getDialogFile(dialogID, format: format) { file, error in // verify expected response XCTAssertNotNil(file) @@ -409,7 +411,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // upload the Dialog file - service!.updateDialog(dialogID!, fileURL: dialogFile!, fileType: .WDSXML) { + service.updateDialog(dialogID, fileURL: dialogFile, fileType: .WDSXML) { error in // verify expected response @@ -426,7 +428,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // start a new conversation - service!.converse(dialogID!) { response, error in + service.converse(dialogID) { response, error in // verify expected response XCTAssertNotNil(response) @@ -450,8 +452,8 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // continue conversation - service!.converse(dialogID!, conversationID: conversationID!, - clientID: clientID!, input: input) { response, error in + service.converse(dialogID, conversationID: conversationID, + clientID: clientID, input: input) { response, error in // verify expected response XCTAssertNotNil(response) @@ -473,7 +475,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // set profile variables - service!.updateProfile(dialogID!, clientID: clientID!, parameters: parameters) { + service.updateProfile(dialogID, clientID: clientID, parameters: parameters) { error in // verify expected response @@ -490,7 +492,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // get all profile parameters - service!.getProfile(dialogID!, clientID: clientID!) { parameters, error in + service.getProfile(dialogID, clientID: clientID) { parameters, error in // verify expected response XCTAssertNotNil(parameters) @@ -515,7 +517,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // get first profile parameter - service!.getProfile(dialogID!, clientID: clientID!, names: [parameterName]) { + service.getProfile(dialogID, clientID: clientID, names: [parameterName]) { parameters, error in // verify expected response @@ -547,7 +549,7 @@ class DialogTests: XCTestCase { } // delete Dialog application - service!.deleteDialog(dialogID) { error in + service.deleteDialog(dialogID) { error in // verify expected response XCTAssertNil(error) @@ -565,7 +567,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // get content - service!.getContent(invalidDialogID) { nodes, error in + service.getContent(invalidDialogID) { nodes, error in // verify expected response XCTAssertNil(nodes) @@ -588,7 +590,7 @@ class DialogTests: XCTestCase { let nodes = [Dialog.Node(content: initialResponse, node: initialNode)] // update the initial node - service!.updateContent(invalidDialogID, nodes: nodes) { error in + service.updateContent(invalidDialogID, nodes: nodes) { error in // verify expected response XCTAssertNotNil(error) @@ -610,7 +612,7 @@ class DialogTests: XCTestCase { let nodes = [Dialog.Node(content: initialNode, node: initialResponse)] // update the node - service!.updateContent(dialogID!, nodes: nodes) { error in + service.updateContent(dialogID, nodes: nodes) { error in // verify expected response XCTAssertNotNil(error) @@ -635,7 +637,7 @@ class DialogTests: XCTestCase { } // create Dialog application - service!.createDialog(longDialogName, fileURL: dialogFile!) { dialogID, error in + service.createDialog(longDialogName, fileURL: dialogFile) { dialogID, error in // verify expected response XCTAssertNil(dialogID) @@ -662,7 +664,7 @@ class DialogTests: XCTestCase { let fileURL = directoryURL.URLByAppendingPathComponent(pathComponent) // create Dialog application - service!.createDialog(dialogName, fileURL: fileURL) { dialogID, error in + service.createDialog(dialogName, fileURL: fileURL) { dialogID, error in // verify expected response XCTAssertNil(dialogID) @@ -699,7 +701,7 @@ class DialogTests: XCTestCase { } // create Dialog application - service!.createDialog(dialogName, fileURL: fileURL) { dialogID, error in + service.createDialog(dialogName, fileURL: fileURL) { dialogID, error in // verify expected response XCTAssertNil(dialogID) @@ -726,7 +728,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // delete Dialog application - service!.deleteDialog(invalidDialogID) { error in + service.deleteDialog(invalidDialogID) { error in // verify expected response XCTAssertNotNil(error) @@ -745,7 +747,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // download the Dialog file - service!.getDialogFile(invalidDialogID) { file, error in + service.getDialogFile(invalidDialogID) { file, error in // verify expected response XCTAssertNotNil(file) @@ -773,7 +775,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // upload the Dialog file - service!.updateDialog(invalidDialogID, fileURL: dialogFile!, fileType: .WDSXML) { + service.updateDialog(invalidDialogID, fileURL: dialogFile, fileType: .WDSXML) { error in // verify expected response @@ -800,7 +802,7 @@ class DialogTests: XCTestCase { let fileURL = directoryURL.URLByAppendingPathComponent(pathComponent) // upload the Dialog file - service!.updateDialog(dialogID!, fileURL: fileURL, fileType: .WDSJSON) { error in + service.updateDialog(dialogID, fileURL: fileURL, fileType: .WDSJSON) { error in // verify expected response XCTAssertNotNil(error) @@ -836,7 +838,7 @@ class DialogTests: XCTestCase { } // upload the Dialog file - service!.updateDialog(dialogID!, fileURL: fileURL, fileType: .WDSJSON) { error in + service.updateDialog(dialogID, fileURL: fileURL, fileType: .WDSJSON) { error in // verify expected response XCTAssertNotNil(error) @@ -864,7 +866,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // start a new conversation - service!.converse(invalidDialogID) { response, error in + service.converse(invalidDialogID) { response, error in // verify expected response XCTAssertNil(response) @@ -884,7 +886,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // start a new conversation - service!.converse(dialogID!, conversationID: invalidConversationID, + service.converse(dialogID, conversationID: invalidConversationID, clientID: invalidClientID) { response, error in // verify expected response @@ -905,7 +907,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // start a new conversation - service!.converse(dialogID!, input: input) { response, error in + service.converse(dialogID, input: input) { response, error in // verify expected response XCTAssertNotNil(response) @@ -925,7 +927,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // get all profile parameters - service!.getProfile(invalidDialogID, clientID: clientID!) { parameters, error in + service.getProfile(invalidDialogID, clientID: clientID) { parameters, error in // verify expected response XCTAssertNil(parameters) @@ -945,7 +947,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // get all profile parameters - service!.getProfile(dialogID!, clientID: invalidClientID) { parameters, error in + service.getProfile(dialogID, clientID: invalidClientID) { parameters, error in // verify expected response XCTAssertNotNil(parameters) @@ -965,7 +967,7 @@ class DialogTests: XCTestCase { let invalidNames = ["Hello", "World"] // get invalid profile parameters - service!.getProfile(dialogID!, clientID: clientID!, names: invalidNames) { + service.getProfile(dialogID, clientID: clientID, names: invalidNames) { parameters, error in // verify expected response @@ -986,7 +988,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // set profile parameters - service!.updateProfile(invalidDialogID, clientID: clientID!, + service.updateProfile(invalidDialogID, clientID: clientID, parameters: parameters) { error in // verify expected response @@ -1006,7 +1008,7 @@ class DialogTests: XCTestCase { let expectation = expectationWithDescription(description) // set profile parameters - service!.updateProfile(dialogID!, clientID: invalidClientID, + service.updateProfile(dialogID, clientID: invalidClientID, parameters: parameters) { error in // verify expected response @@ -1026,7 +1028,7 @@ class DialogTests: XCTestCase { let invalidParameters = ["Hello": "World"] // update invalid profile parameters - service!.updateProfile(dialogID!, clientID: clientID!, + service.updateProfile(dialogID, clientID: clientID, parameters: invalidParameters) { error in // verify expected response diff --git a/WatsonDeveloperCloud/LanguageTranslation/LanguageTranslation.swift b/WatsonDeveloperCloud/LanguageTranslation/LanguageTranslation.swift index 27b55fe7d..b94d200c2 100644 --- a/WatsonDeveloperCloud/LanguageTranslation/LanguageTranslation.swift +++ b/WatsonDeveloperCloud/LanguageTranslation/LanguageTranslation.swift @@ -45,7 +45,7 @@ public class LanguageTranslation: WatsonService { identifiable languages */ public func getIdentifiableLanguages( - completionHandler: ([IdentifiableLanguage]?, NSError?) -> ()) { + completionHandler: ([IdentifiableLanguage]?, NSError?) -> Void) { // construct request let request = WatsonRequest( @@ -71,7 +71,7 @@ public class LanguageTranslation: WatsonService { confidence */ public func identify(text: String, - completionHandler: ([IdentifiedLanguage]?, NSError?) -> ()) { + completionHandler: ([IdentifiedLanguage]?, NSError?) -> Void) { // construct request let request = WatsonRequest( @@ -99,7 +99,7 @@ public class LanguageTranslation: WatsonService { translated strings */ public func translate(text: [String], source: String, target: String, - completionHandler: ([String]?, NSError?) -> ()) { + completionHandler: ([String]?, NSError?) -> Void) { translate(TranslateRequest(text: text, source: source, target: target), completionHandler: completionHandler) @@ -115,7 +115,7 @@ public class LanguageTranslation: WatsonService { translated strings */ public func translate(text: [String], modelID: String, - completionHandler: ([String]?, NSError?) -> ()) { + completionHandler: ([String]?, NSError?) -> Void) { translate(TranslateRequest(text: text, modelID: modelID), completionHandler: completionHandler) @@ -129,7 +129,7 @@ public class LanguageTranslation: WatsonService { translated strings. */ private func translate(translateRequest: TranslateRequest, - completionHandler: ([String]?, NSError?) -> ()) { + completionHandler: ([String]?, NSError?) -> Void) { // construct request let request = WatsonRequest( @@ -157,7 +157,7 @@ public class LanguageTranslation: WatsonService { - parameter callback: The callback method to invoke after the response is received */ public func getModels(source: String? = nil, target: String? = nil, - defaultModel: Bool? = nil, completionHandler: ([TranslationModel]?, NSError?) -> ()) { + defaultModel: Bool? = nil, completionHandler: ([TranslationModel]?, NSError?) -> Void) { // construct url query parameters var urlParams = [NSURLQueryItem]() @@ -194,7 +194,7 @@ public class LanguageTranslation: WatsonService { - parameter completionHandler: The callback method to invoke after the response is received */ public func getModel(modelID: String, - completionHandler: (TranslationModel?, NSError?) -> ()) { + completionHandler: (TranslationModel?, NSError?) -> Void) { // construct request let request = WatsonRequest( @@ -219,7 +219,7 @@ public class LanguageTranslation: WatsonService { - parameter forcedGlossaryPath: (Required). A TMX file with your customizations. Anything specified in this file will completely overwrite the domain data translation. - parameter callback: Returns the created model */ - public func createModel(baseModelID: String, name: String? = nil, fileKey: String, fileURL: NSURL, completionHandler: (String?, NSError?) -> ()) { + public func createModel(baseModelID: String, name: String? = nil, fileKey: String, fileURL: NSURL, completionHandler: (String?, NSError?) -> Void) { // force token to refresh // TODO: can remove this after its handled by WatsonGateway @@ -287,7 +287,7 @@ public class LanguageTranslation: WatsonService { - parameter modelID: The model identifier - parameter completionHandler: The callback method to invoke after the response is received, returns true if delete is successful */ - public func deleteModel(modelID: String, completionHandler: NSError? -> ()) { + public func deleteModel(modelID: String, completionHandler: NSError? -> Void) { // construct request let request = WatsonRequest( diff --git a/WatsonDeveloperCloud/LanguageTranslation/Tests/LanguageTranslationTests.swift b/WatsonDeveloperCloud/LanguageTranslation/Tests/LanguageTranslationTests.swift index 97732fd99..687649ec7 100644 --- a/WatsonDeveloperCloud/LanguageTranslation/Tests/LanguageTranslationTests.swift +++ b/WatsonDeveloperCloud/LanguageTranslation/Tests/LanguageTranslationTests.swift @@ -19,7 +19,7 @@ import XCTest class LanguageTranslationTests: XCTestCase { /// Language translation service - private var service: LanguageTranslation? + private var service: LanguageTranslation! /// Timeout for an asynchronous call to return before failing the unit test private let timeout: NSTimeInterval = 60.0 @@ -30,7 +30,9 @@ class LanguageTranslationTests: XCTestCase { if let dict = NSDictionary(contentsOfFile: url) as? Dictionary { let username = dict["LanguageTranslationUsername"]! let password = dict["LanguageTranslationPassword"]! - service = LanguageTranslation(username: username, password: password) + if service == nil { + service = LanguageTranslation(username: username, password: password) + } } else { XCTFail("Unable to extract dictionary from plist") } @@ -47,7 +49,7 @@ class LanguageTranslationTests: XCTestCase { func testIdentifiableLanguages() { let expectation = expectationWithDescription("Identifiable Languages") - service!.getIdentifiableLanguages({(languages:[LanguageTranslation.IdentifiableLanguage]?, error) in + service.getIdentifiableLanguages({(languages:[LanguageTranslation.IdentifiableLanguage]?, error) in XCTAssertNotNil(languages,"Expected non-nil array of identifiable languages to be returned") XCTAssertGreaterThan(languages!.count,0,"Expected at least 1 identifiable language to be returned") expectation.fulfill() @@ -60,12 +62,12 @@ class LanguageTranslationTests: XCTestCase { let emptyExpectation = expectationWithDescription("Empty") let validExpectation = expectationWithDescription("Valid") - service!.identify("") { languages, error in + service.identify("") { languages, error in XCTAssertNil(languages, "Expected empty result when passing in an empty string to identify()") emptyExpectation.fulfill() } - service!.identify("hola") { languages, error in + service.identify("hola") { languages, error in if let firstLanguage = languages?.first { XCTAssertEqual(firstLanguage.language,"es", "Expected 'hola' to be identified as 'es' language") } @@ -82,7 +84,7 @@ class LanguageTranslationTests: XCTestCase { let sourceTargetExpectation = expectationWithDescription("Source and Target Translation") let modelExpectation = expectationWithDescription("Model Translation") - service!.translate(["Hello","House"],source:"en",target:"es") { text, error in + service.translate(["Hello","House"],source:"en",target:"es") { text, error in XCTAssertNotNil(text, "Expected non-empty array to be returned") XCTAssertEqual(text!.first!,"Hola","Expected hello to translate to hola") @@ -90,7 +92,7 @@ class LanguageTranslationTests: XCTestCase { sourceTargetExpectation.fulfill() } - service!.translate(["Hello"],modelID:"en-es") { text, error in + service.translate(["Hello"],modelID:"en-es") { text, error in XCTAssertNotNil(text, "Expected non-empty array to be returned") XCTAssertEqual(text!.first!,"Hola","Expected hello to translate to Hola") @@ -106,23 +108,23 @@ class LanguageTranslationTests: XCTestCase { let targetExpectation = expectationWithDescription("Get Models by Target") let defaultModelExpectation = expectationWithDescription("Get Models by Default") - service!.getModels() { models, error in + service.getModels() { models, error in XCTAssertGreaterThan(models!.count,0,"Expected at least 1 model to be returned") allExpectation.fulfill() } - service!.getModels("es") { models, error in + service.getModels("es") { models, error in XCTAssertEqual(models!.count,3,"Expected exactly 3 models to be returned") sourceExpectation.fulfill() } - service!.getModels(target:"pt") { models, error in + service.getModels(target:"pt") { models, error in XCTAssertNotNil(models, "Expected models to be returned") XCTAssertEqual(models!.count,2,"Expected exactly 2 models to be returned") targetExpectation.fulfill() } - service!.getModels(defaultModel:true) { models, error in + service.getModels(defaultModel:true) { models, error in XCTAssertNotNil(models, "Expected models to be returned") XCTAssertGreaterThanOrEqual(models!.count, 8, "Expected at least 8 models to be returned") defaultModelExpectation.fulfill() @@ -141,13 +143,17 @@ class LanguageTranslationTests: XCTestCase { print("URL: " + fileURL!.URLString) XCTAssertNotNil(fileURL) - service!.createModel("en-es", name: "custom-english-to-spanish", fileKey: "forced_glossary", fileURL: fileURL!) { model, error in + service.createModel("en-es", name: "custom-english-to-spanish", fileKey: "forced_glossary", fileURL: fileURL!) { model, error in XCTAssertNotNil(model, "Expected non-nil model to be returned.") Log.sharedLogger.error("\(error)") creationExpectation.fulfill() - self.service!.deleteModel(model!) { error in + // Add a small delay so the model is ready for delete. This is not a normal flow of create and delete immediately + // so this is only a testing issue + sleep(3) + + self.service.deleteModel(model!) { error in XCTAssertNil(error) deletionExpectation.fulfill() } @@ -160,12 +166,12 @@ class LanguageTranslationTests: XCTestCase { let unauthorizedDeleteExpectation = expectationWithDescription("Unauthorized expectation") let missingDeleteExpectation = expectationWithDescription("Missing delete expectation") - service!.deleteModel("en-es") { error in + service.deleteModel("en-es") { error in XCTAssertNotNil(error, "Expected unauthorized exception when trying to delete an IBM model") unauthorizedDeleteExpectation.fulfill() } - service!.deleteModel("qwerty") { error in + service.deleteModel("qwerty") { error in XCTAssertNotNil(error!,"Expected missing delete exception when trying to delete a nonexistent model") missingDeleteExpectation.fulfill() } @@ -177,12 +183,12 @@ class LanguageTranslationTests: XCTestCase { let expectation1 = expectationWithDescription("Missing model") let expectation2 = expectationWithDescription("Valid model") - service!.getModel("MISSING_MODEL_ID") { model, error in + service.getModel("MISSING_MODEL_ID") { model, error in XCTAssertNil(model,"Expected no model to be return for invalid id") expectation1.fulfill() } - service!.getModel("en-es") { model, error in + service.getModel("en-es") { model, error in guard let model = model else { XCTFail("Expected non-nil model to be returned") return diff --git a/WatsonDeveloperCloud/NaturalLanguageClassifier/NaturalLanguageClassifier.swift b/WatsonDeveloperCloud/NaturalLanguageClassifier/NaturalLanguageClassifier.swift index 011b842b1..c78f77e99 100644 --- a/WatsonDeveloperCloud/NaturalLanguageClassifier/NaturalLanguageClassifier.swift +++ b/WatsonDeveloperCloud/NaturalLanguageClassifier/NaturalLanguageClassifier.swift @@ -43,7 +43,7 @@ public class NaturalLanguageClassifier: WatsonService { - parameter completionHandler: Callback with [Classifier]? */ - public func getClassifiers(completionHandler: ([Classifier]?, NSError?) -> ()) { + public func getClassifiers(completionHandler: ([Classifier]?, NSError?) -> Void) { // construct request let request = WatsonRequest( @@ -69,7 +69,7 @@ public class NaturalLanguageClassifier: WatsonService { - parameter completionHandler: Callback with Classification? */ public func classify(classifierId: String, text: String, - completionHandler: (Classification?, NSError?) -> ()) { + completionHandler: (Classification?, NSError?) -> Void) { // construct request let request = WatsonRequest( @@ -94,7 +94,7 @@ public class NaturalLanguageClassifier: WatsonService { - parameter completionHandler: Callback with Classifer? */ public func getClassifier(classifierId: String, - completionHandler: (Classifier?, NSError?) -> ()) { + completionHandler: (Classifier?, NSError?) -> Void) { // construct request let request = WatsonRequest( @@ -117,7 +117,7 @@ public class NaturalLanguageClassifier: WatsonService { - parameter classifierId: The classifer ID used to delete the classifier - parameter completionHandler: Bool return with true as success */ - public func deleteClassifier(classifierId: String, completionHandler: NSError? -> ()) { + public func deleteClassifier(classifierId: String, completionHandler: NSError? -> Void) { // construct request let request = WatsonRequest( @@ -144,7 +144,7 @@ public class NaturalLanguageClassifier: WatsonService { - parameter completionHandler: Callback with Classifier? */ public func createClassifier(trainerMetaURL: NSURL, trainerURL: NSURL, - completionHandler: (Classifier?, NSError?) -> ()) { + completionHandler: (Classifier?, NSError?) -> Void) { // force token to refresh // TODO: can remove this after its handled by WatsonGateway diff --git a/WatsonDeveloperCloud/NaturalLanguageClassifier/Tests/NaturalLanguageClassifierTests.swift b/WatsonDeveloperCloud/NaturalLanguageClassifier/Tests/NaturalLanguageClassifierTests.swift index 611f883d7..db08a80ca 100644 --- a/WatsonDeveloperCloud/NaturalLanguageClassifier/Tests/NaturalLanguageClassifierTests.swift +++ b/WatsonDeveloperCloud/NaturalLanguageClassifier/Tests/NaturalLanguageClassifierTests.swift @@ -57,18 +57,20 @@ class NaturalLanguageClassifierTests: XCTestCase { // read NLC username guard let username = credentials["NaturalLanguageClassifierUsername"] else { - XCTFail("Unable to read Dialog username.") + XCTFail("Unable to read NaturalLanguageClassifier username.") return } // read NLC password guard let password = credentials["NaturalLanguageClassifierPassword"] else { - XCTFail("Unable to read Dialog password.") + XCTFail("Unable to read NaturalLanguageClassifier password.") return } // instantiate the service - service = NaturalLanguageClassifier(username: username, password: password) + if service == nil { + service = NaturalLanguageClassifier(username: username, password: password) + } } override func tearDown() { diff --git a/WatsonDeveloperCloud/PersonalityInsights/PersonalityInsights.swift b/WatsonDeveloperCloud/PersonalityInsights/PersonalityInsights.swift index 7b2138d79..51b957d91 100644 --- a/WatsonDeveloperCloud/PersonalityInsights/PersonalityInsights.swift +++ b/WatsonDeveloperCloud/PersonalityInsights/PersonalityInsights.swift @@ -60,7 +60,7 @@ public class PersonalityInsights: WatsonService { acceptLanguage: String? = nil, contentLanguage: String? = nil, includeRaw: Bool? = nil, - completionHandler: (Profile?, NSError?) -> ()) { + completionHandler: (Profile?, NSError?) -> Void) { // construct url query parameters var urlParams = [NSURLQueryItem]() @@ -116,7 +116,7 @@ public class PersonalityInsights: WatsonService { acceptLanguage: String? = nil, contentLanguage: String? = nil, includeRaw: Bool? = nil, - completionHandler: (Profile?, NSError?) -> ()) { + completionHandler: (Profile?, NSError?) -> Void) { // construct url query parameters var urlParams = [NSURLQueryItem]() diff --git a/WatsonDeveloperCloud/PersonalityInsights/Tests/PersonalityInsightsTests.swift b/WatsonDeveloperCloud/PersonalityInsights/Tests/PersonalityInsightsTests.swift index 78765c58d..e3d225032 100644 --- a/WatsonDeveloperCloud/PersonalityInsights/Tests/PersonalityInsightsTests.swift +++ b/WatsonDeveloperCloud/PersonalityInsights/Tests/PersonalityInsightsTests.swift @@ -22,11 +22,11 @@ class PersonalityInsightsTests: XCTestCase { // MARK: - Parameters and Constants // the PersonalityInsights service - var service: PersonalityInsights? + var service: PersonalityInsights! // sample text - var mobyDickIntro: String? - var kennedySpeech: String? + var mobyDickIntro: String! + var kennedySpeech: String! // timeout for asynchronous completion handlers let timeout: NSTimeInterval = 30.0 @@ -82,8 +82,9 @@ class PersonalityInsightsTests: XCTestCase { } // instantiate the service - service = PersonalityInsights(username: user, password: password) - + if service == nil { + service = PersonalityInsights(username: user, password: password) + } } // Wait for an expectation to be fulfilled. @@ -101,7 +102,7 @@ class PersonalityInsightsTests: XCTestCase { let expectation = expectationWithDescription(description) // analyze speech - service!.getProfile(kennedySpeech!) { profile, error in + service.getProfile(kennedySpeech) { profile, error in // verify expected response XCTAssertNotNil(profile) @@ -131,13 +132,13 @@ class PersonalityInsightsTests: XCTestCase { contentType: MediaType.Plain.rawValue, charset: "UTF-8", language: "en-us", - content: kennedySpeech!, + content: kennedySpeech, parentID: "", reply: false, forward: false) // analyze duplicate content items - service!.getProfile([contentItem, contentItem]) { profile, error in + service.getProfile([contentItem, contentItem]) { profile, error in // verify expected response XCTAssertNotNil(profile) @@ -159,7 +160,7 @@ class PersonalityInsightsTests: XCTestCase { let description = "Try to analyze text that is too short (less than 100 words)." let expectation = expectationWithDescription(description) - service!.getProfile(mobyDickIntro!) { profile, error in + service.getProfile(mobyDickIntro) { profile, error in // verify expected response XCTAssertNil(profile) XCTAssertNotNil(error) diff --git a/WatsonDeveloperCloud/SpeechToText/SpeechToText.swift b/WatsonDeveloperCloud/SpeechToText/SpeechToText.swift index 08756e8dc..bff02358a 100644 --- a/WatsonDeveloperCloud/SpeechToText/SpeechToText.swift +++ b/WatsonDeveloperCloud/SpeechToText/SpeechToText.swift @@ -203,13 +203,13 @@ public class SpeechToText: WatsonService { */ public func transcribe(audioData: NSData, format: MediaType = .FLAC, - oncompletion: (SpeechToTextResponse?, NSError?) -> Void) { + completionHandler: (SpeechToTextResponse?, NSError?) -> Void) { watsonSocket.format = format watsonSocket.send(audioData) - self.callback = oncompletion + self.callback = completionHandler // connectWebsocket() // @@ -217,7 +217,7 @@ public class SpeechToText: WatsonService { // self.audioData = audioData // self.format = format // -// self.callback = oncompletion +// self.callback = completionHandler } diff --git a/WatsonDeveloperCloud/SpeechToText/Tests/SpeechToTextTests.swift b/WatsonDeveloperCloud/SpeechToText/Tests/SpeechToTextTests.swift index 7579e0e53..3d9d24c88 100644 --- a/WatsonDeveloperCloud/SpeechToText/Tests/SpeechToTextTests.swift +++ b/WatsonDeveloperCloud/SpeechToText/Tests/SpeechToTextTests.swift @@ -19,317 +19,185 @@ import XCTest class SpeechToTextTests: XCTestCase { - private var inputText: String? - private let timeout: NSTimeInterval = 30.0 - + var service: SpeechToText! var recorder: AVAudioRecorder! var recordingSession: AVAudioSession! - - private lazy var username: String = "" - private lazy var password: String = "" + let timeout: NSTimeInterval = 30.0 override func setUp() { super.setUp() - if let url = NSBundle(forClass: self.dynamicType).pathForResource("Credentials", ofType: "plist") { - if let dict = NSDictionary(contentsOfFile: url) as? Dictionary { - - username = dict["SpeechToTextUsername"]! - password = dict["SpeechToTextPassword"]! - - - } else { - XCTFail("Unable to extract dictionary from plist") - } - } else { - XCTFail("Plist file not found") + + let bundle = NSBundle(forClass: self.dynamicType) + guard let url = bundle.pathForResource("Credentials", ofType: "plist") else { + XCTFail("Unable to locate credentials file.") + return } - recordingSession = AVAudioSession.sharedInstance() + let dict = NSDictionary(contentsOfFile: url) + guard let credentials = dict as? Dictionary else { + XCTFail("Unable to read credentials file.") + return + } + + guard let username = credentials["SpeechToTextUsername"] else { + XCTFail("Unable to read SpeechToText username.") + return + } + + guard let password = credentials["SpeechToTextPassword"] else { + XCTFail("Unable to read SpeechToText password.") + return + } + + if service == nil { + service = SpeechToText(username: username, password: password) + } + recordingSession = AVAudioSession.sharedInstance() do { try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord) try recordingSession.setActive(true) - } catch { - + XCTFail("Unable to configure AVAudioSession.") } } override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testEncoding() { - - - let basicAuth = BasicAuthenticationStrategy( - tokenURL: "https://stream.watsonplatform.net/authorization/api/v1/token", - serviceURL: "https://stream.watsonplatform.net/speech-to-text/api", - username: username, - password: password) - - let service = SpeechToText(authStrategy: basicAuth) - - let url = NSBundle(forClass: self.dynamicType).URLForResource("test", withExtension: "raw") - if let url = url - { - if let data = NSData(contentsOfURL: url) - { - let encodedAudio = service.encodeOpus( data) - - XCTAssertLessThan(encodedAudio.length, data.length, - "Encoded audio must be smaller than the original.") - - } else { - XCTAssert(true, "Could not load test PCM file") - } - + let bundle = NSBundle(forClass: self.dynamicType) + guard let url = bundle.URLForResource("test", withExtension: "raw") else { + XCTFail("Unable to locate test.raw file.") + return } - + guard let data = NSData(contentsOfURL: url) else { + XCTFail("Unable to read text.raw file.") + return + } + + let encodedAudio = service.encodeOpus(data) + XCTAssertLessThan(encodedAudio.length, data.length, + "Encoded audio must be smaller than the original.") } + func testSimpleFLACTranscription() { + transcribe("SpeechSample", withExtension: "flac", format: .FLAC) + } - - // func testContinuousRecording() { - // - // let basicAuth = BasicAuthenticationStrategy( - // tokenURL: "https://stream.watsonplatform.net/authorization/api/v1/token", - // serviceURL: "https://stream.watsonplatform.net/speech-to-text/api", - // username: username, - // password: password) - - // let service = SpeechToText(authStrategy: basicAuth) - - // service.startListening() - - //} + func testSimpleWAVTranscription() { + transcribe("SpeechSample", withExtension: "wav", format: .WAV) + } -// func testRecording() { -// -// let recordExpectation = expectationWithDescription("Record") -// -// let recordSettings = [ -// // AVFormatIDKey: NSNumber(unsignedInt:kAudioFormatLinearPCM), -// AVNumberOfChannelsKey: 1, -// AVSampleRateKey : 16000.0 -// ] -// -// let dirsPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) -// let docsDir = dirsPaths[0] as String -// let soundFilePath = docsDir + "/test.wav" -// -// print("Saving recorded audio file in \(soundFilePath)") -// AVAudioSession.sharedInstance().requestRecordPermission({(granted: Bool) -> Void -// in -// if granted { -// let soundFileURL = NSURL(string: soundFilePath) -// -// if let soundFileURL = soundFileURL { -// -// do { -// -// self.recorder = try AVAudioRecorder(URL: soundFileURL, settings: recordSettings) -// -// self.recorder.meteringEnabled = true -// -// // self.recorder.prepareToRecord() -// self.recorder.recordForDuration(3.0) -// -// sleep(5) -// -// print(self.recorder.recording) -// -// self.recorder.stop() -// -// recordExpectation.fulfill() -// -// } catch { -// -// -// XCTAssertTrue(false, "Could not create audio recorder") -// } -// } -// -// } -// -// }) -// -// waitForExpectationsWithTimeout(timeout) { -// error in XCTAssertNil(error, "Timeout") -// } -// } + func testSimpleOGGTranscription() { + transcribe("SpeechSample", withExtension: "ogg", format: .OPUS) + } - - func testSimpleFLACTranscription() { - - let basicAuth = BasicAuthenticationStrategy( - tokenURL: "https://stream.watsonplatform.net/authorization/api/v1/token", - serviceURL: "https://stream.watsonplatform.net/speech-to-text/api", - username: username, - password: password) - - let service = SpeechToText(authStrategy: basicAuth) + func transcribe(filename: String, withExtension: String, format: MediaType) { + let expectation = expectationWithDescription("Testing transcribe.") - let expectation = expectationWithDescription("WebSockets") - let url = NSBundle(forClass: self.dynamicType).URLForResource("SpeechSample", withExtension: "flac") - - guard let audioData = NSData(contentsOfURL: url!) else { - XCTFail("Need to read file") + let bundle = NSBundle(forClass: self.dynamicType) + guard let url = bundle.URLForResource(filename, withExtension: withExtension) else { + XCTFail("Unable to locate SpeechSample.flac.") return } - - service.transcribe( audioData, format: .FLAC, oncompletion: { - response, error in - - if let response = response { - - if let results = response.results { - XCTAssertGreaterThan(results.count, 0, "Must return more than zero results") - - if let alternatives = results[0].alternatives { - - XCTAssertGreaterThan(alternatives.count, 0, "Must return more than zero results") - - - if let transcript = alternatives[0].transcript - { - XCTAssertEqual(transcript, "several tornadoes touch down as a line of severe thunderstorms swept through Colorado on Sunday ") - - } - - } - - expectation.fulfill() - - } else { - XCTFail("Could not get back results for the response") - } - - } else { - XCTFail("Could not get back SpeechToTextResponse structure") - } - - - }) - - waitForExpectationsWithTimeout(timeout) { - error in XCTAssertNil(error, "Timeout") - } - } - - func testSimpleWAVTranscription() { - - let basicAuth = BasicAuthenticationStrategy( - tokenURL: "https://stream.watsonplatform.net/authorization/api/v1/token", - serviceURL: "https://stream.watsonplatform.net/speech-to-text/api", - username: username, - password: password) - - let service = SpeechToText(authStrategy: basicAuth) - - let expectation = expectationWithDescription("WebSockets") - let url = NSBundle(forClass: self.dynamicType).URLForResource("SpeechSample", withExtension: "wav") - - guard let audioData = NSData(contentsOfURL: url!) else { - XCTFail("Need to read file") + guard let audioData = NSData(contentsOfURL: url) else { + XCTFail("Unable to read SpeechSample.flac.") return } - service.transcribe( audioData, format: .WAV, oncompletion: { - response, error in + service.transcribe(audioData, format: format) { response, error in + guard let response = response else { + XCTFail("Expected a non-nil response.") + return + } - if let response = response { - - if let results = response.results { - XCTAssertGreaterThan(results.count, 0, "Must return more than zero results") - - if let alternatives = results[0].alternatives { - - XCTAssertGreaterThan(alternatives.count, 0, "Must return more than zero results") - - - if let transcript = alternatives[0].transcript - { - XCTAssertEqual(transcript, "several tornadoes touch down as a line of severe thunderstorms swept through Colorado on Sunday ") - - } - - } - - expectation.fulfill() - - } else { - XCTFail("Could not get back results for the response") - } - - } else { - XCTFail("Could not get back SpeechToTextResponse structure") + guard let results = response.results else { + XCTFail("Expected a non-nil result.") + return } + XCTAssertGreaterThan(results.count, 0, "Must return more than zero results") - }) - - waitForExpectationsWithTimeout(timeout) { - error in XCTAssertNil(error, "Timeout") - } - } - - func testSimpleOGGTranscription() { - - let basicAuth = BasicAuthenticationStrategy( - tokenURL: "https://stream.watsonplatform.net/authorization/api/v1/token", - serviceURL: "https://stream.watsonplatform.net/speech-to-text/api", - username: username, - password: password) - - let service = SpeechToText(authStrategy: basicAuth) - - let expectation = expectationWithDescription("WebSockets") - let url = NSBundle(forClass: self.dynamicType).URLForResource("SpeechSample", withExtension: "ogg") - - guard let audioData = NSData(contentsOfURL: url!) else { - XCTFail("Need to read file") - return - } - - service.transcribe( audioData, format: .OPUS, oncompletion: { - response, error in + guard let alternatives = results[0].alternatives else { + XCTFail("Must return more than zero results.") + return + } + + XCTAssertGreaterThan(alternatives.count, 0, "Must return more than zero results") - if let response = response { - - if let results = response.results { - XCTAssertGreaterThan(results.count, 0, "Must return more than zero results") - - if let alternatives = results[0].alternatives { - - XCTAssertGreaterThan(alternatives.count, 0, "Must return more than zero results") - - - if let transcript = alternatives[0].transcript - { - XCTAssertEqual(transcript, "several tornadoes touch down as a line of severe thunderstorms swept through Colorado on Sunday ") - - } - - } - - expectation.fulfill() - - } else { - XCTFail("Could not get back results for the response") - } - - } else { - XCTFail("Could not get back SpeechToTextResponse structure") + guard let transcript = alternatives[0].transcript else { + XCTFail("Expected a non-nil transcript.") + return } + XCTAssertEqual(transcript, "several tornadoes touch down as a line of severe thunderstorms swept through Colorado on Sunday ") - }) + expectation.fulfill() + } waitForExpectationsWithTimeout(timeout) { error in XCTAssertNil(error, "Timeout") } } + // func testContinuousRecording() { + // service.startListening() + // } + + // func testRecording() { + // + // let recordExpectation = expectationWithDescription("Record") + // + // let recordSettings = [ + // // AVFormatIDKey: NSNumber(unsignedInt:kAudioFormatLinearPCM), + // AVNumberOfChannelsKey: 1, + // AVSampleRateKey : 16000.0 + // ] + // + // let dirsPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) + // let docsDir = dirsPaths[0] as String + // let soundFilePath = docsDir + "/test.wav" + // + // print("Saving recorded audio file in \(soundFilePath)") + // AVAudioSession.sharedInstance().requestRecordPermission({(granted: Bool) -> Void + // in + // if granted { + // let soundFileURL = NSURL(string: soundFilePath) + // + // if let soundFileURL = soundFileURL { + // + // do { + // + // self.recorder = try AVAudioRecorder(URL: soundFileURL, settings: recordSettings) + // + // self.recorder.meteringEnabled = true + // + // // self.recorder.prepareToRecord() + // self.recorder.recordForDuration(3.0) + // + // sleep(5) + // + // print(self.recorder.recording) + // + // self.recorder.stop() + // + // recordExpectation.fulfill() + // + // } catch { + // + // + // XCTAssertTrue(false, "Could not create audio recorder") + // } + // } + // + // } + // + // }) + // + // waitForExpectationsWithTimeout(timeout) { + // error in XCTAssertNil(error, "Timeout") + // } + // } } diff --git a/WatsonDeveloperCloud/TextToSpeech/Tests/TextToSpeechTests.swift b/WatsonDeveloperCloud/TextToSpeech/Tests/TextToSpeechTests.swift index bf159bdae..b6ebe7222 100644 --- a/WatsonDeveloperCloud/TextToSpeech/Tests/TextToSpeechTests.swift +++ b/WatsonDeveloperCloud/TextToSpeech/Tests/TextToSpeechTests.swift @@ -20,52 +20,47 @@ import XCTest class TextToSpeechTests: XCTestCase { - // Text to Speech Service - private var service:TextToSpeech? - - - private let playAudio = true - - // Phrase used for testing + var service: TextToSpeech! + let playAudio = true + let testString = "All the problems of the world could be solved if men were only willing to think." - let germanString = "Alle Probleme der Welt könnten gelöst werden, wenn Männer waren nur bereit, zu denken." - let ssmlString = "Hello" - /// Timeout for an asynchronous call to return before failing the unit test - private let timeout: NSTimeInterval = 20.0 + let timeout: NSTimeInterval = 20.0 override func setUp() { - super.setUp() - if let url = NSBundle(forClass: self.dynamicType).pathForResource("Credentials", ofType: "plist") { - if let dict = NSDictionary(contentsOfFile: url) as? Dictionary { - - let username = dict["TextToSpeechUsername"] - let password = dict["TextToSpeechPassword"] - - let basicAuth = BasicAuthenticationStrategy( - tokenURL: "https://stream.watsonplatform.net/authorization/api/v1/token", - serviceURL: "https://stream.watsonplatform.net/text-to-speech/api", - username: username!, - password: password!) - - service = TextToSpeech(authStrategy: basicAuth) - - } else { - XCTFail("Unable to extract dictionary from plist") - } - } else { - XCTFail("Plist file not found") + + let bundle = NSBundle(forClass: self.dynamicType) + guard let url = bundle.pathForResource("Credentials", ofType: "plist") else { + XCTFail("Unable to locate credentials file.") + return + } + + let dict = NSDictionary(contentsOfFile: url) + guard let credentials = dict as? Dictionary else { + XCTFail("Unable to read credentials file.") + return } + guard let username = credentials["TextToSpeechUsername"] else { + XCTFail("Unable to read TextToSpeech username.") + return + } + + guard let password = credentials["TextToSpeechPassword"] else { + XCTFail("Unable to read TextToSpeech password.") + return + } + + if service == nil { + service = TextToSpeech(username: username, password: password) + } } override func tearDown() { - super.tearDown() - } /** @@ -75,7 +70,7 @@ class TextToSpeechTests: XCTestCase { let voicesExpectation = expectationWithDescription("Get Voices") - service!.listVoices { voices, error in + service.listVoices { voices, error in print(voices) @@ -96,7 +91,7 @@ class TextToSpeechTests: XCTestCase { let synthExpectation = expectationWithDescription("Synthesize Audio") - service!.synthesize(testString) { data, error in + service.synthesize(testString) { data, error in print(error) XCTAssertNil(error) @@ -120,7 +115,7 @@ class TextToSpeechTests: XCTestCase { let voice = "No voice" let synthIncorrectExpectation = expectationWithDescription("Synthesize Incorrect Voice Audio") - service!.synthesize(testString, voice: voice) { + service.synthesize(testString, voice: voice) { data, error in XCTAssertNotNil(error) @@ -143,7 +138,7 @@ class TextToSpeechTests: XCTestCase { let synthEmptyExpectation = expectationWithDescription("Synthesize Incorrect Voice Audio") - service!.synthesize("") { data, error in + service.synthesize("") { data, error in XCTAssertNotNil(error) @@ -165,7 +160,7 @@ class TextToSpeechTests: XCTestCase { let synthPlayExpectation = expectationWithDescription("Synthesize Audio") - service!.synthesize(testString) { + service.synthesize(testString) { data, error in if let data = data { @@ -201,7 +196,7 @@ class TextToSpeechTests: XCTestCase { let playExpectation = expectationWithDescription("Synthesize German Audio") let dieterVoice = "de-DE_DieterVoice" - service!.synthesize(germanString, voice: dieterVoice) { + service.synthesize(germanString, voice: dieterVoice) { data, error in if let data = data { @@ -235,11 +230,8 @@ class TextToSpeechTests: XCTestCase { func testSynthAndPlaySSML() { let synthPlaySSMLExpectation = expectationWithDescription("Synthesize Audio") - - - - - service!.synthesize(ssmlString) { data, error in + + service.synthesize(ssmlString) { data, error in if let data = data { @@ -265,7 +257,4 @@ class TextToSpeechTests: XCTestCase { waitForExpectationsWithTimeout(timeout, handler: { error in XCTAssertNil(error, "Timeout") }) } - - - } diff --git a/WatsonDeveloperCloud/WatsonCore/Utilities/NetworkUtils.swift b/WatsonDeveloperCloud/WatsonCore/Utilities/NetworkUtils.swift index 51f8caf9f..120d89eee 100644 --- a/WatsonDeveloperCloud/WatsonCore/Utilities/NetworkUtils.swift +++ b/WatsonDeveloperCloud/WatsonCore/Utilities/NetworkUtils.swift @@ -82,7 +82,7 @@ public class NetworkUtils { - parameter apiKey: The authentication string used for HTTP basic authorization. - parameter completionHandler: The closure called when the token request is complete. */ - public static func requestAuthToken(tokenURL: String, serviceURL: String, apiKey: String? = nil, completionHandler: (token: String?, error: NSError?) -> ()) { + public static func requestAuthToken(tokenURL: String, serviceURL: String, apiKey: String? = nil, completionHandler: (token: String?, error: NSError?) -> Void) { Log.sharedLogger.debug("Entered requestAuthToken") @@ -103,7 +103,7 @@ public class NetworkUtils { - parameter contentType: This will switch the input and outout request from text or json - parameter completionHandler: Returns CoreResponse which is a payload of valid AnyObject data or a NSError */ - public static func performBasicAuthRequest(url: String, method: HTTPMethod = HTTPMethod.GET, parameters: [String: AnyObject]? = [:], contentType: ContentType = ContentType.JSON, accept: ContentType = ContentType.JSON, encoding: ParameterEncoding = ParameterEncoding.URL, apiKey:String? = nil, completionHandler: (returnValue: CoreResponse) -> ()) { + public static func performBasicAuthRequest(url: String, method: HTTPMethod = HTTPMethod.GET, parameters: [String: AnyObject]? = [:], contentType: ContentType = ContentType.JSON, accept: ContentType = ContentType.JSON, encoding: ParameterEncoding = ParameterEncoding.URL, apiKey:String? = nil, completionHandler: (returnValue: CoreResponse) -> Void) { Log.sharedLogger.debug("Entered performBasicAuthRequest") @@ -135,7 +135,7 @@ public class NetworkUtils { - parameter parameters: Dictionary of parameters to use as part of the HTTP query - parameter completionHandler: Returns CoreResponse which is a payload of valid AnyObject data or a NSError */ - public static func performRequest(url: String, method: HTTPMethod = HTTPMethod.GET, parameters: [String: AnyObject] = [:], completionHandler: (returnValue: CoreResponse) -> ()) { + public static func performRequest(url: String, method: HTTPMethod = HTTPMethod.GET, parameters: [String: AnyObject] = [:], completionHandler: (returnValue: CoreResponse) -> Void) { Log.sharedLogger.debug("Entered performRequest") @@ -156,7 +156,7 @@ public class NetworkUtils { - parameter parameters: Dictionary of parameters to use as part of the HTTP query - parameter completionHandler: Returns CoreResponse which is a payload of valid AnyObject data or a NSError */ - public static func performBasicAuthFileUploadMultiPart(url: String, fileURLs: [String:NSURL], parameters: [String: AnyObject]=[:], apiKey: String? = nil, contentType: ContentType = ContentType.URLEncoded, accept: ContentType = ContentType.URLEncoded, completionHandler: (returnValue: CoreResponse) -> ()) { + public static func performBasicAuthFileUploadMultiPart(url: String, fileURLs: [String:NSURL], parameters: [String: AnyObject]=[:], apiKey: String? = nil, contentType: ContentType = ContentType.URLEncoded, accept: ContentType = ContentType.URLEncoded, completionHandler: (returnValue: CoreResponse) -> Void) { Log.sharedLogger.debug("Entered performBasicAuthFileUploadMultiPart") @@ -193,7 +193,7 @@ public class NetworkUtils { - parameter completionHandler: Returns CoreResponse which is a payload of valid AnyObject data or a NSError */ // TODO: STILL IN PROGRESS - public static func performBasicAuthFileUpload(url: String, fileURL: NSURL, parameters: [String: AnyObject]=[:], apiKey: String? = nil, completionHandler: (returnValue: CoreResponse) -> ()) { + public static func performBasicAuthFileUpload(url: String, fileURL: NSURL, parameters: [String: AnyObject]=[:], apiKey: String? = nil, completionHandler: (returnValue: CoreResponse) -> Void) { // TODO: This is not optimal but I had to append the params to the url in order for this to work correctly. // I will get back to looking into this at some point but want to get it working diff --git a/WatsonDeveloperCloud/WatsonCore/WatsonManager.swift b/WatsonDeveloperCloud/WatsonCore/WatsonGateway.swift similarity index 84% rename from WatsonDeveloperCloud/WatsonCore/WatsonManager.swift rename to WatsonDeveloperCloud/WatsonCore/WatsonGateway.swift index d00a099d3..9c6cc78e9 100644 --- a/WatsonDeveloperCloud/WatsonCore/WatsonManager.swift +++ b/WatsonDeveloperCloud/WatsonCore/WatsonGateway.swift @@ -79,8 +79,36 @@ internal class WatsonGateway { } var authStrategy = request.authStrategy - if authStrategy.isRefreshing { + + guard !authStrategy.isRefreshing else { + cachedRequests.append(cachedRequest) + return + } + + guard authStrategy.token != nil else { cachedRequests.append(cachedRequest) + authStrategy.isRefreshing = true + authStrategy.retries++ + authStrategy.refreshToken { [weak self] error in + authStrategy.isRefreshing = false + + guard let strongSelf = self else { + let code = -1 + let description = "Internal error. Unable to execute network operation." + let error = NSError.createWatsonError(code, description: description) + completionHandler(nil, error) + return + } + + guard error == nil else { + completionHandler(nil, error) + return + } + + let cachedRequestsCopy = strongSelf.cachedRequests + strongSelf.cachedRequests.removeAll() + cachedRequestsCopy.forEach { $0() } + } return } diff --git a/WatsonDeveloperCloudTests/Credentials.plist b/WatsonDeveloperCloudTests/Credentials.plist.example similarity index 100% rename from WatsonDeveloperCloudTests/Credentials.plist rename to WatsonDeveloperCloudTests/Credentials.plist.example diff --git a/WatsonDeveloperCloudTests/Credentials_full.plist.enc b/WatsonDeveloperCloudTests/Credentials_full.plist.enc new file mode 100644 index 000000000..036f537c0 Binary files /dev/null and b/WatsonDeveloperCloudTests/Credentials_full.plist.enc differ