├── .ruby-version ├── .ruby-gemset ├── docs ├── img │ ├── gh.png │ ├── carat.png │ └── dash.png ├── docsets │ ├── PredictionIO.tgz │ ├── PredictionIO.xml │ └── PredictionIO.docset │ │ └── Contents │ │ ├── Resources │ │ ├── docSet.dsidx │ │ └── Documents │ │ │ ├── img │ │ │ ├── gh.png │ │ │ ├── carat.png │ │ │ └── dash.png │ │ │ ├── undocumented.json │ │ │ ├── badge.svg │ │ │ ├── js │ │ │ └── jazzy.js │ │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ │ ├── Enums.html │ │ │ ├── Structs │ │ │ ├── CreateEventResponse.html │ │ │ ├── EventResponse.html │ │ │ └── CreateBatchEventsResponse.html │ │ │ ├── Enums │ │ │ └── PIOError │ │ │ │ └── SerializationFailureReason.html │ │ │ └── Classes.html │ │ └── Info.plist ├── undocumented.json ├── badge.svg ├── js │ └── jazzy.js ├── css │ ├── highlight.css │ └── jazzy.css ├── Enums.html ├── Structs │ ├── CreateEventResponse.html │ ├── EventResponse.html │ └── CreateBatchEventsResponse.html ├── Enums │ └── PIOError │ │ └── SerializationFailureReason.html ├── Classes.html └── Structs.html ├── Gemfile ├── iOS Example ├── Source │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── edit.imageset │ │ │ ├── edit.png │ │ │ ├── edit@2x.png │ │ │ └── Contents.json │ │ ├── list.imageset │ │ │ ├── list.png │ │ │ ├── list@2x.png │ │ │ └── Contents.json │ │ ├── star.imageset │ │ │ ├── star.png │ │ │ ├── star@2x.png │ │ │ └── Contents.json │ │ ├── star-empty.imageset │ │ │ ├── star-empty.png │ │ │ ├── star-empty@2x.png │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── RatingViewController.swift │ └── RecommendationViewController.swift └── iOS Example.xcodeproj │ └── xcshareddata │ └── xcschemes │ └── iOS Example.xcscheme ├── PredictionIO.xcworkspace ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── contents.xcworkspacedata ├── .jazzy.yaml ├── PredictionIO ├── Source │ ├── PredictionIO.h │ ├── Info.plist │ ├── PIOError.swift │ ├── NetworkConnection.swift │ └── Event.swift ├── Tests │ ├── Info.plist │ ├── Result+Extension.swift │ ├── EngineClientTests.swift │ ├── PIOError+Tests.swift │ ├── EventTests.swift │ └── EventClientTests.swift └── PredictionIO.xcodeproj │ └── xcshareddata │ └── xcschemes │ ├── PredictionIO iOS.xcscheme │ └── PredictionIO macOS.xcscheme ├── .gitignore ├── PredictionIO.podspec ├── .swiftlint.yml ├── .travis.yml ├── Gemfile.lock └── README.md /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.5 2 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | PredictionIO -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/img/gh.png -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "cocoapods" 4 | gem "jazzy" 5 | gem "xcpretty" 6 | -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/docsets/PredictionIO.tgz -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/minhtule/projects/predictionio/PredictionIO-Swift-SDK" 6 | } -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.xml: -------------------------------------------------------------------------------- 1 | 3.0.0https://minhtule.github.io/PredictionIO-Swift-SDK/docsets/PredictionIO.tgz 2 | -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/edit.imageset/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/edit.imageset/edit.png -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/list.imageset/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/list.imageset/list.png -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/star.imageset/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/star.imageset/star.png -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/edit.imageset/edit@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/edit.imageset/edit@2x.png -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/list.imageset/list@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/list.imageset/list@2x.png -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/star.imageset/star@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/star.imageset/star@2x.png -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/docsets/PredictionIO.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/star-empty.imageset/star-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/star-empty.imageset/star-empty.png -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/docsets/PredictionIO.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/minhtule/projects/predictionio/PredictionIO-Swift-SDK" 6 | } -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/star-empty.imageset/star-empty@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/iOS Example/Source/Assets.xcassets/star-empty.imageset/star-empty@2x.png -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/docsets/PredictionIO.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhtule/PredictionIO-Swift-SDK/HEAD/docs/docsets/PredictionIO.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /PredictionIO.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PredictionIO.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | author: Minh-Tu Le 2 | author_url: https://github.com/minhtule 3 | github_url: https://github.com/minhtule/PredictionIO-Swift-SDK 4 | root_url: https://minhtule.github.io/PredictionIO-Swift-SDK/ 5 | module: PredictionIO 6 | output: docs 7 | theme: apple 8 | swift_version: '5.1' 9 | swift_build_tool: xcodebuild 10 | build_tool_arguments: [-workspace, 'PredictionIO.xcworkspace', -scheme, 'PredictionIO iOS'] 11 | -------------------------------------------------------------------------------- /PredictionIO/Source/PredictionIO.h: -------------------------------------------------------------------------------- 1 | // 2 | // PredictionIO.h 3 | // PredictionIO 4 | // 5 | // Created by Minh Tu Le on 1/1/18. 6 | // Copyright © 2018 Minh Tu Le. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | //! Project version number for PredictionIO. 12 | FOUNDATION_EXPORT double PredictionIOVersionNumber; 13 | 14 | //! Project version string for PredictionIO. 15 | FOUNDATION_EXPORT const unsigned char PredictionIOVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/edit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "edit.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "edit@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/list.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "list.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "list@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "star.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "star@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /iOS Example/Source/Assets.xcassets/star-empty.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "star-empty.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "star-empty@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /iOS Example/Source/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iOS Example 4 | // 5 | // Created by Minh Tu Le on 1/9/18. 6 | // Copyright © 2018 Minh Tu Le. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.predictionio 7 | CFBundleName 8 | PredictionIO 9 | DocSetPlatformFamily 10 | predictionio 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ 27 | 28 | # Carthage 29 | # 30 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 31 | # Carthage/Checkouts 32 | 33 | Carthage/Build 34 | -------------------------------------------------------------------------------- /PredictionIO.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'PredictionIO' 3 | spec.version = '3.0.0' 4 | spec.license = 'Apache 2.0' 5 | spec.summary = 'The iOS SDK that provides easy-to-use functions to integrate with PredictionIO REST API services' 6 | spec.homepage = 'https://github.com/minhtule/PredictionIO-Swift-SDK' 7 | spec.authors = { 'Minh-Tu Le' => 'minhtule05@gmail.com' } 8 | spec.source = { :git => 'https://github.com/minhtule/PredictionIO-Swift-SDK.git', :tag => spec.version } 9 | spec.source_files = 'PredictionIO/Source/*.swift' 10 | spec.swift_versions = ['5.0', '5.1'] 11 | 12 | # Platform 13 | spec.platform = :ios 14 | spec.ios.deployment_target = '10.0' 15 | spec.osx.deployment_target = '10.10' 16 | 17 | # Build settings 18 | spec.requires_arc = true 19 | 20 | end 21 | -------------------------------------------------------------------------------- /PredictionIO/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - cyclomatic_complexity 3 | - file_length 4 | - line_length 5 | - todo 6 | - unused_optional_binding 7 | opt_in_rules: 8 | - array_init 9 | - attributes 10 | - closure_end_indentation 11 | - closure_spacing 12 | - empty_count 13 | - explicit_init 14 | - extension_access_modifier 15 | - fatal_error_message 16 | - first_where 17 | - implicit_return 18 | - let_var_whitespace 19 | - literal_expression_end_indentation 20 | - number_separator 21 | - object_literal 22 | - operator_usage_whitespace 23 | - overridden_super_call 24 | - pattern_matching_keywords 25 | - prohibited_super_call 26 | - redundant_nil_coalescing 27 | - trailing_closure 28 | - unneeded_parentheses_in_closure_argument 29 | - vertical_parameter_alignment_on_call 30 | 31 | # Rule configurations 32 | nesting: 33 | type_level: 34 | warning: 2 35 | -------------------------------------------------------------------------------- /PredictionIO/Source/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | 2.1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /PredictionIO/Tests/Result+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result+Extension.swift 3 | // PredictionIO iOS 4 | // 5 | // Created by Minh Tu Le on 9/16/19. 6 | // Copyright © 2019 PredictionIO. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Result { 12 | var isSuccess: Bool { 13 | switch self { 14 | case .success: 15 | return true 16 | case .failure: 17 | return false 18 | } 19 | } 20 | 21 | var isFailure: Bool { 22 | return !isSuccess 23 | } 24 | 25 | var value: Success? { 26 | switch self { 27 | case let .success(value): 28 | return value 29 | case .failure: 30 | return nil 31 | } 32 | } 33 | 34 | var error: Failure? { 35 | switch self { 36 | case .success: 37 | return nil 38 | case let .failure(error): 39 | return error 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /iOS Example/Source/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 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /iOS Example/Source/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSExceptionDomains 26 | 27 | localhost 28 | 29 | NSTemporaryExceptionAllowsInsecureHTTPLoads 30 | 31 | 32 | 33 | 34 | UILaunchStoryboardName 35 | LaunchScreen 36 | UIMainStoryboardFile 37 | Main 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | UISupportedInterfaceOrientations~ipad 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationPortraitUpsideDown 52 | UIInterfaceOrientationLandscapeLeft 53 | UIInterfaceOrientationLandscapeRight 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /iOS Example/Source/RatingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RatingViewController.swift 3 | // iOS Example 4 | // 5 | // Created by Minh Tu Le on 1/9/18. 6 | // Copyright © 2018 Minh Tu Le. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PredictionIO 11 | 12 | let accessKey = "Your app's access key" 13 | 14 | class RatingViewController: UIViewController { 15 | @IBOutlet weak var userIDTextField: UITextField! 16 | @IBOutlet weak var movieIDTextField: UITextField! 17 | @IBOutlet var starButtons: [UIButton]! 18 | 19 | let eventClient = EventClient(accessKey: accessKey) 20 | var userID = "" 21 | var movieID = "" 22 | var rating = 0 23 | 24 | @IBAction func starButtonAction(_ sender: UIButton) { 25 | if sender.tag == rating { 26 | rating = 0 27 | } else { 28 | rating = sender.tag 29 | } 30 | 31 | starButtons.forEach { $0.isSelected = ($0.tag <= self.rating) } 32 | } 33 | 34 | @IBAction func rateButtonAction(_ sender: UIButton) { 35 | userID = userIDTextField.text ?? "" 36 | movieID = movieIDTextField.text ?? "" 37 | 38 | eventClient.recordAction("rate", byUserID: userID, onItemID: movieID, properties: ["rating": rating]) { result in 39 | var alertController: UIAlertController 40 | switch result { 41 | case let .success(response): 42 | alertController = UIAlertController(title: "Successful", message: "EventID: \(response.eventID)", preferredStyle: .alert) 43 | case let .failure(error): 44 | alertController = UIAlertController(title: "Failed", message: "\(error)", preferredStyle: .alert) 45 | } 46 | 47 | let okAction = UIAlertAction(title: "OK", style: .default) 48 | alertController.addAction(okAction) 49 | 50 | DispatchQueue.main.async { 51 | self.present(alertController, animated: true) 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /iOS Example/Source/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 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /PredictionIO/Tests/EngineClientTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EngineClientTests.swift 3 | // PredictionIOTests 4 | // 5 | // Created by Minh Tu Le on 1/8/18. 6 | // Copyright © 2018 PredictionIO. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import PredictionIO 11 | 12 | class EngineClientTests: XCTestCase { 13 | var engineClient: EngineClient! 14 | 15 | override func setUp() { 16 | super.setUp() 17 | 18 | engineClient = EngineClient(baseURL: "http://localhost:8000") 19 | } 20 | 21 | func testSendQuery() { 22 | let expectation = self.expectation(description: "Sending query") 23 | 24 | engineClient.sendQuery(["user": "1"]) { result in 25 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 26 | 27 | expectation.fulfill() 28 | } 29 | 30 | waitForExpectations(timeout: 5) { error in 31 | XCTAssertNil(error, "\(error!)") 32 | } 33 | } 34 | 35 | func testSendQueryWithResponseType() { 36 | // This test expects the engine server to return a JSON response that 37 | // has same response format as the recommendation/similar product engine 38 | // template e.g. 39 | // 40 | // { 41 | // "itemScores": [ 42 | // { 43 | // "item": "39", 44 | // "score": 6.177719297832409 45 | // }, 46 | // { 47 | // "item": "79", 48 | // "score": 5.931687319083594 49 | // } 50 | // ] 51 | // } 52 | // 53 | let expectation = self.expectation(description: "Sending query") 54 | 55 | engineClient.sendQuery(["user": "1"], responseType: SimilarProductResponse.self) { result in 56 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 57 | 58 | expectation.fulfill() 59 | } 60 | 61 | waitForExpectations(timeout: 5) { error in 62 | XCTAssertNil(error, "\(error!)") 63 | } 64 | } 65 | } 66 | 67 | private struct SimilarProductResponse: Decodable { 68 | struct ItemScore: Decodable { 69 | let itemID: String 70 | let score: Double 71 | 72 | enum CodingKeys: String, CodingKey { 73 | case itemID = "item" 74 | case score 75 | } 76 | } 77 | 78 | let itemScores: [ItemScore] 79 | } 80 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode11 3 | git: 4 | depth: 1 5 | branches: 6 | only: 7 | - master 8 | cache: bundler 9 | 10 | env: 11 | global: 12 | - WORKSPACE=PredictionIO.xcworkspace 13 | - IOS_FRAMEWORK_SCHEME="PredictionIO iOS" 14 | - MACOS_FRAMEWORK_SCHEME="PredictionIO macOS" 15 | - EXAMPLE_SCHEME="iOS Example" 16 | - MOCK_SERVER_DIR=mock_server 17 | matrix: 18 | # iOS 19 | - DESTINATION="OS=13.0,name=iPhone XS Max" SCHEME="$IOS_FRAMEWORK_SCHEME" BUILD_EXAMPLE="YES" LINTING="YES" 20 | - DESTINATION="OS=12.2,name=iPhone XS" SCHEME="$IOS_FRAMEWORK_SCHEME" BUILD_EXAMPLE="YES" LINTING="NO" 21 | - DESTINATION="OS=11.4,name=iPhone X" SCHEME="$IOS_FRAMEWORK_SCHEME" BUILD_EXAMPLE="YES" LINTING="NO" 22 | - DESTINATION="OS=10.3.1,name=iPhone 7 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" BUILD_EXAMPLE="YES" LINTING="NO" 23 | # macOS 24 | - DESTINATION="arch=x86_64" SCHEME="$MACOS_FRAMEWORK_SCHEME" BUILD_EXAMPLE="NO" LINTING="NO" 25 | 26 | # WARNING: Because of the existence of `.ruby-gemset`, we should not change 27 | # directory and go back into this repo directory. When we `cd` back into 28 | # this current working directory, `rvm` would use the `PredictionIO` gemset 29 | # which doesn't have any gem installed!!! 30 | before_script: 31 | # Set up mock API server 32 | - node --version 33 | - npm --version 34 | - echo "Setting up PredictionIO mock server..." 35 | - git clone git://github.com/minhtule/PredictionIO-Mock-Server.git ../$MOCK_SERVER_DIR --depth 1 36 | - npm --prefix ../$MOCK_SERVER_DIR install ../$MOCK_SERVER_DIR 37 | - node ../$MOCK_SERVER_DIR/app.js & 38 | - sleep 3 # Give mock server some time to bind to sockets, etc 39 | 40 | script: 41 | - set -eo pipefail 42 | 43 | # Linting with SwiftLint and CocoaPods 44 | - if [ $LINTING == "YES" ]; then 45 | echo "SwiftLint version `swiftlint version`"; 46 | echo "SwiftLint linting..."; 47 | swiftlint lint --strict; 48 | echo "CocoaPods linting..."; 49 | pod lib lint; 50 | fi 51 | 52 | # Test in Release 53 | - xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty 54 | 55 | # Build the iOS demo app in Debug 56 | - if [ $BUILD_EXAMPLE == "YES" ]; then 57 | xcodebuild -workspace "$WORKSPACE" -scheme "$EXAMPLE_SCHEME" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty; 58 | fi 59 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.1) 5 | activesupport (4.2.11.1) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | atomos (0.1.3) 11 | claide (1.0.3) 12 | cocoapods (1.7.5) 13 | activesupport (>= 4.0.2, < 5) 14 | claide (>= 1.0.2, < 2.0) 15 | cocoapods-core (= 1.7.5) 16 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 17 | cocoapods-downloader (>= 1.2.2, < 2.0) 18 | cocoapods-plugins (>= 1.0.0, < 2.0) 19 | cocoapods-search (>= 1.0.0, < 2.0) 20 | cocoapods-stats (>= 1.0.0, < 2.0) 21 | cocoapods-trunk (>= 1.3.1, < 2.0) 22 | cocoapods-try (>= 1.1.0, < 2.0) 23 | colored2 (~> 3.1) 24 | escape (~> 0.0.4) 25 | fourflusher (>= 2.3.0, < 3.0) 26 | gh_inspector (~> 1.0) 27 | molinillo (~> 0.6.6) 28 | nap (~> 1.0) 29 | ruby-macho (~> 1.4) 30 | xcodeproj (>= 1.10.0, < 2.0) 31 | cocoapods-core (1.7.5) 32 | activesupport (>= 4.0.2, < 6) 33 | fuzzy_match (~> 2.0.4) 34 | nap (~> 1.0) 35 | cocoapods-deintegrate (1.0.4) 36 | cocoapods-downloader (1.6.3) 37 | cocoapods-plugins (1.0.0) 38 | nap 39 | cocoapods-search (1.0.0) 40 | cocoapods-stats (1.1.0) 41 | cocoapods-trunk (1.4.0) 42 | nap (>= 0.8, < 2.0) 43 | netrc (~> 0.11) 44 | cocoapods-try (1.1.0) 45 | colored2 (3.1.2) 46 | concurrent-ruby (1.1.5) 47 | escape (0.0.4) 48 | ffi (1.11.1) 49 | fourflusher (2.3.1) 50 | fuzzy_match (2.0.4) 51 | gh_inspector (1.1.3) 52 | i18n (0.9.5) 53 | concurrent-ruby (~> 1.0) 54 | jazzy (0.11.0) 55 | cocoapods (~> 1.5) 56 | mustache (~> 1.1) 57 | open4 58 | redcarpet (~> 3.4) 59 | rouge (>= 2.0.6, < 4.0) 60 | sassc (~> 2.1) 61 | sqlite3 (~> 1.3) 62 | xcinvoke (~> 0.3.0) 63 | liferaft (0.0.6) 64 | minitest (5.11.3) 65 | molinillo (0.6.6) 66 | mustache (1.1.0) 67 | nanaimo (0.2.6) 68 | nap (1.1.0) 69 | netrc (0.11.0) 70 | open4 (1.3.4) 71 | redcarpet (3.5.1) 72 | rouge (2.0.7) 73 | ruby-macho (1.4.0) 74 | sassc (2.2.0) 75 | ffi (~> 1.9) 76 | sqlite3 (1.4.1) 77 | thread_safe (0.3.6) 78 | tzinfo (1.2.5) 79 | thread_safe (~> 0.1) 80 | xcinvoke (0.3.0) 81 | liferaft (~> 0.0.6) 82 | xcodeproj (1.12.0) 83 | CFPropertyList (>= 2.3.3, < 4.0) 84 | atomos (~> 0.1.3) 85 | claide (>= 1.0.2, < 2.0) 86 | colored2 (~> 3.1) 87 | nanaimo (~> 0.2.6) 88 | xcpretty (0.3.0) 89 | rouge (~> 2.0.7) 90 | 91 | PLATFORMS 92 | ruby 93 | 94 | DEPENDENCIES 95 | cocoapods 96 | jazzy 97 | xcpretty 98 | 99 | BUNDLED WITH 100 | 1.16.6 101 | -------------------------------------------------------------------------------- /iOS Example/Source/RecommendationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecommendationViewController.swift 3 | // iOS Example 4 | // 5 | // Created by Minh Tu Le on 1/9/18. 6 | // Copyright © 2018 Minh Tu Le. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PredictionIO 11 | 12 | class RecommendationViewController: UIViewController { 13 | @IBOutlet weak var userIDTextField: UITextField! 14 | @IBOutlet weak var numberOfItemsTextField: UITextField! 15 | @IBOutlet weak var resultTableView: UITableView! 16 | 17 | let engineClient = EngineClient() 18 | var userID = 1 19 | var numberOfItems = 4 20 | var recommendation: [RecommendationResponse.ItemScore] = [] 21 | 22 | @IBAction func recommendButtonAction(_ sender: UIButton) { 23 | userID = Int(userIDTextField.text!)! 24 | numberOfItems = Int(numberOfItemsTextField.text!)! 25 | 26 | let query = [ 27 | "user": userID, 28 | "num": numberOfItems 29 | ] 30 | 31 | engineClient.sendQuery(query, responseType: RecommendationResponse.self) { [weak self] result in 32 | switch result { 33 | case let .success(response): 34 | self?.recommendation = response.itemScores 35 | case let .failure(error): 36 | self?.recommendation = [] 37 | 38 | let alertController = UIAlertController(title: "Failed", message: "\(error)", preferredStyle: .alert) 39 | let okAction = UIAlertAction(title: "OK", style: .default) 40 | alertController.addAction(okAction) 41 | 42 | DispatchQueue.main.async { 43 | self?.present(alertController, animated: true) 44 | } 45 | } 46 | 47 | DispatchQueue.main.async { 48 | self?.resultTableView.reloadData() 49 | } 50 | } 51 | } 52 | } 53 | 54 | extension RecommendationViewController: UITableViewDataSource { 55 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 56 | return recommendation.count 57 | } 58 | 59 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 60 | let cell = tableView.dequeueReusableCell(withIdentifier: "RecommendedItemCellIdentifier", for: indexPath) 61 | let recommendedItem = recommendation[indexPath.row] 62 | cell.textLabel?.text = "Item ID: \(recommendedItem.itemID),\t score: \(recommendedItem.score)" 63 | 64 | return cell 65 | } 66 | } 67 | 68 | struct RecommendationResponse: Decodable { 69 | struct ItemScore: Decodable { 70 | let itemID: String 71 | let score: Double 72 | 73 | enum CodingKeys: String, CodingKey { 74 | case itemID = "item" 75 | case score 76 | } 77 | } 78 | 79 | let itemScores: [ItemScore] 80 | } 81 | -------------------------------------------------------------------------------- /iOS Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /PredictionIO/PredictionIO.xcodeproj/xcshareddata/xcschemes/PredictionIO iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PredictionIO Swift SDK 2 | 3 | [![Build Status](https://travis-ci.org/minhtule/PredictionIO-Swift-SDK.svg?branch=master)](https://travis-ci.org/minhtule/PredictionIO-Swift-SDK) 4 | 5 | The Swift SDK provides a convenient API for your iOS and macOS application to record your users' behaviors in the [PredictionIO](https://github.com/apache/predictionio) event server and retrieve predictions from PredictionIO engines. 6 | 7 | ## Requirements 8 | - iOS 10+ or macOS 10.10+ 9 | - Xcode 11+ 10 | - Swift 5+ 11 | - PredictionIO 0.12.0+ 12 | 13 | ## Installation 14 | 15 | ### Cocoapods 16 | Install [CocoaPods](https://cocoapods.org/), the dependency manager for Cocoa project. 17 | ```bash 18 | $ gem install cocoapods 19 | ``` 20 | 21 | To integrate PredictionIO, add the following lines to your `Podfile`. 22 | ```ruby 23 | source 'https://github.com/CocoaPods/Specs.git' 24 | platform :ios, '10.0' 25 | use_frameworks! 26 | 27 | target '' do 28 | pod 'PredictionIO', '~> 3.0' 29 | end 30 | ``` 31 | 32 | Then run the following command. 33 | ```bash 34 | $ pod install 35 | ``` 36 | 37 | Finally, import the SDK in your Swift files before using. 38 | ```swift 39 | import PredictionIO 40 | ``` 41 | 42 | ## Usage 43 | 44 | ### EngineClient 45 | Use `EngineClient` to query predictions from the PredictionIO Engines. 46 | 47 | ```swift 48 | // Response format of a Recommendation engine. 49 | struct RecommendationResponse: Decodable { 50 | struct ItemScore: Decodable { 51 | let item: String 52 | let score: Double 53 | } 54 | 55 | let itemScores: [ItemScore] 56 | } 57 | 58 | let engineClient = EngineClient(baseURL: "http://localhost:8000") 59 | let query = [ 60 | "user": "1", 61 | "num": 2 62 | ] 63 | 64 | engineClient.sendQuery(query, responseType: RecommendationResponse.self) { result in 65 | guard let response = result.value else { return } 66 | 67 | print(response.itemScores) 68 | } 69 | ``` 70 | 71 | ### EventClient 72 | Use `EventClient` to send information to the PredictionIO Event Server. 73 | 74 | ```swift 75 | let eventClient = EventClient(accessKey: "Access key of the app", baseURL: "http://localhost:7070") 76 | let event = Event( 77 | event: "rate", 78 | entityType: "user", 79 | entityID: "1", 80 | targetEntity: (type: "item", id: "9"), 81 | properties: [ 82 | "rating": 5 83 | ] 84 | ) 85 | 86 | eventClient.createEvent(event) { result in 87 | guard let response = result.value else { return } 88 | 89 | print(response.eventID) 90 | } 91 | ``` 92 | 93 | There are other convenience methods to manage User and Item entity types. Please see the [API documentation](http://minhtule.github.io/PredictionIO-Swift-SDK/index.html) for more details. 94 | 95 | ## Documentation 96 | 97 | The documentation is generated by [jazzy](https://github.com/realm/jazzy). To build the documentation, run 98 | 99 | ```bash 100 | $ jazzy 101 | ``` 102 | 103 | The latest API documentation is available at http://minhtule.github.io/PredictionIO-Swift-SDK/index.html. 104 | 105 | ## iOS Demo App 106 | Please follow this [quick guide](http://predictionio.apache.org/templates/recommendation/quickstart/) to start the Event Server and set up a Recommendation Engine on your local machine first. 107 | 108 | You also need to: 109 | - Include your app's access key in `RatingViewController.swift`. 110 | - Import some data using the python script as instructed in step 4. Alternatively, you can use the demo app to record new rating events; however, remember to re-train and deploy the engine before querying for recommendations. 111 | - Run the simulator! 112 | 113 | There are 2 screens in the demo app: 114 | - **Rating**: corresponding to step *4. Collecting Data* in the quick guide. 115 | - **Recommendation**: corresponding to step *6. Use the Engine* in the quick guide. 116 | 117 | ### Tapster iOS Demo 118 | 119 | Also check out [Tapster iOS](https://github.com/minhtule/Tapster-iOS-Demo), a recommender for comics, to see a more extensive intergration of the SDK. 120 | 121 | ## License 122 | PredictionIO Swift SDK is released under the Apache License 2.0. Please see 123 | [LICENSE](https://github.com/minhtule/PredictionIO-Swift-SDK/blob/master/LICENSE) for details. 124 | -------------------------------------------------------------------------------- /PredictionIO/Tests/PIOError+Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PIOError+Tests.swift 3 | // PredictionIOTests 4 | // 5 | // Created by Minh Tu Le on 1/7/18. 6 | // Copyright © 2018 PredictionIO. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PredictionIO 11 | 12 | extension PIOError { 13 | 14 | // Invalid URL 15 | 16 | func isInvalidURL(string: String, queryParams: [String: String]?) -> Bool { 17 | if case let .invalidURL(selfString, selfQueryParams) = self { 18 | let sameQueryParams: Bool 19 | if let queryParams = queryParams, let selfQueryParams = selfQueryParams { 20 | sameQueryParams = queryParams == selfQueryParams 21 | } else { 22 | sameQueryParams = queryParams == nil && selfQueryParams == nil 23 | } 24 | 25 | return sameQueryParams && string == selfString 26 | } 27 | return false 28 | } 29 | 30 | // Invalid Event 31 | 32 | func isInvalidEventID() -> Bool { 33 | if case let .invalidEvent(reason) = self, 34 | case PIOError.InvalidEventReason.invalidEventID = reason { 35 | return true 36 | } 37 | return false 38 | } 39 | 40 | func isInvalidJSONProperties() -> Bool { 41 | if case let .invalidEvent(reason) = self, 42 | case PIOError.InvalidEventReason.invalidJSONProperties = reason { 43 | return true 44 | } 45 | return false 46 | } 47 | 48 | func isUnsetEventWithEmptyProperties() -> Bool { 49 | if case let .invalidEvent(reason) = self, 50 | case PIOError.InvalidEventReason.unsetEventWithEmptyProperties = reason { 51 | return true 52 | } 53 | return false 54 | } 55 | 56 | // Failed Request 57 | 58 | func isServerFailureRequest(statusCode: Int) -> Bool { 59 | if case let .failedRequest(reason) = self, 60 | case let PIOError.RequestFailureReason.serverFailure(statusCode: actualStatusCode, message: _) = reason { 61 | return statusCode == actualStatusCode 62 | } 63 | return false 64 | } 65 | 66 | func isUnknownResponseRequest() -> Bool { 67 | if case let .failedRequest(reason) = self, 68 | case PIOError.RequestFailureReason.unknownResponse = reason { 69 | return true 70 | } 71 | return false 72 | } 73 | 74 | func isFailedRequest() -> Bool { 75 | if case let .failedRequest(reason) = self, 76 | case PIOError.RequestFailureReason.failed = reason { 77 | return true 78 | } 79 | return false 80 | } 81 | 82 | // Failed Serialization 83 | 84 | func isFailedSerialization() -> Bool { 85 | if case let .failedSerialization(reason) = self, 86 | case PIOError.SerializationFailureReason.failed = reason { 87 | return true 88 | } 89 | return false 90 | } 91 | 92 | // Failed Deserialization 93 | 94 | func isDeserializingUnknownFormat() -> Bool { 95 | if case let .failedDeserialization(reason) = self, 96 | case PIOError.DeserializationFailureReason.unknownFormat = reason { 97 | return true 98 | } 99 | return false 100 | } 101 | 102 | func isDeserializingMissingField(_ field: String? = nil) -> Bool { 103 | if case let .failedDeserialization(reason) = self, 104 | case let PIOError.DeserializationFailureReason.missingField(myMissingField) = reason { 105 | if let field = field { 106 | return field == myMissingField 107 | } 108 | return true 109 | } 110 | return false 111 | } 112 | 113 | func isDeserializingInvalidField(_ field: String? = nil) -> Bool { 114 | if case let .failedDeserialization(reason) = self, 115 | case let PIOError.DeserializationFailureReason.invalidField(myInvalidField, _) = reason { 116 | if let field = field { 117 | return field == myInvalidField 118 | } 119 | return true 120 | } 121 | return false 122 | } 123 | 124 | func isFailedDeserialization() -> Bool { 125 | if case let .failedDeserialization(reason) = self, 126 | case PIOError.DeserializationFailureReason.failed = reason { 127 | return true 128 | } 129 | return false 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /PredictionIO/PredictionIO.xcodeproj/xcshareddata/xcschemes/PredictionIO macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 77 | 83 | 84 | 85 | 86 | 92 | 93 | 99 | 100 | 101 | 102 | 104 | 105 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /PredictionIO/Source/PIOError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error.swift 3 | // PredictionIO 4 | // 5 | // Created by Minh Tu Le on 1/2/18. 6 | // Copyright © 2018 PredictionIO. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// `PIOError` is the error type returned by PredictionIO. 12 | public enum PIOError: Error { 13 | /// Returned when failing to convert a string and optionally a query params 14 | /// dictionary to a valid `URL`. 15 | case invalidURL(string: String, queryParams: [String: String]?) 16 | /// Returned when an event is found invalid 17 | case invalidEvent(reason: InvalidEventReason) 18 | /// Returned when a request fails. It could be due to network error, backend error 19 | /// or unknown response format. 20 | case failedRequest(reason: RequestFailureReason) 21 | /// Returned when failing to serialize an object to a JSON. 22 | case failedSerialization(reason: SerializationFailureReason) 23 | /// Returned when failing to deserialize a JSON to an object. 24 | case failedDeserialization(reason: DeserializationFailureReason) 25 | 26 | // MARK: - Failure reasons 27 | 28 | /// The underlying reason the event is invalid. 29 | public enum InvalidEventReason { 30 | /// The `Event.eventID` cannot be URL-quoted. 31 | case invalidEventID 32 | /// The `Event.properties` is not a valid JSON dictionary. 33 | case invalidJSONProperties 34 | /// An `$unset` event has empty or nil `Event.properties`. 35 | case unsetEventWithEmptyProperties 36 | } 37 | 38 | /// The underlying reason the request fails. 39 | public enum RequestFailureReason { 40 | /// Server returns a non-successful status code. 41 | /// - 400: bad request e.g. invalid date format 42 | /// - 401: unauthorized 43 | /// - 404: resource not found 44 | /// - 500: internal error 45 | /// 46 | /// The server would also return a "message" field in the 47 | /// response's JSON data. 48 | case serverFailure(statusCode: Int, message: String) 49 | /// Unknown response format returned by the server. 50 | case unknownResponse 51 | /// Failure due to network or any other error. 52 | case failed(error: Error) 53 | } 54 | 55 | /// The underlying reason the serialization fails. 56 | public enum SerializationFailureReason { 57 | /// Failure due to some error. 58 | case failed(error: Error) 59 | } 60 | 61 | /// The underlying reason the deserialization fails. 62 | public enum DeserializationFailureReason { 63 | /// The format is unknown. 64 | case unknownFormat 65 | /// There is a missing field. 66 | case missingField(String) 67 | /// There is an invalid field. 68 | case invalidField(String, value: Any?) 69 | /// Failure due to some other error. 70 | case failed(error: Error) 71 | } 72 | } 73 | 74 | // MARK: - Convenience factory methods to create a PIOError 75 | 76 | extension PIOError.InvalidEventReason { 77 | static func invalidEventIDError() -> PIOError { 78 | return PIOError.invalidEvent(reason: .invalidEventID) 79 | } 80 | 81 | static func invalidJSONPropertiesError() -> PIOError { 82 | return PIOError.invalidEvent(reason: .invalidJSONProperties) 83 | } 84 | 85 | static func unsetEventWithEmptyPropertiesError() -> PIOError { 86 | return PIOError.invalidEvent(reason: .unsetEventWithEmptyProperties) 87 | } 88 | } 89 | 90 | extension PIOError.RequestFailureReason { 91 | static func serverFailureError(statusCode: Int, message: String) -> PIOError { 92 | return PIOError.failedRequest(reason: .serverFailure(statusCode: statusCode, message: message)) 93 | } 94 | 95 | static func unknownResponseError() -> PIOError { 96 | return PIOError.failedRequest(reason: .unknownResponse) 97 | } 98 | 99 | static func failedError(_ error: Error) -> PIOError { 100 | return PIOError.failedRequest(reason: .failed(error: error)) 101 | } 102 | } 103 | 104 | extension PIOError.SerializationFailureReason { 105 | static func failedError(_ error: Error) -> PIOError { 106 | return PIOError.failedSerialization(reason: .failed(error: error)) 107 | } 108 | } 109 | 110 | extension PIOError.DeserializationFailureReason { 111 | static func unknownFormatError() -> PIOError { 112 | return PIOError.failedDeserialization(reason: .unknownFormat) 113 | } 114 | 115 | static func missingFieldError(field: String) -> PIOError { 116 | return PIOError.failedDeserialization(reason: .missingField(field)) 117 | } 118 | 119 | static func invalidFieldError(field: String, value: Any?) -> PIOError { 120 | return PIOError.failedDeserialization(reason: .invalidField(field, value: value)) 121 | } 122 | 123 | static func failedError(_ error: Error) -> PIOError { 124 | return PIOError.failedDeserialization(reason: .failed(error: error)) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /PredictionIO/Source/NetworkConnection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkConnection.swift 3 | // PredictionIO 4 | // 5 | // Created by Minh Tu Le on 1/1/18. 6 | // Copyright © 2018 PredictionIO. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum HTTPMethod: String { 12 | case get = "GET" 13 | case post = "POST" 14 | case delete = "DELETE" 15 | } 16 | 17 | typealias HTTPHeaders = [String: String] 18 | typealias QueryParams = [String: String] 19 | 20 | class NetworkConnection { 21 | let session: URLSession 22 | 23 | init(session: URLSession) { 24 | self.session = session 25 | } 26 | 27 | @discardableResult 28 | func get(url: String, queryParams: QueryParams? = nil, headers: HTTPHeaders? = nil, completionHandler: @escaping (Result) -> Void) -> URLSessionDataTask? { 29 | return request(url, method: .get, queryParams: queryParams, payload: nil, headers: headers, completionHandler: completionHandler) 30 | } 31 | 32 | @discardableResult 33 | func post(url: String, payload: Any?, queryParams: QueryParams? = nil, headers: HTTPHeaders? = nil, completionHandler: @escaping (Result) -> Void) -> URLSessionDataTask? { 34 | return request(url, method: .post, queryParams: queryParams, payload: payload, headers: headers, completionHandler: completionHandler) 35 | } 36 | 37 | @discardableResult 38 | func delete(url: String, queryParams: QueryParams? = nil, headers: HTTPHeaders? = nil, completionHandler: @escaping (Result) -> Void) -> URLSessionDataTask? { 39 | return request(url, method: .delete, queryParams: queryParams, payload: nil, headers: headers, completionHandler: completionHandler) 40 | } 41 | 42 | private func request(_ url: String, method: HTTPMethod, queryParams: QueryParams? = nil, payload: Any?, headers: HTTPHeaders? = nil, completionHandler: @escaping (Result) -> Void) -> URLSessionDataTask? { 43 | do { 44 | var request = try URLRequest(url: url, method: method, queryParams: queryParams, headers: headers) 45 | try request.attachJSONPayload(payload: payload) 46 | 47 | let task = session.dataTask(with: request) { data, response, error in 48 | if let error = error { 49 | completionHandler(.failure(PIOError.RequestFailureReason.failedError(error))) 50 | return 51 | } 52 | 53 | guard let response = response as? HTTPURLResponse, 54 | let data = data 55 | else { 56 | // We should never be here! 57 | completionHandler(.failure(PIOError.RequestFailureReason.unknownResponseError())) 58 | return 59 | } 60 | 61 | switch response.statusCode { 62 | case 200...201: 63 | completionHandler(.success(data)) 64 | default: 65 | let message: String 66 | if 67 | let data = try? JSONSerialization.jsonObject(with: data, options: []), 68 | let jsonData = data as? [String: Any], 69 | let messageValue = jsonData["message"] as? String 70 | { 71 | message = messageValue 72 | } else { 73 | message = "-- Cannot parse message from server --" 74 | } 75 | let error = PIOError.RequestFailureReason.serverFailureError(statusCode: response.statusCode, message: message) 76 | completionHandler(.failure(error)) 77 | } 78 | } 79 | task.resume() 80 | return task 81 | } catch { 82 | completionHandler(.failure(error as! PIOError)) // swiftlint:disable:this force_cast 83 | return nil 84 | } 85 | } 86 | } 87 | 88 | extension URLRequest { 89 | init(url: String, method: HTTPMethod, queryParams: QueryParams? = nil, payload: Any? = nil, headers: HTTPHeaders? = nil) throws { 90 | var urlComponent = URLComponents(string: url) 91 | 92 | if let queryParams = queryParams { 93 | urlComponent?.queryItems = queryParams.map { URLQueryItem(name: $0, value: $1) } 94 | } 95 | 96 | guard let urlWithQuery = urlComponent?.url else { 97 | throw PIOError.invalidURL(string: url, queryParams: queryParams) 98 | } 99 | 100 | self.init(url: urlWithQuery) 101 | httpMethod = method.rawValue 102 | 103 | headers?.forEach { field, value in 104 | setValue(value, forHTTPHeaderField: field) 105 | } 106 | } 107 | 108 | mutating func attachJSONPayload(payload: Any?) throws { 109 | if let payload = payload, 110 | let httpMethod = httpMethod, httpMethod == HTTPMethod.post.rawValue { 111 | do { 112 | httpBody = try JSONSerialization.data(withJSONObject: payload, options: []) 113 | setValue("application/json", forHTTPHeaderField: "Content-Type") 114 | } catch { 115 | throw PIOError.SerializationFailureReason.failedError(error) 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

Enumerations

86 |

The following enumerations are available globally.

87 | 88 |
89 |
90 |
91 |
    92 |
  • 93 |
    94 | 95 | 96 | 97 | PIOError 98 | 99 |
    100 |
    101 |
    102 |
    103 |
    104 |
    105 |

    PIOError is the error type returned by PredictionIO.

    106 | 107 | See more 108 |
    109 |
    110 |

    Declaration

    111 |
    112 |

    Swift

    113 |
    public enum PIOError : Error
    114 | 115 |
    116 |
    117 |
    118 |
    119 |
  • 120 |
121 |
122 |
123 |
124 | 128 |
129 |
130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

Enumerations

86 |

The following enumerations are available globally.

87 | 88 |
89 |
90 |
91 |
    92 |
  • 93 |
    94 | 95 | 96 | 97 | PIOError 98 | 99 |
    100 |
    101 |
    102 |
    103 |
    104 |
    105 |

    PIOError is the error type returned by PredictionIO.

    106 | 107 | See more 108 |
    109 |
    110 |

    Declaration

    111 |
    112 |

    Swift

    113 |
    public enum PIOError : Error
    114 | 115 |
    116 |
    117 |
    118 |
    119 |
  • 120 |
121 |
122 |
123 |
124 | 128 |
129 |
130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /docs/Structs/CreateEventResponse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CreateEventResponse Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

CreateEventResponse

86 |
87 |
88 |
public struct CreateEventResponse : Decodable
89 | 90 |
91 |
92 |

Represents the response of a creating event request.

93 | 94 |
95 |
96 |
97 |
    98 |
  • 99 |
    100 | 101 | 102 | 103 | eventID 104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |

    The event ID of the created event.

    112 | 113 |
    114 |
    115 |

    Declaration

    116 |
    117 |

    Swift

    118 |
    public let eventID: String
    119 | 120 |
    121 |
    122 |
    123 |
    124 |
  • 125 |
126 |
127 |
128 |
129 | 133 |
134 |
135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /docs/Structs/EventResponse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EventResponse Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (96% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 85 |
86 |
87 |
88 |

EventResponse

89 |
90 |
91 |
public struct EventResponse: Decodable
92 | 93 |
94 |
95 |

Response structure for an event-creation request.

96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | eventID 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    Undocumented

    115 | 116 |
    117 |
    118 |

    Declaration

    119 |
    120 |

    Swift

    121 |
    public let eventID: String
    122 | 123 |
    124 |
    125 |
    126 |
    127 |
  • 128 |
129 |
130 |
131 |
132 | 136 |
137 |
138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/Structs/CreateEventResponse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CreateEventResponse Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

CreateEventResponse

86 |
87 |
88 |
public struct CreateEventResponse : Decodable
89 | 90 |
91 |
92 |

Represents the response of a creating event request.

93 | 94 |
95 |
96 |
97 |
    98 |
  • 99 |
    100 | 101 | 102 | 103 | eventID 104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |

    The event ID of the created event.

    112 | 113 |
    114 |
    115 |

    Declaration

    116 |
    117 |

    Swift

    118 |
    public let eventID: String
    119 | 120 |
    121 |
    122 |
    123 |
    124 |
  • 125 |
126 |
127 |
128 |
129 | 133 |
134 |
135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/Structs/EventResponse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EventResponse Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (96% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 85 |
86 |
87 |
88 |

EventResponse

89 |
90 |
91 |
public struct EventResponse: Decodable
92 | 93 |
94 |
95 |

Response structure for an event-creation request.

96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | eventID 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    Undocumented

    115 | 116 |
    117 |
    118 |

    Declaration

    119 |
    120 |

    Swift

    121 |
    public let eventID: String
    122 | 123 |
    124 |
    125 |
    126 |
    127 |
  • 128 |
129 |
130 |
131 |
132 | 136 |
137 |
138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /docs/Enums/PIOError/SerializationFailureReason.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SerializationFailureReason Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

SerializationFailureReason

86 |
87 |
88 |
public enum SerializationFailureReason
89 | 90 |
91 |
92 |

The underlying reason the serialization fails.

93 | 94 |
95 |
96 |
97 |
    98 |
  • 99 |
    100 | 101 | 102 | 103 | failed(error:) 104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |

    Failure due to some error.

    112 | 113 |
    114 |
    115 |

    Declaration

    116 |
    117 |

    Swift

    118 |
    case failed(error: Error)
    119 | 120 |
    121 |
    122 |
    123 |
    124 |
  • 125 |
126 |
127 |
128 |
129 | 133 |
134 |
135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/Enums/PIOError/SerializationFailureReason.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SerializationFailureReason Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

SerializationFailureReason

86 |
87 |
88 |
public enum SerializationFailureReason
89 | 90 |
91 |
92 |

The underlying reason the serialization fails.

93 | 94 |
95 |
96 |
97 |
    98 |
  • 99 |
    100 | 101 | 102 | 103 | failed(error:) 104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |

    Failure due to some error.

    112 | 113 |
    114 |
    115 |

    Declaration

    116 |
    117 |

    Swift

    118 |
    case failed(error: Error)
    119 | 120 |
    121 |
    122 |
    123 |
    124 |
  • 125 |
126 |
127 |
128 |
129 | 133 |
134 |
135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /PredictionIO/Tests/EventTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventTests.swift 3 | // PredictionIOTests 4 | // 5 | // Created by Minh Tu Le on 1/2/18. 6 | // Copyright © 2018 PredictionIO. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PredictionIO 11 | 12 | // swiftlint:disable force_cast force_try 13 | class EventTests: XCTestCase { 14 | 15 | func testInit() { 16 | let event = Event( 17 | event: "rate", 18 | entityType: "customer", 19 | entityID: "c1", 20 | targetEntity: (type: "book", id: "b1"), 21 | properties: ["rating": 5], 22 | eventTime: Date(timeIntervalSince1970: 0) 23 | ) 24 | 25 | XCTAssertEqual(event.event, "rate") 26 | XCTAssertEqual(event.entityType, "customer") 27 | XCTAssertEqual(event.entityID, "c1") 28 | XCTAssertEqual(event.targetEntityType!, "book") 29 | XCTAssertEqual(event.targetEntityID!, "b1") 30 | XCTAssert(event.properties!["rating"] as? Int == 5) 31 | XCTAssertEqual(event.eventTime, Date(timeIntervalSince1970: 0)) 32 | } 33 | 34 | func testInit_withoutOptionalParameters() { 35 | let event = Event( 36 | event: "create", 37 | entityType: "customer", 38 | entityID: "c1" 39 | ) 40 | 41 | XCTAssertEqual(event.event, "create") 42 | XCTAssertEqual(event.entityType, "customer") 43 | XCTAssertEqual(event.entityID, "c1") 44 | XCTAssertNil(event.targetEntityType) 45 | XCTAssertNil(event.targetEntityID) 46 | XCTAssertNil(event.properties) 47 | XCTAssertNotNil(event.eventTime) 48 | } 49 | 50 | func testInitWithJSON() { 51 | let json: [String: Any] = [ 52 | "event": "rate", 53 | "entityType": "customer", 54 | "entityId": "c1", 55 | "targetEntityType": "book", 56 | "targetEntityId": "b1", 57 | "properties": [ 58 | "rating": 5, 59 | "others": ["foo", "bar"] 60 | ], 61 | "eventId": "fake_id", 62 | "eventTime": "2018-01-13T21:39:45.618Z" 63 | ] 64 | let eventTime = Event.dateTimeFormatter.date(from: json["eventTime"] as! String)! 65 | let event = try! Event(json: json) 66 | 67 | XCTAssertEqual(event.event, json["event"] as! String) 68 | XCTAssertEqual(event.entityType, json["entityType"] as! String) 69 | XCTAssertEqual(event.entityID, json["entityId"] as! String) 70 | XCTAssertEqual(event.targetEntityType!, json["targetEntityType"] as! String) 71 | XCTAssertEqual(event.targetEntityID!, json["targetEntityId"] as! String) 72 | XCTAssertTrue(isEqualJSON(event.properties!, json["properties"] as! [String: Any])) 73 | XCTAssertEqual(event.eventID!, json["eventId"] as! String) 74 | XCTAssertEqual(event.eventTime, eventTime) 75 | } 76 | 77 | func testInitiWithJSON_missingEventID_throwsSerializationError() { 78 | let json: [String: Any] = [ 79 | "event": "rate", 80 | "entityType": "customer", 81 | "entityId": "c1", 82 | "eventTime": "2018-01-13T21:39:45.618Z" 83 | ] 84 | 85 | XCTAssertThrowsError(try Event(json: json)) { error in 86 | XCTAssertTrue((error as! PIOError).isDeserializingMissingField("eventId")) 87 | } 88 | } 89 | 90 | func testInitiWithJSON_invalidJSONProperties_throwsSerializationError() { 91 | let json: [String: Any] = [ 92 | "event": "rate", 93 | "entityType": "customer", 94 | "entityId": "c1", 95 | "properties": [ 96 | "foo": [ 97 | 1: "this is not a valid JSON" 98 | ] 99 | ], 100 | "eventId": "fake_id", 101 | "eventTime": "2018-01-13T21:39:45.618Z" 102 | ] 103 | 104 | XCTAssertThrowsError(try Event(json: json)) { error in 105 | XCTAssertTrue((error as! PIOError).isDeserializingInvalidField("properties")) 106 | } 107 | } 108 | 109 | func testJSON() { 110 | let event = Event( 111 | event: "rate", 112 | entityType: "customer", 113 | entityID: "c1", 114 | targetEntity: (type: "book", id: "b1"), 115 | properties: [ 116 | "ratings": [5, 4] 117 | ], 118 | eventTime: Date(timeIntervalSince1970: 0) 119 | ) 120 | 121 | let expectedJSON: [String: Any] = [ 122 | "event": "rate", 123 | "entityType": "customer", 124 | "entityId": "c1", 125 | "targetEntityType": "book", 126 | "targetEntityId": "b1", 127 | "properties": [ 128 | "ratings": [5, 4] 129 | ], 130 | "eventTime": "1970-01-01T00:00:00.000Z" 131 | ] 132 | 133 | XCTAssertTrue(isEqualJSON(event.json, expectedJSON)) 134 | } 135 | 136 | func testValidate_invalidJSONProperties_throwsInvalidEventError() { 137 | let event = Event( 138 | event: "rate", 139 | entityType: "customer", 140 | entityID: "c1", 141 | properties: [ 142 | "ratings": [ 143 | 3: "this is an invalid json" 144 | ] 145 | ] 146 | ) 147 | 148 | let error = event.validate()! as! PIOError 149 | XCTAssertTrue(error.isInvalidJSONProperties()) 150 | } 151 | 152 | func testValidate_unsetEventWithEmptyProperties_throwsInvalidEventError() { 153 | let event = Event( 154 | event: Event.unsetEvent, 155 | entityType: "customer", 156 | entityID: "c1", 157 | properties: [:] 158 | ) 159 | 160 | let error = event.validate()! as! PIOError 161 | XCTAssertTrue(error.isUnsetEventWithEmptyProperties()) 162 | } 163 | 164 | private func isEqualJSON(_ left: Any, _ right: Any) -> Bool { 165 | switch (left, right) { 166 | // Dictionary 167 | case let (leftDict as [String: Any], rightDict as [String: Any]): 168 | if leftDict.count != rightDict.count { 169 | return false 170 | } 171 | 172 | for (key, leftValue) in leftDict { 173 | if rightDict[key] == nil || !isEqualJSON(leftValue, rightDict[key]!) { 174 | return false 175 | } 176 | } 177 | 178 | return true 179 | // Array 180 | case let (leftArray as [Any], rightArray as [Any]): 181 | if leftArray.count != rightArray.count { 182 | return false 183 | } 184 | 185 | for (index, leftElement) in leftArray.enumerated() { 186 | if !isEqualJSON(leftElement, rightArray[index]) { 187 | return false 188 | } 189 | } 190 | 191 | return true 192 | // Bool 193 | case let (leftBool as Bool, rightBool as Bool): 194 | return leftBool == rightBool 195 | // String 196 | case let (leftString as String, rightString as String): 197 | return leftString == rightString 198 | // Number 199 | case let (leftNumber as NSNumber, rightNumber as NSNumber): 200 | return leftNumber == rightNumber 201 | // Otherwise 202 | default: 203 | return false 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /docs/Structs/CreateBatchEventsResponse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CreateBatchEventsResponse Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

CreateBatchEventsResponse

86 |
87 |
88 |
public struct CreateBatchEventsResponse : Decodable
89 | 90 |
91 |
92 |

Represents the response of a creating batch events request.

93 | 94 |
95 |
96 |
97 |
    98 |
  • 99 |
    100 | 101 | 102 | 103 | statuses 104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |

    The statuses each of which presents whether an event was successfully 112 | created by the server.

    113 | 114 |

    If an event was successfuly created by the server, its status is a 115 | Result.success which value contains its individual CreateEventResponse. 116 | Otherwise, the event’s status is a Result.failure. The statuses are 117 | returned in the same order of their corresponding events sent in the 118 | request.

    119 | 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    public let statuses: [Result<CreateEventResponse, PIOError>]
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
133 |
134 |
135 |
136 | 140 |
141 |
142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/Structs/CreateBatchEventsResponse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CreateBatchEventsResponse Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

CreateBatchEventsResponse

86 |
87 |
88 |
public struct CreateBatchEventsResponse : Decodable
89 | 90 |
91 |
92 |

Represents the response of a creating batch events request.

93 | 94 |
95 |
96 |
97 |
    98 |
  • 99 |
    100 | 101 | 102 | 103 | statuses 104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |

    The statuses each of which presents whether an event was successfully 112 | created by the server.

    113 | 114 |

    If an event was successfuly created by the server, its status is a 115 | Result.success which value contains its individual CreateEventResponse. 116 | Otherwise, the event’s status is a Result.failure. The statuses are 117 | returned in the same order of their corresponding events sent in the 118 | request.

    119 | 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    public let statuses: [Result<CreateEventResponse, PIOError>]
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
133 |
134 |
135 |
136 | 140 |
141 |
142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /PredictionIO/Source/Event.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Event.swift 3 | // PredictionIO 4 | // 5 | // Created by Minh Tu Le on 1/6/18. 6 | // Copyright © 2018 PredictionIO. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// The `Event` struct represents an event dictionary in the REST API 12 | /// to a PredictionIO's event server. 13 | public struct Event { 14 | // MARK: - Constants 15 | 16 | /// Reversed set event name. 17 | public static let setEvent = "$set" 18 | 19 | /// Reversed unset event name. 20 | public static let unsetEvent = "$unset" 21 | 22 | /// Reversed delete event name. 23 | public static let deleteEvent = "$delete" 24 | 25 | /// Predefined user entity type. 26 | public static let userEntityType = "user" 27 | 28 | /// Predefined item entity type. 29 | public static let itemEntityType = "item" 30 | 31 | // MARK: - Properties 32 | 33 | /// The event name e.g. "sign-up", "rate", "view". 34 | /// 35 | /// **Note:** All event names starting with "$" and "pio_" are reversed 36 | /// and shouldn't be used as your custom event (e.g. "$set"). 37 | public let event: String 38 | 39 | /// The entity type. It is the namespace of the `entityID` and analogous 40 | /// to the table name of a relational database. The `entityID` must be 41 | /// unique within the same `entityType`. 42 | /// 43 | /// **Note:** All entityType names starting with "$" and "pio_" are reversed 44 | /// and shouldn't be used. 45 | public let entityType: String 46 | 47 | /// The entity ID. `entityType-entityID` becomes the unique identifier 48 | /// of the entity. 49 | public let entityID: String 50 | 51 | /// The target entity type. 52 | /// 53 | /// **Note:** All targetEntityType names starting with "$" and "pio_" are reversed 54 | /// and shouldn't be used. 55 | public let targetEntityType: String? 56 | 57 | /// The target entity ID. 58 | public let targetEntityID: String? 59 | 60 | /// The event properties. It should be a valid JSON dictionary. 61 | /// 62 | /// **Note:** All properties names starting with "$" and "pio_" are reversed 63 | /// and shouldn't be used. 64 | public let properties: [String: Any]? 65 | 66 | /// The time of the event. 67 | public let eventTime: Date 68 | 69 | /// The event ID. This value should only be set by the server. 70 | public let eventID: String? 71 | 72 | // MARK: - Initialization 73 | 74 | /// Creates an event struct. 75 | /// 76 | /// - parameter event: The event name. 77 | /// - parameter entityType: The entity type. 78 | /// - parameter entityID: The entity ID. 79 | /// - parameter targetEntity: The target entity (type, ID) tuple. `nil` by default. 80 | /// - parameter properties: The event properties in JSON dictionary. `nil` by default. 81 | /// - parameter eventTime: The event time. Current local time by default. 82 | /// 83 | /// - returns: The new `Event` instance. 84 | public init( 85 | event: String, 86 | entityType: String, 87 | entityID: String, 88 | targetEntity: (type: String, id: String)? = nil, 89 | properties: [String: Any]? = nil, 90 | eventTime: Date = Date() 91 | ) { 92 | self.event = event 93 | self.entityType = entityType 94 | self.entityID = entityID 95 | 96 | if let targetEntity = targetEntity { 97 | self.targetEntityType = targetEntity.type 98 | self.targetEntityID = targetEntity.id 99 | } else { 100 | self.targetEntityType = nil 101 | self.targetEntityID = nil 102 | } 103 | 104 | self.properties = properties 105 | self.eventTime = eventTime 106 | self.eventID = nil 107 | } 108 | 109 | // MARK: - Validation 110 | 111 | /// Validates an event against the following rules: 112 | /// - `properties` must be a valid JSON dictionary. 113 | /// - An `$unset` event must not have an empty or nil `properties`. 114 | /// 115 | /// - returns: A `PIOError.invalidEvent` error if the validation fails. Otherwise, returns nil. 116 | public func validate() -> Error? { 117 | if let properties = properties, !JSONSerialization.isValidJSONObject(properties) { 118 | return PIOError.InvalidEventReason.invalidJSONPropertiesError() 119 | } 120 | 121 | if event == Event.unsetEvent && (properties == nil || properties!.isEmpty == true) { 122 | return PIOError.InvalidEventReason.unsetEventWithEmptyPropertiesError() 123 | } 124 | 125 | return nil 126 | } 127 | } 128 | 129 | // MARK: - 130 | 131 | extension Event { 132 | // MARK: - JSON Serialization and Deserialization 133 | 134 | /// Creates an event from a JSON dictionary. 135 | /// 136 | /// - parameters json: The JSON dictionary. 137 | /// 138 | /// - throws: A `PIOError.failedDeserialization` error if deserialization fails. 139 | /// 140 | /// - returns: The new Event instance. 141 | public init(json: [String: Any]) throws { 142 | guard let event = json["event"] as? String else { 143 | throw PIOError.DeserializationFailureReason.missingFieldError(field: "event") 144 | } 145 | 146 | guard let eventID = json["eventId"] as? String else { 147 | throw PIOError.DeserializationFailureReason.missingFieldError(field: "eventId") 148 | } 149 | 150 | guard let entityType = json["entityType"] as? String else { 151 | throw PIOError.DeserializationFailureReason.missingFieldError(field: "entityType") 152 | } 153 | 154 | guard let entityID = json["entityId"] as? String else { 155 | throw PIOError.DeserializationFailureReason.missingFieldError(field: "entityId") 156 | } 157 | 158 | guard let eventTimeString = json["eventTime"] as? String else { 159 | throw PIOError.DeserializationFailureReason.missingFieldError(field: "eventTime") 160 | } 161 | 162 | guard let eventTime = Event.dateTimeFormatter.date(from: eventTimeString) else { 163 | throw PIOError.DeserializationFailureReason.missingFieldError(field: "eventTime") 164 | } 165 | 166 | let properties = json["properties"] as? [String: Any] 167 | 168 | if let properties = properties, !JSONSerialization.isValidJSONObject(properties) { 169 | throw PIOError.DeserializationFailureReason.invalidFieldError(field: "properties", value: properties) 170 | } 171 | 172 | self.event = event 173 | self.entityType = entityType 174 | self.entityID = entityID 175 | 176 | if let targetEntityType = json["targetEntityType"] as? String, 177 | let targetEntityID = json["targetEntityId"] as? String { 178 | self.targetEntityType = targetEntityType 179 | self.targetEntityID = targetEntityID 180 | } else { 181 | self.targetEntityType = nil 182 | self.targetEntityID = nil 183 | } 184 | 185 | self.properties = properties 186 | self.eventTime = eventTime 187 | self.eventID = eventID 188 | } 189 | 190 | /// Returns a JSON dictionary representing the event. 191 | /// 192 | /// - returns: The JSON dictionary. 193 | public var json: [String: Any] { 194 | var json: [String: Any] = [ 195 | "event": event, 196 | "entityType": entityType, 197 | "entityId": entityID, 198 | "eventTime": Event.dateTimeFormatter.string(from: eventTime) 199 | ] 200 | 201 | if let targetEntityType = targetEntityType, let targetEntityID = targetEntityID { 202 | json["targetEntityType"] = targetEntityType 203 | json["targetEntityId"] = targetEntityID 204 | } 205 | 206 | if let properties = properties { 207 | json["properties"] = properties 208 | } 209 | 210 | return json 211 | } 212 | 213 | static let dateTimeFormatter: DateFormatter = { 214 | let dateFormatter = DateFormatter() 215 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 216 | dateFormatter.timeZone = TimeZone(abbreviation: "UTC") 217 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" 218 | return dateFormatter 219 | }() 220 | } 221 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token, .item .direct-link { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; 223 | transition: all 300ms; } 224 | .item .token-open { 225 | margin-left: 0px; } 226 | .item .discouraged { 227 | text-decoration: line-through; } 228 | .item .declaration-note { 229 | font-size: .85em; 230 | color: gray; 231 | font-style: italic; } 232 | 233 | .pointer-container { 234 | border-bottom: 1px solid #e2e2e2; 235 | left: -23px; 236 | padding-bottom: 13px; 237 | position: relative; 238 | width: 110%; } 239 | 240 | .pointer { 241 | background: #f9f9f9; 242 | border-left: 1px solid #e2e2e2; 243 | border-top: 1px solid #e2e2e2; 244 | height: 12px; 245 | left: 21px; 246 | top: -7px; 247 | -webkit-transform: rotate(45deg); 248 | -moz-transform: rotate(45deg); 249 | -o-transform: rotate(45deg); 250 | transform: rotate(45deg); 251 | position: absolute; 252 | width: 12px; } 253 | 254 | .height-container { 255 | display: none; 256 | left: -25px; 257 | padding: 0 25px; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #f9f9f9; 263 | border-bottom: 1px solid #e2e2e2; 264 | left: -25px; 265 | position: relative; 266 | width: 100%; 267 | padding-top: 10px; 268 | padding-bottom: 5px; } 269 | 270 | .aside, .language { 271 | padding: 6px 12px; 272 | margin: 12px 0; 273 | border-left: 5px solid #dddddd; 274 | overflow-y: hidden; } 275 | .aside .aside-title, .language .aside-title { 276 | font-size: 9px; 277 | letter-spacing: 2px; 278 | text-transform: uppercase; 279 | padding-bottom: 0; 280 | margin: 0; 281 | color: #aaa; 282 | -webkit-user-select: none; } 283 | .aside p:last-child, .language p:last-child { 284 | margin-bottom: 0; } 285 | 286 | .language { 287 | border-left: 5px solid #cde9f4; } 288 | .language .aside-title { 289 | color: #4b8afb; } 290 | 291 | .aside-warning, .aside-deprecated, .aside-unavailable { 292 | border-left: 5px solid #ff6666; } 293 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 294 | color: #ff0000; } 295 | 296 | .graybox { 297 | border-collapse: collapse; 298 | width: 100%; } 299 | .graybox p { 300 | margin: 0; 301 | word-break: break-word; 302 | min-width: 50px; } 303 | .graybox td { 304 | border: 1px solid #e2e2e2; 305 | padding: 5px 25px 5px 10px; 306 | vertical-align: middle; } 307 | .graybox tr td:first-of-type { 308 | text-align: right; 309 | padding: 7px; 310 | vertical-align: top; 311 | word-break: normal; 312 | width: 40px; } 313 | 314 | .slightly-smaller { 315 | font-size: 0.9em; } 316 | 317 | #footer { 318 | position: relative; 319 | top: 10px; 320 | bottom: 0px; 321 | margin-left: 25px; } 322 | #footer p { 323 | margin: 0; 324 | color: #aaa; 325 | font-size: 0.8em; } 326 | 327 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 328 | display: none; } 329 | 330 | html.dash .main-content { 331 | width: 980px; 332 | margin-left: 0; 333 | border: none; 334 | width: 100%; 335 | top: 0; 336 | padding-bottom: 0; } 337 | 338 | html.dash .height-container { 339 | display: block; } 340 | 341 | html.dash .item .token { 342 | margin-left: 0; } 343 | 344 | html.dash .content-wrapper { 345 | width: auto; } 346 | 347 | html.dash #footer { 348 | position: static; } 349 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token, .item .direct-link { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; 223 | transition: all 300ms; } 224 | .item .token-open { 225 | margin-left: 0px; } 226 | .item .discouraged { 227 | text-decoration: line-through; } 228 | .item .declaration-note { 229 | font-size: .85em; 230 | color: gray; 231 | font-style: italic; } 232 | 233 | .pointer-container { 234 | border-bottom: 1px solid #e2e2e2; 235 | left: -23px; 236 | padding-bottom: 13px; 237 | position: relative; 238 | width: 110%; } 239 | 240 | .pointer { 241 | background: #f9f9f9; 242 | border-left: 1px solid #e2e2e2; 243 | border-top: 1px solid #e2e2e2; 244 | height: 12px; 245 | left: 21px; 246 | top: -7px; 247 | -webkit-transform: rotate(45deg); 248 | -moz-transform: rotate(45deg); 249 | -o-transform: rotate(45deg); 250 | transform: rotate(45deg); 251 | position: absolute; 252 | width: 12px; } 253 | 254 | .height-container { 255 | display: none; 256 | left: -25px; 257 | padding: 0 25px; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #f9f9f9; 263 | border-bottom: 1px solid #e2e2e2; 264 | left: -25px; 265 | position: relative; 266 | width: 100%; 267 | padding-top: 10px; 268 | padding-bottom: 5px; } 269 | 270 | .aside, .language { 271 | padding: 6px 12px; 272 | margin: 12px 0; 273 | border-left: 5px solid #dddddd; 274 | overflow-y: hidden; } 275 | .aside .aside-title, .language .aside-title { 276 | font-size: 9px; 277 | letter-spacing: 2px; 278 | text-transform: uppercase; 279 | padding-bottom: 0; 280 | margin: 0; 281 | color: #aaa; 282 | -webkit-user-select: none; } 283 | .aside p:last-child, .language p:last-child { 284 | margin-bottom: 0; } 285 | 286 | .language { 287 | border-left: 5px solid #cde9f4; } 288 | .language .aside-title { 289 | color: #4b8afb; } 290 | 291 | .aside-warning, .aside-deprecated, .aside-unavailable { 292 | border-left: 5px solid #ff6666; } 293 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 294 | color: #ff0000; } 295 | 296 | .graybox { 297 | border-collapse: collapse; 298 | width: 100%; } 299 | .graybox p { 300 | margin: 0; 301 | word-break: break-word; 302 | min-width: 50px; } 303 | .graybox td { 304 | border: 1px solid #e2e2e2; 305 | padding: 5px 25px 5px 10px; 306 | vertical-align: middle; } 307 | .graybox tr td:first-of-type { 308 | text-align: right; 309 | padding: 7px; 310 | vertical-align: top; 311 | word-break: normal; 312 | width: 40px; } 313 | 314 | .slightly-smaller { 315 | font-size: 0.9em; } 316 | 317 | #footer { 318 | position: relative; 319 | top: 10px; 320 | bottom: 0px; 321 | margin-left: 25px; } 322 | #footer p { 323 | margin: 0; 324 | color: #aaa; 325 | font-size: 0.8em; } 326 | 327 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 328 | display: none; } 329 | 330 | html.dash .main-content { 331 | width: 980px; 332 | margin-left: 0; 333 | border: none; 334 | width: 100%; 335 | top: 0; 336 | padding-bottom: 0; } 337 | 338 | html.dash .height-container { 339 | display: block; } 340 | 341 | html.dash .item .token { 342 | margin-left: 0; } 343 | 344 | html.dash .content-wrapper { 345 | width: auto; } 346 | 347 | html.dash #footer { 348 | position: static; } 349 | -------------------------------------------------------------------------------- /PredictionIO/Tests/EventClientTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventClientTests.swift 3 | // PredictionIOTests 4 | // 5 | // Created by Minh Tu Le on 1/5/18. 6 | // Copyright © 2018 PredictionIO. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import PredictionIO 11 | 12 | class EventClientTests: XCTestCase { 13 | let accessKey = "123" // Replace with the real app's access key if testing using actual PredictionIO localhost setup. 14 | var eventClient: EventClient! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | eventClient = EventClient(accessKey: accessKey, baseURL: "http://localhost:7070") 20 | } 21 | 22 | func testCreateEvent() { 23 | let event = Event(event: "register", entityType: "user", entityID: "foo") 24 | let expectation = self.expectation(description: "Creating an event") 25 | 26 | eventClient.createEvent(event) { result in 27 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 28 | 29 | expectation.fulfill() 30 | } 31 | 32 | waitForExpectations(timeout: 5) { error in 33 | XCTAssertNil(error, "\(error!)") 34 | } 35 | } 36 | 37 | func testCreateBatchEvents() { 38 | let events = [ 39 | Event(event: "register", entityType: "user", entityID: "foo1"), 40 | Event(event: "register", entityType: "user", entityID: "foo2"), 41 | Event(event: "register", entityType: "user", entityID: "foo3") 42 | ] 43 | let expectation = self.expectation(description: "Creating batch events") 44 | 45 | eventClient.createBatchEvents(events) { result in 46 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 47 | let response = result.value! 48 | XCTAssertEqual(response.statuses.count, 3) 49 | 50 | for case .failure in response.statuses { 51 | XCTFail("There should be any failure here.") 52 | } 53 | 54 | expectation.fulfill() 55 | } 56 | 57 | waitForExpectations(timeout: 5) { error in 58 | XCTAssertNil(error, "\(error!)") 59 | } 60 | } 61 | 62 | func testGetEvent() { 63 | let event = Event(event: "register", entityType: "user", entityID: "foo") 64 | let eventID = createEvent(event) 65 | 66 | let getEventExpectation = self.expectation(description: "Getting an event") 67 | eventClient.getEvent(eventID: eventID) { result in 68 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 69 | let createdEvent = result.value! 70 | XCTAssertEqual(event.event, createdEvent.event) 71 | 72 | getEventExpectation.fulfill() 73 | } 74 | 75 | waitForExpectations(timeout: 5) 76 | } 77 | 78 | func testGetEvents() { 79 | let randomType = "random" 80 | let randomId = "\(arc4random())" 81 | let events = [ 82 | Event(event: "register", entityType: randomType, entityID: randomId), 83 | Event(event: "register", entityType: randomType, entityID: randomId), 84 | Event(event: "register", entityType: "book", entityID: "math") 85 | ] 86 | events.forEach { createEvent($0) } 87 | 88 | let getEventsExpectation = self.expectation(description: "Getting events in event server") 89 | eventClient.getEvents(entityType: randomType, entityID: randomId) { result in 90 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 91 | let events = result.value! 92 | XCTAssertEqual(events.count, 2) 93 | 94 | getEventsExpectation.fulfill() 95 | } 96 | 97 | waitForExpectations(timeout: 5) 98 | } 99 | 100 | func testDeleteEvent() { 101 | let event = Event(event: "register", entityType: "user", entityID: "foo") 102 | let eventID = createEvent(event) 103 | 104 | let testDoneExpectation = self.expectation(description: "Deleting an event") 105 | eventClient.deleteEvent(eventID: eventID) { error in 106 | XCTAssertNil(error, "Request should succeed, got \(error!)") 107 | 108 | // Verify that the event no longer exists. 109 | self.eventClient.getEvent(eventID: eventID) { result in 110 | XCTAssertTrue(result.isFailure) 111 | 112 | testDoneExpectation.fulfill() 113 | } 114 | } 115 | 116 | waitForExpectations(timeout: 5) 117 | } 118 | 119 | // MARK: Convenient methods for user entity 120 | 121 | func testSetUser() { 122 | let expectation = self.expectation(description: "Setting properties of a user") 123 | 124 | eventClient.setUser(userID: "u1", properties: ["p1": "foo", "p2": "bar"]) { result in 125 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 126 | 127 | expectation.fulfill() 128 | } 129 | 130 | waitForExpectations(timeout: 5) { error in 131 | XCTAssertNil(error, "\(error!)") 132 | } 133 | } 134 | 135 | func testUnsetUser() { 136 | let expectation = self.expectation(description: "Unsetting properties of a user") 137 | 138 | eventClient.unsetUser(userID: "u2", properties: ["p1": NSNull()]) { result in 139 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 140 | 141 | expectation.fulfill() 142 | } 143 | 144 | waitForExpectations(timeout: 5) { error in 145 | XCTAssertNil(error, "\(error!)") 146 | } 147 | } 148 | 149 | func testDeleteUser() { 150 | let expectation = self.expectation(description: "Unsetting properties of a user") 151 | 152 | eventClient.deleteUser(userID: "u4") { result in 153 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 154 | 155 | expectation.fulfill() 156 | } 157 | 158 | waitForExpectations(timeout: 5) { error in 159 | XCTAssertNil(error, "\(error!)") 160 | } 161 | } 162 | 163 | // MARK: Convenient methods for item entity 164 | 165 | func testSetItem() { 166 | let expectation = self.expectation(description: "Setting properties of an item") 167 | 168 | eventClient.setItem(itemID: "i1", properties: ["p1": "foo", "p2": "bar"]) { result in 169 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 170 | 171 | expectation.fulfill() 172 | } 173 | 174 | waitForExpectations(timeout: 5) { error in 175 | XCTAssertNil(error, "\(error!)") 176 | } 177 | } 178 | 179 | func testUnsetItem() { 180 | let expectation = self.expectation(description: "Unsetting properties of an item") 181 | 182 | eventClient.unsetItem(itemID: "i2", properties: ["p1": NSNull()]) { result in 183 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 184 | 185 | expectation.fulfill() 186 | } 187 | 188 | waitForExpectations(timeout: 5) { error in 189 | XCTAssertNil(error, "\(error!)") 190 | } 191 | } 192 | 193 | func testDeleteItem() { 194 | let expectation = self.expectation(description: "Unsetting properties of an item") 195 | 196 | eventClient.deleteItem(itemID: "i4") { result in 197 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 198 | 199 | expectation.fulfill() 200 | } 201 | 202 | waitForExpectations(timeout: 5) { error in 203 | XCTAssertNil(error, "\(error!)") 204 | } 205 | } 206 | 207 | // MARK: Convenient methods for user action on item 208 | 209 | func testRecordAction() { 210 | let expectation = self.expectation(description: "Record user action on item") 211 | 212 | eventClient.recordAction("rate", byUserID: "u1", onItemID: "i1", properties: ["rating": 5]) { result in 213 | XCTAssertTrue(result.isSuccess, "Request should succeed, got \(result.error!)") 214 | 215 | expectation.fulfill() 216 | } 217 | 218 | waitForExpectations(timeout: 5) { error in 219 | XCTAssertNil(error, "\(error!)") 220 | } 221 | } 222 | 223 | // MARK: Helpers 224 | 225 | @discardableResult 226 | private func createEvent(_ event: Event) -> String { 227 | let createEventExpectation = self.expectation(description: "Creating an event") 228 | var eventID: String = "" 229 | 230 | eventClient.createEvent(event) { result in 231 | eventID = result.value!.eventID 232 | createEventExpectation.fulfill() 233 | } 234 | 235 | waitForExpectations(timeout: 5) 236 | return eventID 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

Classes

86 |

The following classes are available globally.

87 | 88 |
89 |
90 |
91 |
    92 |
  • 93 |
    94 | 95 | 96 | 97 | BaseClient 98 | 99 |
    100 |
    101 |
    102 |
    103 |
    104 |
    105 |

    Manages network connections with the server.

    106 | 107 |
    108 |
    109 |

    Declaration

    110 |
    111 |

    Swift

    112 |
    public class BaseClient
    113 | 114 |
    115 |
    116 |
    117 |
    118 |
  • 119 |
120 |
121 |
122 |
    123 |
  • 124 |
    125 | 126 | 127 | 128 | EngineClient 129 | 130 |
    131 |
    132 |
    133 |
    134 |
    135 |
    136 |

    Responsible for retrieving prediction results from a PredictionIO Engine Server.

    137 | 138 | See more 139 |
    140 |
    141 |

    Declaration

    142 |
    143 |

    Swift

    144 |
    public class EngineClient : BaseClient
    145 | 146 |
    147 |
    148 |
    149 |
    150 |
  • 151 |
152 |
153 |
154 |
    155 |
  • 156 |
    157 | 158 | 159 | 160 | EventClient 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    Responsible for sending data to a PredictionIO Event Server.

    169 | 170 | See more 171 |
    172 |
    173 |

    Declaration

    174 |
    175 |

    Swift

    176 |
    public class EventClient : BaseClient
    177 | 178 |
    179 |
    180 |
    181 |
    182 |
  • 183 |
184 |
185 |
186 |
187 | 191 |
192 |
193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/docsets/PredictionIO.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

Classes

86 |

The following classes are available globally.

87 | 88 |
89 |
90 |
91 |
    92 |
  • 93 |
    94 | 95 | 96 | 97 | BaseClient 98 | 99 |
    100 |
    101 |
    102 |
    103 |
    104 |
    105 |

    Manages network connections with the server.

    106 | 107 |
    108 |
    109 |

    Declaration

    110 |
    111 |

    Swift

    112 |
    public class BaseClient
    113 | 114 |
    115 |
    116 |
    117 |
    118 |
  • 119 |
120 |
121 |
122 |
    123 |
  • 124 |
    125 | 126 | 127 | 128 | EngineClient 129 | 130 |
    131 |
    132 |
    133 |
    134 |
    135 |
    136 |

    Responsible for retrieving prediction results from a PredictionIO Engine Server.

    137 | 138 | See more 139 |
    140 |
    141 |

    Declaration

    142 |
    143 |

    Swift

    144 |
    public class EngineClient : BaseClient
    145 | 146 |
    147 |
    148 |
    149 |
    150 |
  • 151 |
152 |
153 |
154 |
    155 |
  • 156 |
    157 | 158 | 159 | 160 | EventClient 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    Responsible for sending data to a PredictionIO Event Server.

    169 | 170 | See more 171 |
    172 |
    173 |

    Declaration

    174 |
    175 |

    Swift

    176 |
    public class EventClient : BaseClient
    177 | 178 |
    179 |
    180 |
    181 |
    182 |
  • 183 |
184 |
185 |
186 |
187 | 191 |
192 |
193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

PredictionIO Docs (100% documented)

18 |

View on GitHub

19 |

Install in Dash

20 |
21 |
22 |
23 | 28 |
29 |
30 | 82 |
83 |
84 |
85 |

Structures

86 |

The following structures are available globally.

87 | 88 |
89 |
90 |
91 |
    92 |
  • 93 |
    94 | 95 | 96 | 97 | Event 98 | 99 |
    100 |
    101 |
    102 |
    103 |
    104 |
    105 |

    The Event struct represents an event dictionary in the REST API 106 | to a PredictionIO’s event server.

    107 | 108 | See more 109 |
    110 |
    111 |

    Declaration

    112 |
    113 |

    Swift

    114 |
    public struct Event
    115 | 116 |
    117 |
    118 |
    119 |
    120 |
  • 121 |
122 |
123 |
124 |
125 | 126 | 127 | 128 |

Responses

129 |
130 |
131 |
    132 |
  • 133 |
    134 | 135 | 136 | 137 | CreateEventResponse 138 | 139 |
    140 |
    141 |
    142 |
    143 |
    144 |
    145 |

    Represents the response of a creating event request.

    146 | 147 | See more 148 |
    149 |
    150 |

    Declaration

    151 |
    152 |

    Swift

    153 |
    public struct CreateEventResponse : Decodable
    154 | 155 |
    156 |
    157 |
    158 |
    159 |
  • 160 |
  • 161 |
    162 | 163 | 164 | 165 | CreateBatchEventsResponse 166 | 167 |
    168 |
    169 |
    170 |
    171 |
    172 |
    173 |

    Represents the response of a creating batch events request.

    174 | 175 | See more 176 |
    177 |
    178 |

    Declaration

    179 |
    180 |

    Swift

    181 |
    public struct CreateBatchEventsResponse : Decodable
    182 | 183 |
    184 |
    185 |
    186 |
    187 |
  • 188 |
189 |
190 |
191 |
192 | 196 |
197 |
198 | 199 | 200 | 201 | --------------------------------------------------------------------------------