├── .circleci └── config.yml ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .jazzy.yaml ├── .swiftlint.yml ├── Examples ├── moltin WatchKit Example Extension │ ├── Assets.xcassets │ │ ├── Complication.complicationset │ │ │ ├── Circular.imageset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Extra Large.imageset │ │ │ │ └── Contents.json │ │ │ ├── Modular.imageset │ │ │ │ └── Contents.json │ │ │ └── Utilitarian.imageset │ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── ExtensionDelegate.swift │ ├── Info.plist │ └── InterfaceController.swift ├── moltin WatchKit Example │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── Interface.storyboard │ └── Info.plist ├── moltin iOS Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── ill-dark.imageset │ │ │ ├── Contents.json │ │ │ ├── ill-dark-1.png │ │ │ ├── ill-dark-2.png │ │ │ └── ill-dark.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CartViewController.swift │ ├── CollectionCell │ │ ├── CategoryCollectionViewCell.swift │ │ ├── CategoryCollectionViewCell.xib │ │ ├── MasterSectionHeaderCollectionReusableView.swift │ │ └── MasterSectionHeaderCollectionReusableView.xib │ ├── DetailViewController.swift │ ├── Extensions │ │ ├── UIColor.swift │ │ └── UIImageView.swift │ ├── Info.plist │ ├── MasterViewController.swift │ ├── Models │ │ ├── CustomProduct.swift │ │ └── ProductCategory.swift │ └── ProductCell │ │ ├── ProductCollectionViewCell.swift │ │ └── ProductCollectionViewCell.xib └── moltin tvOS Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── App Icon & Top Shelf Image.brandassets │ │ ├── App Icon - App Store.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ └── Middle.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── App Icon.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ └── Middle.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Top Shelf Image Wide.imageset │ │ │ └── Contents.json │ │ └── Top Shelf Image.imageset │ │ │ └── Contents.json │ ├── Contents.json │ ├── LaunchImage.launchimage │ │ └── Contents.json │ ├── first.imageset │ │ ├── Contents.json │ │ └── first.pdf │ └── second.imageset │ │ ├── Contents.json │ │ └── second.pdf │ ├── Base.lproj │ └── Main.storyboard │ ├── CartViewController.swift │ ├── CategoriesViewController.swift │ ├── Info.plist │ ├── ProductCategoryCollectionViewCell.swift │ ├── ProductCategoryCollectionViewCell.xib │ ├── ProductCollectionViewCell.swift │ ├── ProductCollectionViewCell.xib │ └── ProductsCollectionViewController.swift ├── LICENSE ├── Moltin.podspec ├── Package.swift ├── README.md ├── Sourcery ├── AutoRequests.swift ├── Requests.stencil └── Tests │ └── RequestsTests.stencil ├── Sources ├── Info-iOS.plist ├── Info-tvOS.plist ├── Info-watchOS.plist ├── SDK │ ├── Models │ │ ├── Address.swift │ │ ├── Brand.swift │ │ ├── Cart.swift │ │ ├── Category.swift │ │ ├── Collection.swift │ │ ├── Currency.swift │ │ ├── Customer.swift │ │ ├── File.swift │ │ ├── Flow.swift │ │ ├── Order.swift │ │ ├── PaymentMethod.swift │ │ ├── Prices.swift │ │ ├── Product.swift │ │ ├── Relationship.swift │ │ └── Timestamps.swift │ ├── Moltin.swift │ ├── Requests │ │ ├── CartRequest.swift │ │ ├── CustomerRequest.swift │ │ ├── FlowRequest.swift │ │ ├── MoltinAuth.swift │ │ ├── MoltinRequest.swift │ │ └── Requests.swift │ └── Utils │ │ ├── CodableExtract.swift │ │ ├── Config.swift │ │ ├── DataSerializer.swift │ │ ├── DateFormatter.swift │ │ ├── Error.swift │ │ ├── HTTP.swift │ │ ├── Pagination.swift │ │ ├── Parser.swift │ │ ├── Query.swift │ │ └── URLSession.swift └── moltin.h ├── Tests ├── moltin iOS Tests │ ├── AuthTests.swift │ ├── CartTests.swift │ ├── Data │ │ ├── Brands.swift │ │ ├── Cart.swift │ │ ├── Category.swift │ │ ├── Collection.swift │ │ ├── Currency.swift │ │ ├── Field.swift │ │ ├── File.swift │ │ └── Products.swift │ ├── FlowTests.swift │ ├── HTTPMock.swift │ ├── Info.plist │ ├── MoltinTests.swift │ ├── PaymentMethodTests.swift │ └── RequestTests.swift └── moltin tvOS Tests │ ├── Info.plist │ └── moltin_tvOS_Tests.swift ├── docs ├── Classes │ ├── Address.html │ ├── AdyenPayment.html │ ├── BraintreeCustomerID.html │ ├── BraintreePaymentNonce.html │ ├── BraintreePaymentToken.html │ ├── Brand.html │ ├── BrandRequest.html │ ├── Cart.html │ ├── CartItem.html │ ├── CartItemMeta.html │ ├── CartItemPrice.html │ ├── CartMeta.html │ ├── CartRequest.html │ ├── Category.html │ ├── CategoryRequest.html │ ├── Collection.html │ ├── CollectionRequest.html │ ├── Currency.html │ ├── CurrencyMeta.html │ ├── CurrencyRequest.html │ ├── CustomCartItem.html │ ├── Customer.html │ ├── CustomerRequest.html │ ├── CustomerToken.html │ ├── Entry.html │ ├── Field.html │ ├── FieldRequest.html │ ├── File.html │ ├── FileMeta.html │ ├── FileRequest.html │ ├── Flow.html │ ├── FlowRequest.html │ ├── ManuallyAuthorizePayment.html │ ├── Moltin.html │ ├── MoltinRequest.html │ ├── Order.html │ ├── OrderMeta.html │ ├── OrderRelationships.html │ ├── OrderRequest.html │ ├── PaginatedResponse.html │ ├── PaginationMeta.html │ ├── Product.html │ ├── ProductMeta.html │ ├── ProductRequest.html │ ├── ProductVariation.html │ ├── ProductVariationOption.html │ ├── RelationshipMany.html │ ├── RelationshipSingle.html │ ├── Relationships.html │ ├── StripeCard.html │ ├── StripeToken.html │ ├── TaxItem.html │ ├── Timestamps.html │ └── UpdateCustomer.html ├── Enums │ ├── CartItemType.html │ ├── MoltinError.html │ └── Result.html ├── Extensions │ ├── Decodable.html │ └── JSONDecoder.html ├── Guides.html ├── Models.html ├── Moltin Core.html ├── Other Classes.html ├── Other Functions.html ├── Other Typealiases.html ├── Payment Methods.html ├── Protocols │ └── PaymentMethod.html ├── Resources.html ├── Structs │ ├── CartItemDisplayPrice.html │ ├── CartItemDisplayPrices.html │ ├── DisplayPrice.html │ ├── DisplayPrices.html │ ├── FieldMeta.html │ ├── FileDimensions.html │ ├── MoltinConfig.html │ ├── MoltinFilterOperator.html │ ├── MoltinInclude.html │ ├── ProductPrice.html │ ├── ProductStock.html │ └── RelationshipData.html ├── Utils.html ├── authentication.html ├── badge.svg ├── communication.html ├── css │ ├── highlight.css │ └── jazzy.css ├── docsets │ ├── moltin.docset │ │ └── Contents │ │ │ ├── Info.plist │ │ │ └── Resources │ │ │ ├── Documents │ │ │ ├── Classes │ │ │ │ ├── Address.html │ │ │ │ ├── AdyenPayment.html │ │ │ │ ├── BraintreeCustomerID.html │ │ │ │ ├── BraintreePaymentNonce.html │ │ │ │ ├── BraintreePaymentToken.html │ │ │ │ ├── Brand.html │ │ │ │ ├── BrandRequest.html │ │ │ │ ├── Cart.html │ │ │ │ ├── CartItem.html │ │ │ │ ├── CartItemMeta.html │ │ │ │ ├── CartItemPrice.html │ │ │ │ ├── CartMeta.html │ │ │ │ ├── CartRequest.html │ │ │ │ ├── Category.html │ │ │ │ ├── CategoryRequest.html │ │ │ │ ├── Collection.html │ │ │ │ ├── CollectionRequest.html │ │ │ │ ├── Currency.html │ │ │ │ ├── CurrencyMeta.html │ │ │ │ ├── CurrencyRequest.html │ │ │ │ ├── CustomCartItem.html │ │ │ │ ├── Customer.html │ │ │ │ ├── CustomerRequest.html │ │ │ │ ├── CustomerToken.html │ │ │ │ ├── Entry.html │ │ │ │ ├── Field.html │ │ │ │ ├── FieldRequest.html │ │ │ │ ├── File.html │ │ │ │ ├── FileMeta.html │ │ │ │ ├── FileRequest.html │ │ │ │ ├── Flow.html │ │ │ │ ├── FlowRequest.html │ │ │ │ ├── ManuallyAuthorizePayment.html │ │ │ │ ├── Moltin.html │ │ │ │ ├── MoltinRequest.html │ │ │ │ ├── Order.html │ │ │ │ ├── OrderMeta.html │ │ │ │ ├── OrderRelationships.html │ │ │ │ ├── OrderRequest.html │ │ │ │ ├── PaginatedResponse.html │ │ │ │ ├── PaginationMeta.html │ │ │ │ ├── Product.html │ │ │ │ ├── ProductMeta.html │ │ │ │ ├── ProductRequest.html │ │ │ │ ├── ProductVariation.html │ │ │ │ ├── ProductVariationOption.html │ │ │ │ ├── RelationshipMany.html │ │ │ │ ├── RelationshipSingle.html │ │ │ │ ├── Relationships.html │ │ │ │ ├── StripeCard.html │ │ │ │ ├── StripeToken.html │ │ │ │ ├── TaxItem.html │ │ │ │ ├── Timestamps.html │ │ │ │ └── UpdateCustomer.html │ │ │ ├── Enums │ │ │ │ ├── CartItemType.html │ │ │ │ ├── MoltinError.html │ │ │ │ └── Result.html │ │ │ ├── Extensions │ │ │ │ ├── Decodable.html │ │ │ │ └── JSONDecoder.html │ │ │ ├── Guides.html │ │ │ ├── Models.html │ │ │ ├── Moltin Core.html │ │ │ ├── Other Classes.html │ │ │ ├── Other Functions.html │ │ │ ├── Other Typealiases.html │ │ │ ├── Payment Methods.html │ │ │ ├── Protocols │ │ │ │ └── PaymentMethod.html │ │ │ ├── Resources.html │ │ │ ├── Structs │ │ │ │ ├── CartItemDisplayPrice.html │ │ │ │ ├── CartItemDisplayPrices.html │ │ │ │ ├── DisplayPrice.html │ │ │ │ ├── DisplayPrices.html │ │ │ │ ├── FieldMeta.html │ │ │ │ ├── FileDimensions.html │ │ │ │ ├── MoltinConfig.html │ │ │ │ ├── MoltinFilterOperator.html │ │ │ │ ├── MoltinInclude.html │ │ │ │ ├── ProductPrice.html │ │ │ │ ├── ProductStock.html │ │ │ │ └── RelationshipData.html │ │ │ ├── Utils.html │ │ │ ├── authentication.html │ │ │ ├── badge.svg │ │ │ ├── communication.html │ │ │ ├── css │ │ │ │ ├── highlight.css │ │ │ │ └── jazzy.css │ │ │ ├── filtering.html │ │ │ ├── flows.html │ │ │ ├── further-documentation.html │ │ │ ├── img │ │ │ │ ├── carat.png │ │ │ │ ├── dash.png │ │ │ │ ├── gh.png │ │ │ │ └── spinner.gif │ │ │ ├── index.html │ │ │ ├── installation.html │ │ │ ├── js │ │ │ │ ├── jazzy.js │ │ │ │ ├── jazzy.search.js │ │ │ │ ├── jquery.min.js │ │ │ │ ├── lunr.min.js │ │ │ │ └── typeahead.jquery.js │ │ │ ├── license.html │ │ │ ├── requirements.html │ │ │ ├── search.json │ │ │ ├── tmp │ │ │ │ └── docs │ │ │ │ │ ├── Authentication.md │ │ │ │ │ ├── Communication.md │ │ │ │ │ ├── Filtering.md │ │ │ │ │ ├── Flows.md │ │ │ │ │ ├── Further Documentation.md │ │ │ │ │ ├── Installation.md │ │ │ │ │ ├── License.md │ │ │ │ │ ├── Requirements.md │ │ │ │ │ └── Usage.md │ │ │ ├── undocumented.json │ │ │ └── usage.html │ │ │ └── docSet.dsidx │ └── moltin.tgz ├── filtering.html ├── flows.html ├── further-documentation.html ├── img │ ├── carat.png │ ├── dash.png │ ├── gh.png │ └── spinner.gif ├── index.html ├── installation.html ├── js │ ├── jazzy.js │ ├── jazzy.search.js │ ├── jquery.min.js │ ├── lunr.min.js │ └── typeahead.jquery.js ├── license.html ├── requirements.html ├── search.json ├── undocumented.json └── usage.html ├── fastlane ├── Appfile ├── Fastfile └── README.md ├── moltin.playground ├── Contents.swift └── contents.xcplayground ├── moltin.png ├── moltin.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── craigtweedy.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ ├── moltin WatchKit Example.xcscheme │ │ ├── moltin iOS Example.xcscheme │ │ ├── moltin iOS.xcscheme │ │ ├── moltin tvOS.xcscheme │ │ └── moltin watchOS.xcscheme └── xcuserdata │ └── craigtweedy.xcuserdatad │ └── xcschemes │ ├── moltin iOS Tests.xcscheme │ └── xcschememanagement.plist ├── moltin.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── scripts ├── doc-preprocessor └── generator-docs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # iOS CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/ios-migrating-from-1-2/ for more details 4 | # 5 | 6 | version: 2 7 | jobs: 8 | 9 | swiftlint: 10 | docker: 11 | - image: dantoml/swiftlint:latest 12 | steps: 13 | - checkout 14 | - run: swiftlint lint --reporter junit | tee result.xml 15 | - store_artifacts: 16 | path: result.xml 17 | - store_test_results: 18 | path: result.xml 19 | 20 | test_iOS: 21 | macos: 22 | xcode: "10.1.0" 23 | 24 | steps: 25 | - checkout 26 | - run: fastlane ios test 27 | - store_test_results: 28 | path: fastlane/test_output/ 29 | - store_artifacts: 30 | path: ~/Library/Logs/scan 31 | destination: scan-logs 32 | 33 | test_tvOS: 34 | macos: 35 | xcode: "10.1.0" 36 | 37 | steps: 38 | - checkout 39 | - run: fastlane ios test_tv 40 | - store_test_results: 41 | path: fastlane/test_output/ 42 | - store_artifacts: 43 | path: ~/Library/Logs/scan 44 | destination: scan-logs 45 | 46 | test_macOS: 47 | macos: 48 | xcode: "10.1.0" 49 | 50 | steps: 51 | - checkout 52 | - run: fastlane mac test 53 | - store_test_results: 54 | path: fastlane/test_output/ 55 | - store_artifacts: 56 | path: ~/Library/Logs/scan 57 | destination: scan-logs 58 | 59 | update_documentation: 60 | macos: 61 | xcode: "10.1.0" 62 | 63 | steps: 64 | - run: echo "hello" 65 | 66 | update_cocoapods: 67 | macos: 68 | xcode: "10.1.0" 69 | 70 | steps: 71 | - run: echo "hello" 72 | 73 | tag: 74 | macos: 75 | xcode: "10.1.0" 76 | 77 | steps: 78 | - run: echo "hello" 79 | 80 | push: 81 | macos: 82 | xcode: "10.1.0" 83 | 84 | steps: 85 | - run: echo "hello" 86 | 87 | workflows: 88 | version: 2 89 | build-test-and-deploy: 90 | jobs: 91 | - swiftlint 92 | - test_iOS: 93 | requires: 94 | - swiftlint 95 | - test_tvOS: 96 | requires: 97 | - swiftlint 98 | # - test_macOS 99 | - update_cocoapods: 100 | requires: 101 | - test_iOS 102 | # - test_macOS 103 | - test_tvOS 104 | filters: 105 | branches: 106 | only: master 107 | - update_documentation: 108 | requires: 109 | - update_cocoapods 110 | - tag: 111 | requires: 112 | - update_documentation 113 | filters: 114 | branches: 115 | only: master 116 | - push: 117 | requires: 118 | - tag 119 | filters: 120 | branches: 121 | only: master -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Issue Type 2 | 3 | - [ ] Question 4 | - [ ] Feature 5 | - [ ] Bug 6 | - [ ] Other Issue 7 | 8 | ### Issue Description and Steps 9 | 10 | Please fill in as much info as you can about the issue, including reproduction steps if possible. 11 | 12 | If you have any code snippets or screenshots you are able to share, please add them as well. 13 | 14 | ### Issue Info 15 | 16 | Info | Value | 17 | -------------------------|-------------------------------------| 18 | Platform Name | e.g. ios / tvos 19 | Platform Version | e.g. 8.0 20 | Language | e.g Objective-C / Swift 21 | moltin SDK Version | e.g. 3.7.6 22 | Integration Method | e.g. carthage / cocoapods / manually 23 | Xcode Version | e.g. Xcode 7.3 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Status 2 | * ✅ Ready * 🚧 WIP * ⛔ Hold 3 | ## Type 4 | * ### Feature 5 | Implements a new feature * ### Fix 6 | Fixes a bug * ### Docs 7 | Documentation only changes * ### 8 | Style 9 | Changes that **do not** affect the 10 | meaning of the code (white-space, 11 | formatting, missing semi-colons, etc) * 12 | ### Refactor 13 | A code change that neither fixes a 14 | bug nor adds a feature * ### 15 | Performance 16 | A code change that improves 17 | performance * ### Test 18 | Adding missing or correcting existing 19 | tests * ### Chore 20 | Changes to the build process or 21 | auxiliary tools and libraries such as 22 | documentation generation 23 | ## Description 24 | *A brief description of the goals of 25 | the pull request.* 26 | ## Dependencies 27 | *Other PRs or builds that this PR 28 | depends on.* 29 | ## Issues 30 | *A list of issues closed by this PR.* * 31 | Fixes # 32 | ## Notes 33 | *Any additional notes.* 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | .build/ 37 | 38 | # Carthage 39 | Carthage/Build 40 | 41 | # Fastlane 42 | fastlane/report.xml 43 | fastlane/test_output -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | module: moltin 2 | author: Moltin 3 | author_url: https://moltin.com 4 | github_url: https://github.com/moltin/ios-sdk 5 | copyright: '© [Moltin](https://moltin.com/). See [license](https://github.com/moltin/ios-sdk/blob/master/LICENSE.md) for more details.' 6 | 7 | output: docs 8 | documentation: "docs/tmp/docs/*.md" 9 | readme: "docs/tmp/compile/README.md" 10 | abstract: "docs/tmp/api/*.md" 11 | theme: fullwidth 12 | 13 | custom_categories: 14 | - name: Guides 15 | children: 16 | - Requirements 17 | - Installation 18 | - Usage 19 | - Authentication 20 | - Filtering 21 | - Flows 22 | - Further Documentation 23 | - Communication 24 | - License 25 | - name: Moltin Core 26 | children: 27 | - Moltin 28 | - MoltinConfig 29 | - MoltinQuery 30 | - MoltinError 31 | - MoltinFilterOperator 32 | - MoltinInclude 33 | - Result 34 | - PaginatedResponse 35 | - PaginationMeta 36 | - name: Resources 37 | children: 38 | - BrandRequest 39 | - CartRequest 40 | - CategoryRequest 41 | - CollectionRequest 42 | - CurrencyRequest 43 | - FieldRequest 44 | - FileRequest 45 | - FlowRequest 46 | - ProductRequest 47 | - MoltinRequest 48 | - name: Models 49 | children: 50 | - Address 51 | - Brand 52 | - Cart 53 | - CartMeta 54 | - CartItem 55 | - CartItemMeta 56 | - CartItemType 57 | - CartItemDisplayPrice 58 | - CartItemDisplayPrices 59 | - Category 60 | - Collection 61 | - Currency 62 | - CurrencyMeta 63 | - CustomCartItem 64 | - Customer 65 | - DisplayPrice 66 | - DisplayPrices 67 | - Entry 68 | - Field 69 | - FieldMeta 70 | - File 71 | - FileMeta 72 | - FileDimensions 73 | - Flow 74 | - Order 75 | - OrderMeta 76 | - OrderRelationships 77 | - OrderSuccess 78 | - Product 79 | - ProductMeta 80 | - ProductVariation 81 | - ProductVariationOption 82 | - ProductPrice 83 | - ProductStock 84 | - Relationships 85 | - RelationshipData 86 | - RelationshipSingle 87 | - RelationshipMany 88 | - Timestamps 89 | - name: Payment Methods 90 | children: 91 | - StripeCard 92 | - StripeToken 93 | - BraintreeCustomerID 94 | - BraintreePaymentToken 95 | - BraintreePaymentNonce 96 | - AdyenPayment 97 | - ManuallyAuthorizePayment 98 | - PaymentMethod 99 | - name: Utils 100 | children: 101 | - Decodable 102 | - JSONDecoder -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | excluded: 2 | - Tests 3 | 4 | disabled_rules: 5 | - type_name 6 | - identifier_name 7 | - large_tuple 8 | - file_length 9 | 10 | line_length: 180 11 | file_length: 600 12 | -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/Assets.xcassets/Complication.complicationset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "idiom" : "watch", 5 | "filename" : "Circular.imageset", 6 | "role" : "circular" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "filename" : "Extra Large.imageset", 11 | "role" : "extra-large" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "filename" : "Modular.imageset", 16 | "role" : "modular" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "filename" : "Utilitarian.imageset", 21 | "role" : "utilitarian" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDelegate.swift 3 | // moltin WatchKit Example Extension 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import WatchKit 9 | 10 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 11 | 12 | func handle(_ backgroundTasks: Set) { 13 | // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one. 14 | for task in backgroundTasks { 15 | // Use a switch statement to check the task type 16 | switch task { 17 | case let backgroundTask as WKApplicationRefreshBackgroundTask: 18 | // Be sure to complete the background task once you’re done. 19 | backgroundTask.setTaskCompletedWithSnapshot(false) 20 | case let snapshotTask as WKSnapshotRefreshBackgroundTask: 21 | // Snapshot tasks have a unique completion call, make sure to set your expiration date 22 | snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil) 23 | case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask: 24 | // Be sure to complete the connectivity task once you’re done. 25 | connectivityTask.setTaskCompletedWithSnapshot(false) 26 | case let urlSessionTask as WKURLSessionRefreshBackgroundTask: 27 | // Be sure to complete the URL session task once you’re done. 28 | urlSessionTask.setTaskCompletedWithSnapshot(false) 29 | default: 30 | // make sure to complete unhandled task types 31 | task.setTaskCompletedWithSnapshot(false) 32 | } 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | moltin WatchKit Example Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | WKAppBundleIdentifier 28 | com.moltin.moltin-iOS-Example.watchkitapp 29 | 30 | NSExtensionPointIdentifier 31 | com.apple.watchkit 32 | 33 | WKExtensionDelegateClassName 34 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 35 | 36 | 37 | -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.swift 3 | // moltin WatchKit Example Extension 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import WatchKit 9 | import Foundation 10 | 11 | class InterfaceController: WKInterfaceController { 12 | 13 | override func awake(withContext context: Any?) { 14 | super.awake(withContext: context) 15 | 16 | // Configure interface objects here. 17 | } 18 | 19 | override func willActivate() { 20 | // This method is called when watch view controller is about to be visible to user 21 | super.willActivate() 22 | } 23 | 24 | override func didDeactivate() { 25 | // This method is called when watch view controller is no longer visible 26 | super.didDeactivate() 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "size" : "44x44", 38 | "idiom" : "watch", 39 | "scale" : "2x", 40 | "role" : "longLook", 41 | "subtype" : "42mm" 42 | }, 43 | { 44 | "size" : "86x86", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "quickLook", 48 | "subtype" : "38mm" 49 | }, 50 | { 51 | "size" : "98x98", 52 | "idiom" : "watch", 53 | "scale" : "2x", 54 | "role" : "quickLook", 55 | "subtype" : "42mm" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Examples/moltin WatchKit Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | moltin iOS Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | UISupportedInterfaceOrientations 24 | 25 | UIInterfaceOrientationPortrait 26 | UIInterfaceOrientationPortraitUpsideDown 27 | 28 | WKCompanionAppBundleIdentifier 29 | com.moltin.moltin-iOS-Example 30 | WKWatchKitApp 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { 12 | 13 | static let cartID = UUID().uuidString 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/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 | } -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Assets.xcassets/ill-dark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ill-dark.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ill-dark-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ill-dark-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Assets.xcassets/ill-dark.imageset/ill-dark-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/Examples/moltin iOS Example/Assets.xcassets/ill-dark.imageset/ill-dark-1.png -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Assets.xcassets/ill-dark.imageset/ill-dark-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/Examples/moltin iOS Example/Assets.xcassets/ill-dark.imageset/ill-dark-2.png -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Assets.xcassets/ill-dark.imageset/ill-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/Examples/moltin iOS Example/Assets.xcassets/ill-dark.imageset/ill-dark.png -------------------------------------------------------------------------------- /Examples/moltin iOS Example/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 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/CartViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CartViewController.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 03/04/2018. 6 | // 7 | 8 | import UIKit 9 | import moltin 10 | 11 | class CartViewController: UIViewController { 12 | 13 | @IBOutlet weak var tableView: UITableView! 14 | 15 | let moltin: Moltin = Moltin(withClientID: "j6hSilXRQfxKohTndUuVrErLcSJWP15P347L6Im0M4", withLocale: Locale(identifier: "en_US")) 16 | 17 | var cartItems: [CartItem] = [] 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | // Do any additional setup after loading the view. 23 | self.moltin.cart.items(forCartID: AppDelegate.cartID) { (result) in 24 | switch result { 25 | case .success(let result): 26 | DispatchQueue.main.async { 27 | self.cartItems = result.data ?? [] 28 | self.tableView.reloadData() 29 | } 30 | case .failure(let error): 31 | print(error) 32 | } 33 | } 34 | } 35 | 36 | override func didReceiveMemoryWarning() { 37 | super.didReceiveMemoryWarning() 38 | // Dispose of any resources that can be recreated. 39 | } 40 | 41 | @IBAction func checkout(_ sender: Any) { 42 | let customer = Customer(withEmail: "craig.tweedy@moltin.com", withName: "Craig Tweedy") 43 | let address = Address(withFirstName: "Craig", withLastName: "Tweedy") 44 | address.line1 = "1 Silicon Way" 45 | address.county = "Somewhere" 46 | address.country = "Fiction" 47 | address.postcode = "NE1 1AA" 48 | self.moltin.cart.checkout(cart: AppDelegate.cartID, withCustomer: customer, withBillingAddress: address, withShippingAddress: nil) { (result) in 49 | switch result { 50 | case .success(let order): 51 | self.payForOrder(order) 52 | default: break 53 | } 54 | } 55 | } 56 | 57 | func payForOrder(_ order: Order) { 58 | let paymentMethod = ManuallyAuthorizePayment() 59 | self.moltin.cart.pay(forOrderID: order.id, withPaymentMethod: paymentMethod) { (result) in 60 | switch result { 61 | case .success: 62 | DispatchQueue.main.async { 63 | self.showOrderStatus(withSuccess: true) 64 | } 65 | case .failure(let error): 66 | DispatchQueue.main.async { 67 | self.showOrderStatus(withSuccess: false, withError: error) 68 | } 69 | } 70 | } 71 | } 72 | 73 | func showOrderStatus(withSuccess success: Bool, withError error: Error? = nil) { 74 | let title = success ? "Order paid!" : "Order error" 75 | let message = success ? "Complete!" : error?.localizedDescription 76 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 77 | self.present(alert, animated: true, completion: nil) 78 | } 79 | } 80 | 81 | extension CartViewController: UITableViewDelegate, UITableViewDataSource { 82 | 83 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 84 | return self.cartItems.count 85 | } 86 | 87 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 88 | var cell: UITableViewCell! 89 | cell = tableView.dequeueReusableCell(withIdentifier: "Cell") 90 | if cell == nil { 91 | cell = UITableViewCell(style: .default, reuseIdentifier: "Cell") 92 | } 93 | 94 | let cartItem = self.cartItems[indexPath.row] 95 | cell.textLabel?.text = cartItem.name 96 | 97 | return cell 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/CollectionCell/CategoryCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CategoryCollectionViewCell.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 28/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class CategoryCollectionViewCell: UICollectionViewCell { 11 | 12 | @IBOutlet weak var categoryTitleLabel: UILabel! 13 | @IBOutlet weak var categoryBackgroundImage: UIImageView! 14 | override func awakeFromNib() { 15 | super.awakeFromNib() 16 | // Initialization code 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/CollectionCell/MasterSectionHeaderCollectionReusableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MasterSectionHeaderCollectionReusableView.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 29/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class MasterSectionHeaderCollectionReusableView: UICollectionReusableView { 11 | 12 | @IBOutlet weak var sectionLabel: UILabel! 13 | 14 | override func awakeFromNib() { 15 | super.awakeFromNib() 16 | // Initialization code 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/CollectionCell/MasterSectionHeaderCollectionReusableView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import UIKit 9 | import moltin 10 | 11 | class DetailViewController: UIViewController { 12 | 13 | let moltin: Moltin = Moltin(withClientID: "j6hSilXRQfxKohTndUuVrErLcSJWP15P347L6Im0M4", withLocale: Locale(identifier: "en_US")) 14 | 15 | @IBOutlet weak var collectionView: UICollectionView! 16 | var category: ProductCategory? 17 | var products: [Product]? = [] 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | self.title = category?.name 23 | 24 | self.collectionView.register(UINib(nibName: "ProductCollectionViewCell", bundle: Bundle.main), forCellWithReuseIdentifier: "Cell") 25 | } 26 | 27 | override func viewWillAppear(_ animated: Bool) { 28 | super.viewWillAppear(animated) 29 | self.navigationController?.setNavigationBarHidden(false, animated: true) 30 | } 31 | } 32 | 33 | extension DetailViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 34 | 35 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 36 | return self.products?.count ?? 0 37 | } 38 | 39 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 40 | let itemWidth = collectionView.frame.width / 2 41 | 42 | return CGSize(width: itemWidth, height: itemWidth) 43 | } 44 | 45 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 46 | guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? ProductCollectionViewCell else { return UICollectionViewCell() } 47 | 48 | if let product = self.products?[indexPath.row] { 49 | 50 | cell.productName.text = product.name 51 | cell.productPrice.text = product.meta.displayPrice?.withTax.formatted 52 | 53 | self.moltin.product.include([.mainImage]).get(forID: product.id, completionHandler: { (result: Result) in 54 | switch result { 55 | case .success(let product): 56 | DispatchQueue.main.async { 57 | cell.productImage.load(urlString: product.mainImage?.link["href"]) 58 | cell.backgroundColor = product.backgroundColor 59 | } 60 | default: break 61 | } 62 | }) 63 | } 64 | 65 | return cell 66 | } 67 | 68 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 69 | if let product = self.products?[indexPath.row] { 70 | self.moltin.cart.addProduct(withID: product.id, ofQuantity: 1, toCart: AppDelegate.cartID, completionHandler: { (_) in 71 | DispatchQueue.main.async { 72 | //test custom cart 73 | let customCartPrice = CartItemPrice(amount: 111, includes_tax: false) 74 | let customCartItem = CustomCartItem(withName: "test", sku: "sku", quantity: 1, description: "test desc", price: customCartPrice) 75 | self.moltin.cart.addCustomItem(customCartItem, toCart: AppDelegate.cartID, completionHandler: { (_) in 76 | DispatchQueue.main.async { 77 | self.performSegue(withIdentifier: "DetailToCart", sender: nil) 78 | } 79 | }) 80 | } 81 | }) 82 | 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Extensions/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 29/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIColor { 11 | convenience init(hexString: String, alpha: CGFloat = 1.0) { 12 | let hexString: String = hexString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 13 | let scanner = Scanner(string: hexString) 14 | if hexString.hasPrefix("#") { 15 | scanner.scanLocation = 1 16 | } 17 | var color: UInt32 = 0 18 | scanner.scanHexInt32(&color) 19 | let mask = 0x000000FF 20 | let r = Int(color >> 16) & mask 21 | let g = Int(color >> 8) & mask 22 | let b = Int(color) & mask 23 | let red = CGFloat(r) / 255.0 24 | let green = CGFloat(g) / 255.0 25 | let blue = CGFloat(b) / 255.0 26 | self.init(red: red, green: green, blue: blue, alpha: alpha) 27 | } 28 | func toHexString() -> String { 29 | var r: CGFloat = 0 30 | var g: CGFloat = 0 31 | var b: CGFloat = 0 32 | var a: CGFloat = 0 33 | getRed(&r, green: &g, blue: &b, alpha: &a) 34 | let rgb: Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 35 | return String(format: "#%06x", rgb) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Extensions/UIImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 29/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIImageView { 11 | 12 | func load(urlString string: String?) { 13 | guard let imageUrl = string, 14 | let url = URL(string: imageUrl) else { return } 15 | 16 | URLSession.shared.dataTask(with: url) { data, _, _ in 17 | if let data = data { 18 | DispatchQueue.main.async { 19 | self.image = UIImage(data: data) 20 | } 21 | } 22 | }.resume() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/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 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | arm64 30 | 31 | UIStatusBarTintParameters 32 | 33 | UINavigationBar 34 | 35 | Style 36 | UIBarStyleDefault 37 | Translucent 38 | 39 | 40 | 41 | UISupportedInterfaceOrientations 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | UISupportedInterfaceOrientations~ipad 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationPortraitUpsideDown 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/MasterViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MasterViewController.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 28/03/2018. 6 | // 7 | 8 | import UIKit 9 | import moltin 10 | 11 | class MasterViewController: UIViewController { 12 | 13 | @IBOutlet weak var collectionView: UICollectionView! 14 | 15 | private let masterNibName = "CategoryCollectionViewCell" 16 | let moltin: Moltin = Moltin(withClientID: "j6hSilXRQfxKohTndUuVrErLcSJWP15P347L6Im0M4", withLocale: Locale(identifier: "en_US")) 17 | 18 | var categories: [ProductCategory] = [] 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | // Do any additional setup after loading the view. 24 | self.collectionView.delegate = self 25 | self.collectionView.dataSource = self 26 | 27 | self.collectionView.register(UINib(nibName: self.masterNibName, bundle: Bundle.main), forCellWithReuseIdentifier: self.masterNibName) 28 | 29 | self.moltin.category.include([.products]).all { (result: Result>) in 30 | switch result { 31 | case .success(let response): 32 | self.categories = response.data ?? [] 33 | DispatchQueue.main.async { 34 | self.collectionView.reloadData() 35 | } 36 | case .failure(let error): 37 | print(error) 38 | } 39 | } 40 | } 41 | 42 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 43 | if let controller = segue.destination as? DetailViewController, 44 | segue.identifier == "CategoriesToProducts", 45 | let category = sender as? ProductCategory { 46 | controller.products = category.products 47 | controller.category = category 48 | } 49 | } 50 | 51 | override func viewWillAppear(_ animated: Bool) { 52 | super.viewWillAppear(animated) 53 | self.navigationController?.setNavigationBarHidden(true, animated: true) 54 | } 55 | 56 | } 57 | 58 | extension MasterViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 59 | 60 | // MARK: UICollectionViewDataSource 61 | 62 | func numberOfSections(in collectionView: UICollectionView) -> Int { 63 | return 1 64 | } 65 | 66 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 67 | return self.categories.count 68 | } 69 | 70 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 71 | 72 | let itemWidth: CGFloat = collectionView.frame.width / 2 73 | 74 | return CGSize(width: itemWidth, height: itemWidth) 75 | } 76 | 77 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 78 | guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.masterNibName, for: indexPath) as? CategoryCollectionViewCell else { 79 | return UICollectionViewCell() 80 | } 81 | 82 | let category = self.categories[indexPath.row] 83 | cell.categoryTitleLabel.text = category.name.uppercased() 84 | cell.categoryBackgroundImage.load(urlString: category.backgroundImage) 85 | cell.backgroundColor = category.backgroundColor 86 | 87 | return cell 88 | } 89 | 90 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 91 | self.performSegue(withIdentifier: "CategoriesToProducts", sender: self.categories[indexPath.row]) 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Models/CustomProduct.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomProduct.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 29/03/2018. 6 | // 7 | 8 | import UIKit 9 | import moltin 10 | 11 | class CustomProduct: Product { 12 | var backgroundColor: UIColor? 13 | 14 | enum ProductCodingKeys: String, CodingKey { 15 | case backgroundColor = "background_colour" 16 | } 17 | 18 | required init(from decoder: Decoder) throws { 19 | let container = try decoder.container(keyedBy: ProductCodingKeys.self) 20 | let color: String = try container.decode(String.self, forKey: .backgroundColor) 21 | self.backgroundColor = UIColor(hexString: color) 22 | try super.init(from: decoder) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/Models/ProductCategory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductCategory.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 29/03/2018. 6 | // 7 | 8 | import UIKit 9 | import moltin 10 | 11 | class ProductCategory: moltin.Category { 12 | var backgroundColor: UIColor? 13 | var backgroundImage: String? 14 | 15 | enum ProductCategoryCodingKeys: String, CodingKey { 16 | case backgroundColor = "background_colour" 17 | case backgroundImage = "background_image" 18 | } 19 | 20 | required init(from decoder: Decoder) throws { 21 | let container = try decoder.container(keyedBy: ProductCategoryCodingKeys.self) 22 | if let color: String = try container.decodeIfPresent(String.self, forKey: .backgroundColor) { 23 | self.backgroundColor = UIColor(hexString: color) 24 | } 25 | self.backgroundImage = try container.decodeIfPresent(String.self, forKey: .backgroundImage) 26 | try super.init(from: decoder) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/moltin iOS Example/ProductCell/ProductCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductCollectionViewCell.swift 3 | // moltin iOS Example 4 | // 5 | // Created by Craig Tweedy on 29/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class ProductCollectionViewCell: UICollectionViewCell { 11 | 12 | @IBOutlet weak var productImage: UIImageView! 13 | @IBOutlet weak var productName: UILabel! 14 | @IBOutlet weak var productPrice: UILabel! 15 | 16 | override func awakeFromNib() { 17 | super.awakeFromNib() 18 | // Initialization code 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // moltin tvOS Example 4 | // 5 | // Created by Craig Tweedy on 21/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "version" : 1, 9 | "author" : "xcode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "version" : 1, 9 | "author" : "xcode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "version" : 1, 9 | "author" : "xcode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "size" : "1280x768", 5 | "idiom" : "tv", 6 | "filename" : "App Icon - App Store.imagestack", 7 | "role" : "primary-app-icon" 8 | }, 9 | { 10 | "size" : "400x240", 11 | "idiom" : "tv", 12 | "filename" : "App Icon.imagestack", 13 | "role" : "primary-app-icon" 14 | }, 15 | { 16 | "size" : "2320x720", 17 | "idiom" : "tv", 18 | "filename" : "Top Shelf Image Wide.imageset", 19 | "role" : "top-shelf-image-wide" 20 | }, 21 | { 22 | "size" : "1920x720", 23 | "idiom" : "tv", 24 | "filename" : "Top Shelf Image.imageset", 25 | "role" : "top-shelf-image" 26 | } 27 | ], 28 | "info" : { 29 | "version" : 1, 30 | "author" : "xcode" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "landscape", 5 | "idiom" : "tv", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "9.0", 8 | "scale" : "1x" 9 | } 10 | ], 11 | "info" : { 12 | "version" : 1, 13 | "author" : "xcode" 14 | } 15 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/Examples/moltin tvOS Example/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/Examples/moltin tvOS Example/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/CartViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CartViewController.swift 3 | // moltin tvOS Example 4 | // 5 | // Created by Craig Tweedy on 21/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class CartViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | // Do any additional setup after loading the view, typically from a nib. 15 | } 16 | 17 | override func didReceiveMemoryWarning() { 18 | super.didReceiveMemoryWarning() 19 | // Dispose of any resources that can be recreated. 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/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 | UIMainStoryboardFile 24 | Main 25 | UIRequiredDeviceCapabilities 26 | 27 | arm64 28 | 29 | UIUserInterfaceStyle 30 | Automatic 31 | 32 | 33 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/ProductCategoryCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductCollectionViewCell.swift 3 | // moltin tvOS Example 4 | // 5 | // Created by Craig Tweedy on 21/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class ProductCategoryCollectionViewCell: UICollectionViewCell { 11 | 12 | @IBOutlet var label: UILabel! 13 | @IBOutlet var image: UIImageView! 14 | } 15 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/ProductCategoryCollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/ProductCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductCollectionViewCell.swift 3 | // moltin tvOS Example 4 | // 5 | // Created by Craig Tweedy on 23/05/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class ProductCollectionViewCell: UICollectionViewCell { 11 | 12 | @IBOutlet weak var productImage: UIImageView! 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/ProductCollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Examples/moltin tvOS Example/ProductsCollectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductsCollectionViewController.swift 3 | // moltin tvOS Example 4 | // 5 | // Created by Craig Tweedy on 23/05/2018. 6 | // 7 | 8 | import UIKit 9 | import moltin 10 | 11 | private let reuseIdentifier = "ProductCell" 12 | 13 | class ProductsCollectionViewController: UICollectionViewController { 14 | 15 | var data: [Product] = [] 16 | var category: ProductCategory? 17 | 18 | let moltin: Moltin = Moltin(withClientID: "j6hSilXRQfxKohTndUuVrErLcSJWP15P347L6Im0M4", withLocale: Locale(identifier: "en_US")) 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | // Uncomment the following line to preserve selection between presentations 24 | // self.clearsSelectionOnViewWillAppear = false 25 | 26 | // Register cell classes 27 | self.collectionView?.register(UINib(nibName: "ProductCollectionViewCell", bundle: Bundle.main), forCellWithReuseIdentifier: reuseIdentifier) 28 | 29 | // Do any additional setup after loading the view. 30 | 31 | guard let categoryID = category?.id else { return } 32 | moltin.product.filter(operator: .equal, key: "category.id", value: categoryID).include([.mainImage]).all { result in 33 | if case .success(let products) = result { 34 | self.data = products.data ?? [] 35 | DispatchQueue.main.async { 36 | self.collectionView?.reloadData() 37 | } 38 | } 39 | } 40 | } 41 | 42 | override func didReceiveMemoryWarning() { 43 | super.didReceiveMemoryWarning() 44 | // Dispose of any resources that can be recreated. 45 | } 46 | 47 | /* 48 | // MARK: - Navigation 49 | 50 | // In a storyboard-based application, you will often want to do a little preparation before navigation 51 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 52 | // Get the new view controller using [segue destinationViewController]. 53 | // Pass the selected object to the new view controller. 54 | } 55 | */ 56 | 57 | // MARK: UICollectionViewDataSource 58 | 59 | override func numberOfSections(in collectionView: UICollectionView) -> Int { 60 | return 1 61 | } 62 | 63 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 64 | return self.data.count 65 | } 66 | 67 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 68 | // Configure the cell 69 | guard let cell: ProductCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? ProductCollectionViewCell else { 70 | return UICollectionViewCell() 71 | } 72 | let product = self.data[indexPath.row] 73 | cell.productImage.load(urlString: product.mainImage?.link["href"]) 74 | 75 | return cell 76 | } 77 | 78 | // MARK: UICollectionViewDelegate 79 | 80 | /* 81 | // Uncomment this method to specify if the specified item should be highlighted during tracking 82 | override func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { 83 | return true 84 | } 85 | */ 86 | 87 | /* 88 | // Uncomment this method to specify if the specified item should be selected 89 | override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { 90 | return true 91 | } 92 | */ 93 | 94 | /* 95 | // Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item 96 | override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool { 97 | return false 98 | } 99 | 100 | override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool { 101 | return false 102 | } 103 | 104 | override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) { 105 | 106 | } 107 | */ 108 | 109 | } 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 moltin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Moltin.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Moltin" 3 | s.version = "3.1.2" 4 | s.summary = "eCommerce made simple" 5 | s.module_name = 'moltin' 6 | 7 | s.description = <<-DESC 8 | moltin makes eCommerce simple with a wide range of SDKs and integrations. 9 | DESC 10 | 11 | s.homepage = "https://moltin.com" 12 | s.license = { :type => 'MIT', :file => 'LICENSE' } 13 | s.authors = { "Craig Tweedy" => "craig.tweedy@moltin.com" } 14 | 15 | s.platforms = { :ios => "10.0", :watchos => "3.0", :tvos => "10.0" } 16 | 17 | s.source = { :git => "https://github.com/moltin/ios-sdk.git", :tag => s.version } 18 | 19 | s.source_files = "Sources/**/*" 20 | s.requires_arc = true 21 | 22 | s.ios.deployment_target = "10.0" 23 | s.tvos.deployment_target = "10.0" 24 | s.watchos.deployment_target = "3.0" 25 | s.module_name = 'moltin' 26 | end 27 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package(name: "moltin") -------------------------------------------------------------------------------- /Sourcery/AutoRequests.swift: -------------------------------------------------------------------------------- 1 | private enum MoltinAPIEndpoints { 2 | /// sourcery: model = Brand, path = "/brands", hasTree, hasCustomType 3 | case brand 4 | 5 | /// sourcery: model = Category, path = "/categories", hasTree, hasCustomType 6 | case category 7 | 8 | /// sourcery: model = Collection, path = "/collections", hasTree, hasCustomType 9 | case collection 10 | 11 | /// sourcery: model = Currency, path = "/currencies" 12 | case currency 13 | 14 | /// sourcery: model = File, path = "/files" 15 | case file 16 | 17 | /// sourcery: model = Field, path = "/fields" 18 | case field 19 | 20 | /// sourcery: model = Order, path = "/orders", hasCustomType 21 | case order 22 | 23 | /// sourcery: model = Product, path = "/products", hasCustomType 24 | case product 25 | } 26 | -------------------------------------------------------------------------------- /Sourcery/Requests.stencil: -------------------------------------------------------------------------------- 1 | // sourcery:file:Requests.swift 2 | {% for case in type.MoltinAPIEndpoints.cases %} 3 | // MARK: {{ case.name|upperFirstLetter }}Request - AutoMoltinRequest 4 | 5 | /// An entry point to make API calls relating to `{{ case.name|upperFirstLetter }}` 6 | public class {{ case.name|upperFirstLetter }}Request: MoltinRequest { 7 | 8 | /** 9 | The API endpoint for this resource. 10 | */ 11 | public var endpoint: String = "{{ case.annotations.path }}" 12 | 13 | /** 14 | A typealias which allows automatic casting of a collection to `[{{ case.annotations.model }}]`. 15 | */ 16 | public typealias DefaultCollectionRequestHandler = CollectionRequestHandler<[{{ case.annotations.model }}]> 17 | 18 | /** 19 | A typealias which allows automatic casting of an object to `{{ case.annotations.model }}`. 20 | */ 21 | public typealias DefaultObjectRequestHandler = ObjectRequestHandler<{{ case.annotations.model }}> 22 | 23 | 24 | 25 | /** 26 | Return all instances of type `{{ case.name|upperFirstLetter }}` 27 | 28 | - Author: 29 | Craig Tweedy 30 | 31 | - parameters: 32 | - completionHandler: The handler to be called on success or failure 33 | 34 | - returns: 35 | A instance of `MoltinRequest` which encapsulates the request. 36 | */ 37 | @discardableResult public func all(completionHandler: @escaping DefaultCollectionRequestHandler) -> MoltinRequest { 38 | return super.list(withPath: "\(self.endpoint)", completionHandler: completionHandler) 39 | } 40 | 41 | {% if case|annotated:"hasCustomType" %} 42 | /** 43 | Return all instances of type `{{ case.name|upperFirstLetter }}`, cast to type `T`, which must be `Codable`. 44 | 45 | - Author: 46 | Craig Tweedy 47 | 48 | - parameters: 49 | - completionHandler: The handler to be called on success or failure 50 | 51 | - returns: 52 | A instance of `MoltinRequest` which encapsulates the request. 53 | */ 54 | @discardableResult public func all(completionHandler: @escaping CollectionRequestHandler<[T]>) -> MoltinRequest { 55 | return super.list(withPath: "\(self.endpoint)", completionHandler: completionHandler) 56 | } 57 | 58 | {% endif %} 59 | /** 60 | Return all instances of type `{{ case.name|upperFirstLetter }}` by `id` 61 | 62 | - Author: 63 | Craig Tweedy 64 | 65 | - parameters: 66 | - forID: The ID of the object 67 | - completionHandler: The handler to be called on success or failure 68 | 69 | - returns: 70 | A instance of `MoltinRequest` which encapsulates the request. 71 | */ 72 | @discardableResult public func get(forID id: String, completionHandler: @escaping DefaultObjectRequestHandler) -> MoltinRequest { 73 | return super.get(withPath: "\(self.endpoint)/\(id)", completionHandler: completionHandler) 74 | } 75 | 76 | {% if case|annotated:"hasCustomType" %} 77 | /** 78 | Return all instances of type `{{ case.name|upperFirstLetter }}` by `id`, cast to type `T`, which must be `Codable`. 79 | 80 | - Author: 81 | Craig Tweedy 82 | 83 | - parameters: 84 | - forID: The ID of the object 85 | - completionHandler: The handler to be called on success or failure 86 | 87 | - returns: 88 | A instance of `MoltinRequest` which encapsulates the request. 89 | */ 90 | @discardableResult public func get(forID id: String, completionHandler: @escaping ObjectRequestHandler) -> MoltinRequest { 91 | return super.get(withPath: "\(self.endpoint)/\(id)", completionHandler: completionHandler) 92 | } 93 | 94 | {% endif %} 95 | {% if case|annotated:"hasTree" %} 96 | /** 97 | Return the tree of `{{ case.name|upperFirstLetter }}` 98 | 99 | - Author: 100 | Craig Tweedy 101 | 102 | - parameters: 103 | - completionHandler: The handler to be called on success or failure 104 | 105 | - returns: 106 | A instance of `MoltinRequest` which encapsulates the request. 107 | */ 108 | @discardableResult public func tree(completionHandler: @escaping DefaultCollectionRequestHandler) -> MoltinRequest { 109 | return super.list(withPath: "\(self.endpoint)/tree", completionHandler: completionHandler) 110 | } 111 | 112 | {% if case|annotated:"hasCustomType" %} 113 | /** 114 | Return the tree of `{{ case.name|upperFirstLetter }}`, cast to type `T`, which must be `Codable`. 115 | 116 | - Author: 117 | Craig Tweedy 118 | 119 | - parameters: 120 | - completionHandler: The handler to be called on success or failure 121 | 122 | - returns: 123 | A instance of `MoltinRequest` which encapsulates the request. 124 | */ 125 | @discardableResult public func tree(completionHandler: @escaping CollectionRequestHandler<[T]>) -> MoltinRequest { 126 | return super.list(withPath: "\(self.endpoint)/tree", completionHandler: completionHandler) 127 | } 128 | 129 | {% endif %} 130 | {% endif %} 131 | } 132 | {% endfor %} 133 | // sourcery:end 134 | -------------------------------------------------------------------------------- /Sources/Info-iOS.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 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Info-tvOS.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 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Info-watchOS.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 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Address.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 26/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a `Address` in Moltin 11 | open class Address: Codable { 12 | /// The ID of this address 13 | public var id: String? 14 | /// The type of this object 15 | public let type: String = "address" 16 | /// The first name for the person at this address 17 | public var firstName: String 18 | /// The last name for the person at this address 19 | public var lastName: String 20 | /// The display name of this address 21 | public var name: String? 22 | /// The delivery instructions for this address 23 | public var instructions: String? 24 | /// The company name at this address 25 | public var companyName: String? 26 | /// The first line for this address 27 | public var line1: String? 28 | /// The second line for this address 29 | public var line2: String? 30 | /// The city for this address 31 | public var city: String? 32 | /// The county for this address 33 | public var county: String? 34 | /// The postcode for this address 35 | public var postcode: String? 36 | /// The country for this address 37 | public var country: String? 38 | 39 | enum CodingKeys: String, CodingKey { 40 | case firstName = "first_name" 41 | case lastName = "last_name" 42 | case companyName = "company_name" 43 | case line1 = "line_1" 44 | case line2 = "line_2" 45 | 46 | case id 47 | case type 48 | case name 49 | case instructions 50 | case city 51 | case county 52 | case postcode 53 | case country 54 | } 55 | 56 | /// Create a new address with first name and last name 57 | public init( 58 | withFirstName firstName: String, 59 | withLastName lastName: String) { 60 | self.firstName = firstName 61 | self.lastName = lastName 62 | } 63 | 64 | func toDictionary() -> [String: Any] { 65 | var data: [String: Any] = [:] 66 | 67 | data["type"] = self.type 68 | data["first_name"] = self.firstName 69 | data["last_name"] = self.lastName 70 | data["company"] = self.companyName 71 | 72 | data["line_1"] = self.line1 73 | data["line_2"] = self.line2 74 | 75 | data["id"] = self.id 76 | data["name"] = self.name 77 | data["instructions"] = self.instructions 78 | data["city"] = self.city 79 | data["county"] = self.county 80 | data["postcode"] = self.postcode 81 | data["country"] = self.country 82 | 83 | return data 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Brand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Brand.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a `Brand` in Moltin 11 | open class Brand: Codable, HasRelationship { 12 | /// The id of this brand 13 | public let id: String 14 | /// The type of this object 15 | public let type: String 16 | /// The name of this brand 17 | public let name: String 18 | /// The slug of this brand 19 | public let slug: String 20 | /// The description of this brand 21 | public let description: String 22 | /// draft / live 23 | public let status: String 24 | /// The relationships this brand has 25 | public let relationships: Relationships? 26 | 27 | /// The brands this brand is associated with 28 | public var brands: [Brand]? 29 | /// The products this brand is associated with 30 | public var products: [Product]? 31 | 32 | /// The children of this brand 33 | public var children: [Brand]? 34 | 35 | required public init(from decoder: Decoder) throws { 36 | let container = try decoder.container(keyedBy: CodingKeys.self) 37 | let includes: IncludesContainer = decoder.userInfo[.includes] as? IncludesContainer ?? [:] 38 | 39 | self.id = try container.decode(String.self, forKey: .id) 40 | self.type = try container.decode(String.self, forKey: .type) 41 | self.name = try container.decode(String.self, forKey: .name) 42 | self.slug = try container.decode(String.self, forKey: .slug) 43 | self.description = try container.decode(String.self, forKey: .description) 44 | self.status = try container.decode(String.self, forKey: .status) 45 | self.relationships = try container.decodeIfPresent(Relationships.self, forKey: .relationships) 46 | self.children = try container.decodeIfPresent([Brand].self, forKey: .children) 47 | 48 | try self.decodeRelationships(fromRelationships: self.relationships, withIncludes: includes) 49 | } 50 | } 51 | 52 | extension Brand { 53 | func decodeRelationships( 54 | fromRelationships relationships: Relationships?, 55 | withIncludes includes: IncludesContainer) throws { 56 | 57 | self.brands = try self.decodeMany(fromRelationships: self.relationships?[keyPath: \Relationships.brands], 58 | withIncludes: includes["brands"]) 59 | 60 | self.products = try self.decodeMany(fromRelationships: self.relationships?[keyPath: \Relationships.products], 61 | withIncludes: includes["products"]) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Category.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Category.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a `Category` in Moltin 11 | open class Category: Codable, HasRelationship { 12 | /// The id of this category 13 | public let id: String 14 | /// The type of this object 15 | public let type: String 16 | /// The name of this category 17 | public let name: String 18 | /// The slug of this category 19 | public let slug: String 20 | /// The description of this category 21 | public let description: String 22 | /// live / draft 23 | public let status: String 24 | /// The relationships this category has 25 | public let relationships: Relationships? 26 | 27 | /// The categories associated with this category 28 | public var categories: [Category]? 29 | /// The products associated with this category 30 | public var products: [Product]? 31 | 32 | /// The children of this category 33 | public var children: [Category]? 34 | 35 | required public init(from decoder: Decoder) throws { 36 | let container = try decoder.container(keyedBy: CodingKeys.self) 37 | let includes: IncludesContainer = decoder.userInfo[.includes] as? IncludesContainer ?? [:] 38 | 39 | self.id = try container.decode(String.self, forKey: .id) 40 | self.type = try container.decode(String.self, forKey: .type) 41 | self.name = try container.decode(String.self, forKey: .name) 42 | self.slug = try container.decode(String.self, forKey: .slug) 43 | self.description = try container.decode(String.self, forKey: .description) 44 | self.status = try container.decode(String.self, forKey: .status) 45 | self.relationships = try container.decodeIfPresent(Relationships.self, forKey: .relationships) 46 | self.children = try container.decodeIfPresent([Category].self, forKey: .children) 47 | 48 | try self.decodeRelationships(fromRelationships: self.relationships, withIncludes: includes) 49 | } 50 | } 51 | 52 | extension Category { 53 | func decodeRelationships( 54 | fromRelationships relationships: Relationships?, 55 | withIncludes includes: IncludesContainer) throws { 56 | 57 | self.categories = try self.decodeMany(fromRelationships: self.relationships?[keyPath: \Relationships.categories], 58 | withIncludes: includes["categories"]) 59 | 60 | self.products = try self.decodeMany(fromRelationships: self.relationships?[keyPath: \Relationships.products], 61 | withIncludes: includes["products"]) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Collection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Collection.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a `Collection` in Moltin 11 | open class Collection: Codable, HasRelationship { 12 | /// The id of this collection 13 | public let id: String 14 | /// The type of this object 15 | public let type: String 16 | /// The name of this collection 17 | public let name: String 18 | /// The slug of this collection 19 | public let slug: String 20 | /// The description of this collection 21 | public let description: String 22 | /// live / draft 23 | public let status: String 24 | /// The relationships this collection has 25 | public let relationships: Relationships? 26 | 27 | /// The collections associated with this collection 28 | public var collections: [Collection]? 29 | /// The products associated with this collection 30 | public var products: [Product]? 31 | 32 | /// The children of this collection 33 | public var children: [Collection]? 34 | 35 | required public init(from decoder: Decoder) throws { 36 | let container = try decoder.container(keyedBy: CodingKeys.self) 37 | let includes: IncludesContainer = decoder.userInfo[.includes] as? IncludesContainer ?? [:] 38 | 39 | self.id = try container.decode(String.self, forKey: .id) 40 | self.type = try container.decode(String.self, forKey: .type) 41 | self.name = try container.decode(String.self, forKey: .name) 42 | self.slug = try container.decode(String.self, forKey: .slug) 43 | self.description = try container.decode(String.self, forKey: .description) 44 | self.status = try container.decode(String.self, forKey: .status) 45 | self.relationships = try container.decodeIfPresent(Relationships.self, forKey: .relationships) 46 | self.children = try container.decodeIfPresent([Collection].self, forKey: .children) 47 | 48 | try self.decodeRelationships(fromRelationships: self.relationships, withIncludes: includes) 49 | } 50 | } 51 | 52 | extension Collection { 53 | func decodeRelationships( 54 | fromRelationships relationships: Relationships?, 55 | withIncludes includes: IncludesContainer) throws { 56 | 57 | self.collections = try self.decodeMany(fromRelationships: self.relationships?[keyPath: \Relationships.collections], 58 | withIncludes: includes["collections"]) 59 | 60 | self.products = try self.decodeMany(fromRelationships: self.relationships?[keyPath: \Relationships.products], 61 | withIncludes: includes["products"]) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Currency.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Currency.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents the meta information of a `Currency` object 11 | public class CurrencyMeta: Codable { 12 | /// The timestamps of this currency 13 | public let timestamps: Timestamps 14 | } 15 | 16 | /// Represents a `Currency` in Moltin 17 | open class Currency: Codable { 18 | /// The id of this currency 19 | public let id: String 20 | /// The type of this object 21 | public let type: String 22 | /// The currency code 23 | public let code: String 24 | /// The exchange rate between this currency and the default currency 25 | public let exchangeRate: Float 26 | /// The format of this currency 27 | public let format: String 28 | /// The decimal point character of this currency 29 | public let decimalPoint: String 30 | /// The thousands separator character of this currency 31 | public let thousandSeparator: String 32 | /// The amount of decimal places this currency has 33 | public let decimalPlaces: Int 34 | /// Whether this currency is the default currency 35 | public let `default`: Bool 36 | /// If this currency is enabled 37 | public let enabled: Bool 38 | /// The external links of this currency 39 | public let links: [String: String] 40 | /// The meta information for this currency 41 | public let meta: CurrencyMeta 42 | 43 | enum CodingKeys: String, CodingKey { 44 | case exchangeRate = "exchange_rate" 45 | case decimalPoint = "decimal_point" 46 | case thousandSeparator = "thousand_separator" 47 | case decimalPlaces = "decimal_places" 48 | 49 | case id 50 | case type 51 | case code 52 | case format 53 | case `default` 54 | case enabled 55 | case links 56 | case meta 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Customer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Customer.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 26/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a `Customer` in Moltin 11 | open class Customer: Codable { 12 | /// The ID of this customer 13 | public var id: String? 14 | /// The email of this customer 15 | public var email: String? 16 | /// The name of this customer 17 | public var name: String? 18 | 19 | /// Initialise a new customer for checking out with 20 | public init(withID id: String? = nil, withEmail email: String? = nil, withName name: String? = nil) { 21 | self.id = id 22 | self.email = email 23 | self.name = name 24 | } 25 | 26 | func toDictionary() -> [String: Any] { 27 | var customerData: [String: Any] = [:] 28 | if let id = self.id { 29 | customerData["id"] = id 30 | } else { 31 | customerData["email"] = self.email 32 | customerData["name"] = self.name 33 | } 34 | 35 | return customerData 36 | } 37 | } 38 | 39 | /// Represents a `CustomerToken` in Moltin 40 | open class CustomerToken: Codable { 41 | /// The ID of this token 42 | public var id: String? 43 | /// The customer ID assigned to this token 44 | public var customerID: String? 45 | /// The JWT token 46 | public var token: String? 47 | /// The expiry timestamp 48 | public var expires: Int? 49 | 50 | @available(*, deprecated, message: "Do not use.") 51 | init() { 52 | fatalError("Swift 4.1 broke Codable synthesized inits") 53 | } 54 | 55 | enum CodingKeys: String, CodingKey { 56 | case id 57 | case customerID = "customer_id" 58 | case token 59 | case expires 60 | } 61 | } 62 | 63 | /// A data object used to update customers with 64 | open class UpdateCustomer { 65 | /// The ID of this customer 66 | public var id: String? 67 | /// The email of this customer 68 | public var email: String? 69 | /// The name of this customer 70 | public var name: String? 71 | /// The password of this customer 72 | public var password: String? 73 | 74 | func toDictionary() -> [String: Any] { 75 | var customerData: [String: Any] = [:] 76 | 77 | customerData["id"] = self.id 78 | customerData["email"] = self.email 79 | customerData["name"] = self.name 80 | customerData["password"] = self.password 81 | 82 | return customerData 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Sources/SDK/Models/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// The dimensions of a `File` 11 | public struct FileDimensions: Codable { 12 | /// The width of the file 13 | public let width: Float? 14 | /// The height of the file 15 | public let height: Float? 16 | 17 | } 18 | 19 | /// The meta information for a `File` 20 | public class FileMeta: Codable { 21 | /// The dimensions for a `File` 22 | public let dimensions: FileDimensions? 23 | /// The timsestamps for a `File` 24 | public let timestamps: Timestamps 25 | } 26 | 27 | /// Represents a `File` in Moltin 28 | open class File: Codable { 29 | /// The id of this file 30 | public var id: String 31 | /// The type of object 32 | public let type: String 33 | /// The name of this file 34 | public let fileName: String 35 | /// The MIME type of this fiel 36 | public let mimeType: String 37 | /// The file size of this file 38 | public let fileSize: Int 39 | /// Whether this file is public or not 40 | public let `public`: Bool 41 | /// The source link of this file 42 | public let link: [String: String] 43 | /// The external links of this file 44 | public let links: [String: String] 45 | /// The meta information for this file 46 | public let meta: FileMeta 47 | 48 | enum CodingKeys: String, CodingKey { 49 | case id 50 | case type 51 | case fileName = "file_name" 52 | case mimeType = "mime_type" 53 | case fileSize = "file_size" 54 | case `public` 55 | case link 56 | case links 57 | case meta 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Flow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Flow.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a `Entry` in Moltin 11 | open class Entry: Codable { 12 | /// The id of this entry 13 | public let id: String 14 | /// The type of this object 15 | public let type: String 16 | } 17 | 18 | /// Represents the meta information for a `Field` 19 | public struct FieldMeta: Codable { 20 | /// The timestamps for this `Field` 21 | public let timestamps: Timestamps 22 | } 23 | 24 | /// Represents a `Field` in Moltin 25 | open class Field: Codable { 26 | /// The id of this field 27 | public let id: String 28 | /// The type of this object 29 | public let type: String 30 | /// The type of this field - string / integer / boolean / float / date / relationship 31 | public let fieldType: String 32 | /// The slug for this field 33 | public let slug: String 34 | /// The name for this field 35 | public let name: String 36 | /// The description of this field 37 | public let description: String 38 | /// Whether this field is required or not 39 | public let required: Bool 40 | /// Whether this field is unique or not 41 | public let unique: Bool 42 | // public let `default`: Any 43 | /// Whether this field is enabled or not 44 | public let enabled: Bool 45 | // public let validationRules: [String: Any] 46 | /// The relationships for this Field 47 | public let relationships: Relationships? 48 | /// The meta information for this Field 49 | public let meta: FieldMeta? 50 | 51 | enum CodingKeys: String, CodingKey { 52 | case fieldType = "field_type" 53 | 54 | case id 55 | case type 56 | case slug 57 | case name 58 | case description 59 | case required 60 | case unique 61 | case enabled 62 | case relationships 63 | case meta 64 | } 65 | } 66 | 67 | /// Represents a `Flow` in Moltin 68 | open class Flow: Codable { 69 | /// The id of this flow 70 | public let id: String 71 | /// The type of this object 72 | public let type: String 73 | /// The name of this flow 74 | public let name: String 75 | /// The slug of this flow 76 | public let slug: String 77 | /// The description of this flow 78 | public let description: String 79 | /// Whether this flow is enabled or not 80 | public let enabled: Bool 81 | } 82 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Order.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Order.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 26/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents the meta information for an `Order` 11 | public class OrderMeta: Codable { 12 | /// The display price information for an order 13 | public let displayPrice: DisplayPrices 14 | /// The timestamps for an order 15 | public let timestamps: Timestamps 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case displayPrice = "display_price" 19 | case timestamps 20 | } 21 | } 22 | 23 | /// Represents the relationships for an `Order` 24 | public class OrderRelationships: Codable { 25 | /// The items in this order 26 | public let items: [String: [[String: String]]]? 27 | /// The customer information in this order 28 | public let customer: [String: [String: String]]? 29 | } 30 | 31 | /// Represents a `Order` in Moltin 32 | open class Order: Codable { 33 | /// This id of this order 34 | public let id: String 35 | /// The type of this object 36 | public let type: String 37 | /// incomplete / cancelled / complete 38 | public let status: String 39 | /// unpaid / authorized / paid / refunded 40 | public let payment: String 41 | /// fulfilled / unfulfilled 42 | public let shipping: String 43 | /// The customer for this order 44 | public let customer: Customer 45 | /// The shipping address for this order 46 | public let shippingAddress: Address 47 | /// The billing address for this order 48 | public let billingAddress: Address 49 | /// The external links for this order 50 | public let links: [String: String] 51 | /// The meta information for this order 52 | public let meta: OrderMeta 53 | /// The relationships for this order 54 | public let relationships: OrderRelationships? 55 | 56 | enum CodingKeys: String, CodingKey { 57 | case id 58 | case type 59 | case status 60 | case payment 61 | case shipping 62 | case customer 63 | case shippingAddress = "shipping_address" 64 | case billingAddress = "billing_address" 65 | case links 66 | case meta 67 | case relationships 68 | } 69 | } 70 | 71 | /// Denotes a successful order returned from the payment gateway 72 | open class OrderSuccess: Codable { 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Prices.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Prices.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 20/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a `DisplayPrice` in Moltin 11 | public struct DisplayPrice: Codable { 12 | /// The amount of money 13 | public let amount: Int 14 | /// The currency of this price 15 | public let currency: String 16 | /// The formatted display price 17 | public let formatted: String 18 | } 19 | 20 | /// Represents display prices in Moltin with and without tax, and the tax value 21 | public struct DisplayPrices: Codable { 22 | /// The display price including tax 23 | public let withTax: DisplayPrice 24 | /// The display price without tax 25 | public let withoutTax: DisplayPrice 26 | /// The display price for tax 27 | public let tax: DisplayPrice? 28 | 29 | enum CodingKeys: String, CodingKey { 30 | case withTax = "with_tax" 31 | case withoutTax = "without_tax" 32 | case tax 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Relationship.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Relationship.swift 3 | // moltin tvOS Example 4 | // 5 | // Created by Craig Tweedy on 22/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol HasRelationship { 11 | func decodeRelationships( 12 | fromRelationships relationships: Relationships?, 13 | withIncludes includes: IncludesContainer 14 | ) throws 15 | } 16 | 17 | /// Represents a relationship which can hold many items 18 | public class RelationshipMany: Codable { 19 | /// The relationship data objects 20 | public var data: [RelationshipData]? 21 | 22 | /** 23 | Returns all ID's for the relationships this object holds 24 | 25 | - Author: 26 | Craig Tweedy 27 | 28 | - returns: 29 | An array of UUID's as strings, representing the ID's of the relationship objects 30 | */ 31 | public func getIds() -> [String]? { 32 | return self.data?.map { $0.id } 33 | } 34 | } 35 | 36 | /// Represents a relationship which can hold a single item 37 | public class RelationshipSingle: Codable { 38 | /// The relationship data object 39 | public var data: RelationshipData? 40 | 41 | /** 42 | Returns the ID for the relationship this object holds 43 | 44 | - Author: 45 | Craig Tweedy 46 | 47 | - returns: 48 | An UUID as a string, representing the ID of the relationship object 49 | */ 50 | public func getId() -> String? { 51 | return self.data?.id 52 | } 53 | } 54 | 55 | /// Represents a relationship item 56 | public struct RelationshipData: Codable { 57 | /// The type of this relationship 58 | public let type: String 59 | /// The id of this relationship 60 | public let id: String 61 | } 62 | 63 | /// Represents all possible relationships a resource can have within Moltin 64 | open class Relationships: Codable { 65 | /// The `File` relationships 66 | public var files: RelationshipMany? 67 | /// The main image (`File`) relationship 68 | public var mainImage: RelationshipSingle? 69 | /// The `Category` relationships 70 | public var categories: RelationshipMany? 71 | /// The `Collection` relationships 72 | public var collections: RelationshipMany? 73 | /// The `Brand` relationships 74 | public var brands: RelationshipMany? 75 | /// The `Flow` relationship 76 | public var flow: RelationshipSingle? 77 | /// The items relationships 78 | public var items: RelationshipMany? 79 | /// The `Customer` relationship 80 | public var customer: RelationshipSingle? 81 | /// The `CartItem` relationship 82 | public var cartItem: RelationshipSingle? 83 | /// The `Product` relationships 84 | public var products: RelationshipMany? 85 | /// The `TaxItem` relationships 86 | public var taxes: RelationshipMany? 87 | 88 | enum CodingKeys: String, CodingKey { 89 | case mainImage = "main_image" 90 | case cartItem = "cart_item" 91 | 92 | case files 93 | case categories 94 | case collections 95 | case brands 96 | case flow 97 | case items 98 | case customer 99 | case products 100 | case taxes 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Sources/SDK/Models/Timestamps.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timestamps.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 20/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents common timestamps returned from Moltin 11 | open class Timestamps: Codable { 12 | /// When the resource was created 13 | public let createdAt: Date 14 | /// When the resource was updated 15 | public let updatedAt: Date? 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case createdAt = "created_at" 19 | case updatedAt = "updated_at" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/SDK/Requests/FlowRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowRequest.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 20/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// An entry point to make API calls relating to `Flow` and `Entry` objects 11 | public class FlowRequest: MoltinRequest { 12 | /// The API endpoint for this resource. 13 | public var endpoint: String = "/flows" 14 | 15 | /// The default collection handler type for `Flow` requests 16 | public typealias DefaultFlowCollectionRequestHandler = CollectionRequestHandler<[Flow]> 17 | /// The default object handler type for `Flow` requests 18 | public typealias DefaultFlowObjectRequestHandler = ObjectRequestHandler 19 | 20 | /// The default collection handler type for `Entry` requests 21 | public typealias DefaultEntryCollectionRequestHandler = CollectionRequestHandler<[Entry]> 22 | /// The default object handler type for `Entry` requests 23 | public typealias DefaultEntryObjectRequestHandler = ObjectRequestHandler 24 | 25 | /** 26 | Return all instances of type flow 27 | 28 | - parameters: 29 | - completionHandler: The handler to be called on success or failure 30 | */ 31 | public func all(completionHandler: @escaping DefaultFlowCollectionRequestHandler) -> MoltinRequest { 32 | return super.list(withPath: "\(self.endpoint)", completionHandler: completionHandler) 33 | } 34 | 35 | /** 36 | Return get an instance of flow by `id` 37 | 38 | - parameters: 39 | - forID: The ID of the object 40 | - completionHandler: The handler to be called on success or failure 41 | */ 42 | public func get(forID id: String, completionHandler: @escaping DefaultFlowObjectRequestHandler) -> MoltinRequest { 43 | return super.get(withPath: "\(self.endpoint)/\(id)", completionHandler: completionHandler) 44 | } 45 | 46 | /** 47 | Return all entries for the flow with the slug `slug` 48 | 49 | - parameters: 50 | - forSlug: The slug of the flow 51 | - completionHandler: The handler to be called on success or failure 52 | */ 53 | public func entries(forSlug slug: String, completionHandler: @escaping DefaultEntryCollectionRequestHandler) -> MoltinRequest { 54 | return super.get(withPath: "\(self.endpoint)/\(slug)/entries", completionHandler: completionHandler) 55 | } 56 | 57 | /** 58 | Return all custom entries for the flow with the slug `slug` 59 | 60 | - parameters: 61 | - forSlug: The slug of the flow 62 | - completionHandler: The handler to be called on success or failure 63 | */ 64 | public func entries(forSlug slug: String, completionHandler: @escaping CollectionRequestHandler) -> MoltinRequest { 65 | return super.get(withPath: "\(self.endpoint)/\(slug)", completionHandler: completionHandler) 66 | } 67 | 68 | /** 69 | Return an entry for the flow with the slug `slug` and an ID of `id` 70 | 71 | - parameters: 72 | - forSlug: The slug of the flow 73 | - forID: The ID of the entry 74 | - completionHandler: The handler to be called on success or failure 75 | */ 76 | public func entry(forSlug slug: String, forID id: String, completionHandler: @escaping DefaultEntryObjectRequestHandler) -> MoltinRequest { 77 | return super.get(withPath: "\(self.endpoint)/\(slug)/entries/\(id)", completionHandler: completionHandler) 78 | } 79 | 80 | /** 81 | Return a custom entry for the flow with the slug `slug` and an ID of `id` 82 | 83 | - parameters: 84 | - forSlug: The slug of the flow 85 | - forID: The ID of the entry 86 | - completionHandler: The handler to be called on success or failure 87 | */ 88 | public func entry(forSlug slug: String, forID id: String, completionHandler: @escaping ObjectRequestHandler) -> MoltinRequest { 89 | return super.get(withPath: "\(self.endpoint)/\(slug)/entries/\(id)", completionHandler: completionHandler) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Sources/SDK/Requests/MoltinAuth.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Auth.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MoltinAuthCredentials: Codable { 11 | var clientID: String 12 | var token: String 13 | var expires: Date 14 | 15 | init(clientID: String, token: String, expires: Date) { 16 | self.clientID = clientID 17 | self.token = token 18 | self.expires = expires 19 | } 20 | } 21 | 22 | class MoltinAuth { 23 | 24 | internal var http: MoltinHTTP 25 | internal var config: MoltinConfig 26 | 27 | var credentials: MoltinAuthCredentials? { 28 | didSet { 29 | UserDefaults.standard.set(try? JSONEncoder().encode(self.credentials), forKey: "Moltin.auth.credentials") 30 | UserDefaults.standard.synchronize() 31 | } 32 | } 33 | 34 | init(withConfiguration config: MoltinConfig) { 35 | if let data = UserDefaults.standard.value(forKey: "Moltin.auth.credentials") as? Data { 36 | self.credentials = try? JSONDecoder().decode(MoltinAuthCredentials.self, from: data) 37 | } 38 | 39 | self.http = MoltinHTTP(withSession: URLSession.shared) 40 | self.config = config 41 | } 42 | 43 | private var requiresRefresh: Bool { 44 | guard let expires = self.credentials?.expires, 45 | self.credentials?.token != nil, 46 | self.credentials?.clientID == self.config.clientID else { 47 | return true 48 | } 49 | 50 | return expires <= Date() 51 | } 52 | 53 | func authenticate(completionHandler: @escaping (Result<(token: String?, expires: Date?)>) -> Void) { 54 | guard self.requiresRefresh else { 55 | completionHandler(.success(result: (token: self.credentials?.token, expires: self.credentials?.expires))) 56 | return 57 | } 58 | 59 | var urlRequest: URLRequest 60 | do { 61 | urlRequest = try self.http.buildURLRequest( 62 | withConfiguration: self.config, 63 | withPath: "/oauth/access_token", 64 | withQueryParameters: [], 65 | includeVersion: false 66 | ) 67 | } catch { 68 | completionHandler(.failure(error: error)) 69 | return 70 | } 71 | 72 | var request = self.http.configureRequest(urlRequest, withToken: nil, withConfig: self.config) 73 | request.httpMethod = "POST" 74 | request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") 75 | 76 | let data = "client_id=\(self.config.clientID)&grant_type=implicit".data(using: .utf8, allowLossyConversion: false) 77 | request.httpBody = data 78 | 79 | self.http.executeRequest(request) { (data, _, error) in 80 | if let error = error { 81 | completionHandler(.failure(error: error)) 82 | return 83 | } 84 | 85 | guard let data = data else { 86 | completionHandler(.failure(error: MoltinError.couldNotAuthenticate)) 87 | return 88 | } 89 | 90 | let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) 91 | let json = jsonObject as? [String: Any] 92 | 93 | guard 94 | let accessToken = json?["access_token"] as? String, 95 | let expires = json?["expires"] as? Int else { 96 | completionHandler(.failure(error: MoltinError.couldNotAuthenticate)) 97 | return 98 | } 99 | 100 | self.credentials = MoltinAuthCredentials( 101 | clientID: self.config.clientID, 102 | token: accessToken, 103 | expires: Date(timeIntervalSince1970: TimeInterval(expires)) 104 | ) 105 | 106 | completionHandler(.success(result: (token: self.credentials?.token, expires: self.credentials?.expires))) 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/CodableExtract.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodableExtract.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 21/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Container type alias for includes 11 | public typealias IncludesContainer = [String: IncludesData] 12 | /// Data schema type alias for includes 13 | public typealias IncludesData = [[String: Any]] 14 | 15 | extension Decodable { 16 | 17 | /// Faciliates the use of extracting a single object from `includes` based on `relationships`, and converting that object to type `T`, which must be `Codable` 18 | public func decodeSingle( 19 | fromRelationships relationships: RelationshipSingle?, 20 | withIncludes includes: IncludesData? = []) throws -> T? { 21 | let data: T? = try self.extractObject(withKey: "id", 22 | withValue: relationships?.getId(), 23 | fromIncludes: includes) 24 | 25 | return data 26 | } 27 | 28 | /// Faciliates the use of extracting multiple objects from `includes` based on `relationships`, and converting those objects to type `T`, which must be `Codable` 29 | public func decodeMany( 30 | fromRelationships relationships: RelationshipMany?, 31 | withIncludes includes: IncludesData? = []) throws -> [T]? { 32 | let data: [T]? = try self.extractArray( 33 | withKey: "id", 34 | withValues: relationships?.getIds(), 35 | fromIncludes: includes) 36 | 37 | return data 38 | } 39 | 40 | private func extractObject(withKey key: String, withValue value: String?, fromIncludes includes: IncludesData? = []) throws -> T? { 41 | 42 | let foundItem = includes?.first { (obj) -> Bool in 43 | return obj[key] as? String == value 44 | } 45 | guard let item = foundItem else { 46 | return nil 47 | } 48 | let itemData = try JSONSerialization.data(withJSONObject: item, options: []) 49 | 50 | let decoder = JSONDecoder.dateFormattingDecoder() 51 | return try decoder.decode(T.self, from: itemData) 52 | } 53 | 54 | private func extractArray(withKey key: String, withValues values: [String]? = [], fromIncludes includes: IncludesData? = []) throws -> [T]? { 55 | 56 | let items = includes?.filter { (obj) -> Bool in 57 | return values?.contains(obj[key] as? String ?? "") ?? false 58 | } ?? [] 59 | let itemData = try JSONSerialization.data(withJSONObject: items, options: []) 60 | 61 | let decoder = JSONDecoder.dateFormattingDecoder() 62 | return try decoder.decode([T].self, from: itemData) 63 | } 64 | 65 | } 66 | 67 | extension JSONDecoder { 68 | /// Returns a default `JSONDecoder` which can decode ISO8601 dates with and without milliseconds 69 | static public func dateFormattingDecoder() -> JSONDecoder { 70 | let decoder = JSONDecoder() 71 | decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full) 72 | decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in 73 | let dateString = try decoder.singleValueContainer().decode(String.self) 74 | if let date = DateFormatter.iso8601Full.date(from: dateString) { 75 | return date 76 | } 77 | 78 | if let date = DateFormatter.iso8601NoMilli.date(from: dateString) { 79 | return date 80 | } 81 | 82 | throw MoltinError.couldNotParseDate 83 | }) 84 | return decoder 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/Config.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Config.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// `MoltinConfig` holds information about the general configuration of the SDK, such as the client ID or the locale, for use during API calls. 11 | public struct MoltinConfig { 12 | /// The moltin client ID to use to connect to a store 13 | public var clientID: String 14 | /// HTTP or HTTPS 15 | public var scheme: String 16 | /// The URL host of the API 17 | public var host: String 18 | /// The version of the API 19 | public var version: String 20 | 21 | /// The locale to use for langauges and currencies 22 | public var locale: Locale = Locale.current 23 | 24 | /// Initialise the config with a clientID, scheme, host, and version 25 | public init(clientID: String, scheme: String, host: String, version: String) { 26 | self.clientID = clientID 27 | self.scheme = scheme 28 | self.host = host 29 | self.version = version 30 | } 31 | 32 | /// Initialise the config with a clientID, scheme, host, version, and locale 33 | public init(clientID: String, scheme: String, host: String, version: String, locale: Locale) { 34 | self.init(clientID: clientID, scheme: scheme, host: host, version: version) 35 | self.locale = locale 36 | } 37 | 38 | /// Returns a default config set up with a clientID 39 | static public func `default`(withClientID clientID: String) -> MoltinConfig { 40 | return MoltinConfig( 41 | clientID: clientID, 42 | scheme: "https", 43 | host: "api.moltin.com", 44 | version: "v2") 45 | } 46 | 47 | /// Returns a default config set up with a clientID and a locale 48 | static public func `default`(withClientID clientID: String, withLocale locale: Locale) -> MoltinConfig { 49 | return MoltinConfig( 50 | clientID: clientID, 51 | scheme: "https", 52 | host: "api.moltin.com", 53 | version: "v2", 54 | locale: locale) 55 | } 56 | } 57 | 58 | extension MoltinConfig: Equatable {} 59 | 60 | /// Validates that two `MoltinConfig` objects and equal 61 | public func == (lhs: MoltinConfig, rhs: MoltinConfig) -> Bool { 62 | return lhs.clientID == rhs.clientID && 63 | lhs.scheme == rhs.scheme && 64 | lhs.host == rhs.host && 65 | lhs.version == rhs.version && 66 | lhs.locale == rhs.locale 67 | } 68 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/DataSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataSerializer.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 26/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol DataSerializer { 11 | func serialize(_ data: Any) throws -> Data 12 | func deserialize(_ data: Data?) throws -> Any 13 | } 14 | 15 | class MoltinDataSerializer: DataSerializer { 16 | 17 | func serialize(_ data: Any) throws -> Data { 18 | let payload: [String: Any] = ["data": data] 19 | return try JSONSerialization.data(withJSONObject: payload, options: []) 20 | } 21 | 22 | func deserialize(_ data: Data?) throws -> Any { 23 | guard let data = data else { return [:] } 24 | return try JSONSerialization.jsonObject(with: data, options: []) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/DateFormatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateFormatter.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 20/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension DateFormatter { 11 | static let iso8601Full: DateFormatter = { 12 | let formatter = DateFormatter() 13 | formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" 14 | formatter.calendar = Calendar(identifier: .iso8601) 15 | formatter.timeZone = TimeZone.current 16 | formatter.locale = Locale.current 17 | return formatter 18 | }() 19 | 20 | static let iso8601NoMilli: DateFormatter = { 21 | let formatter = DateFormatter() 22 | formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 23 | formatter.calendar = Calendar(identifier: .iso8601) 24 | formatter.timeZone = TimeZone.current 25 | formatter.locale = Locale.current 26 | return formatter 27 | }() 28 | } 29 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// `MoltinError` encapsultes various errors that the SDK can return, as well as returning additional information if there are errors such as decoding errors 11 | public enum MoltinError: Error { 12 | /// Thrown if a request could not authenticate 13 | case couldNotAuthenticate 14 | /// Thrown if the response returned an unacceptable error 15 | case responseError(underlyingError: Error?) 16 | /// Thrown if the data could not be added to the request 17 | case couldNotSetData 18 | /// Thrown if the response data can not be parsed correctly. Encapsulates a `DecodingError` for more infromation. 19 | case couldNotParseData(underlyingError: DecodingError?) 20 | /// Thrown if the response does not have any data 21 | case couldNotFindData 22 | /// Thrown if the response cannot parse a `Date` 23 | case couldNotParseDate 24 | /// Thrown if the request is not formed correctly 25 | case unacceptableRequest 26 | /// Thrown if no data was returned from the API 27 | case noData 28 | } 29 | 30 | extension MoltinError: LocalizedError { 31 | public var errorDescription: String? { 32 | switch self { 33 | case .couldNotParseData(let error): 34 | return error?.localizedDescription ?? "Could not parse data" 35 | case .couldNotAuthenticate: 36 | return "Could not authenticate" 37 | case .responseError(let error): 38 | return error?.localizedDescription ?? "Response did not succeed" 39 | case .couldNotFindData: 40 | return "Could not find any data" 41 | case .couldNotParseDate: 42 | return "Date could not be parsed" 43 | case .noData: 44 | return "No data found" 45 | case .unacceptableRequest: 46 | return "Could not compose request" 47 | case .couldNotSetData: 48 | return "Could not serialize data" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/Pagination.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// `PaginationResponse` wraps around a list endpoint response, to give context to the user about the pagination information 11 | open class PaginatedResponse: Codable { 12 | /// Holds the real type of T ([Product] / Product / Brand / Collection) etc 13 | public typealias ContainedType = T 14 | 15 | /// The data returned for this response 16 | public var data: ContainedType? 17 | /// The external links for this response 18 | public var links: [String: String?]? 19 | /// The meta information for this response 20 | public var meta: PaginationMeta? 21 | } 22 | 23 | /// `PaginationMeta` gives information about the pagination details to the user, such as result information and page information 24 | open class PaginationMeta: Codable { 25 | /// The results information for this paginated response 26 | public let results: [String: Int]? 27 | /// The page information for this response 28 | public let page: [String: Int]? 29 | } 30 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/Parser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension CodingUserInfoKey { 11 | static let includes = CodingUserInfoKey(rawValue: "com.moltin.includes")! 12 | } 13 | 14 | class MoltinParser { 15 | 16 | var decoder: JSONDecoder 17 | 18 | init(withDecoder decoder: JSONDecoder) { 19 | self.decoder = decoder 20 | } 21 | 22 | func singleObjectHandler(withData data: Data?, withResponse: URLResponse?, completionHandler: @escaping ObjectRequestHandler) { 23 | guard let data = data else { 24 | completionHandler(Result.failure(error: MoltinError.noData)) 25 | return 26 | } 27 | 28 | do { 29 | let object: T = try self.parseObject(data: data) 30 | completionHandler(Result.success(result: object)) 31 | } catch { 32 | completionHandler(Result.failure(error: error)) 33 | } 34 | } 35 | 36 | func collectionHandler(withData data: Data?, withResponse: URLResponse?, completionHandler: @escaping CollectionRequestHandler) { 37 | guard let data = data else { 38 | completionHandler(Result.failure(error: MoltinError.noData)) 39 | return 40 | } 41 | do { 42 | let paginatedResponse: PaginatedResponse = try self.parseCollection(data: data) 43 | completionHandler(Result.success(result: paginatedResponse)) 44 | } catch { 45 | completionHandler(Result.failure(error: error)) 46 | } 47 | } 48 | 49 | private func parseCollection(data: Data) throws -> PaginatedResponse { 50 | let collection: PaginatedResponse 51 | do { 52 | let parsedData = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] 53 | self.decoder.userInfo[.includes] = parsedData?["included"] as? [String: Any] ?? [:] 54 | collection = try self.decoder.decode(PaginatedResponse.self, from: data) 55 | } catch { 56 | throw MoltinError.couldNotParseData(underlyingError: error as? DecodingError) 57 | } 58 | return collection 59 | } 60 | 61 | private func parseObject(data: Data) throws -> T { 62 | let object: T 63 | do { 64 | 65 | let parsedData = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] 66 | let jsonObject: Any? = parsedData?["data"] != nil ? parsedData?["data"] : parsedData 67 | guard let jsonObj = jsonObject else { 68 | throw MoltinError.couldNotFindData 69 | } 70 | let jsonData = try JSONSerialization.data(withJSONObject: jsonObj, options: []) 71 | self.decoder.userInfo[.includes] = parsedData?["included"] as? [String: Any] ?? [:] 72 | object = try self.decoder.decode(T.self, from: jsonData) 73 | } catch MoltinError.couldNotFindData { 74 | throw MoltinError.couldNotFindData 75 | } catch { 76 | throw MoltinError.couldNotParseData(underlyingError: error as? DecodingError) 77 | } 78 | return object 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/Query.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Query.swift 3 | // moltin iOS 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | /// `MoltinInclude` represents various resources which can be included into other API calls, such as including the collections assigned to products 11 | /// This struct is for use in the `MoltinRequest.include(...)` method 12 | public struct MoltinInclude: RawRepresentable, Equatable { 13 | 14 | public typealias RawValue = String 15 | 16 | public var rawValue: String 17 | 18 | /// Includes `File` objects 19 | public static let files = MoltinInclude(rawValue: "files") 20 | /// Includes `Product` objects 21 | public static let products = MoltinInclude(rawValue: "products") 22 | /// Includes `Collection` objects 23 | public static let collections = MoltinInclude(rawValue: "collections") 24 | /// Includes `Brand` objects 25 | public static let brands = MoltinInclude(rawValue: "brands") 26 | /// Includes `Category` objects 27 | public static let categories = MoltinInclude(rawValue: "categories") 28 | /// Includes a `File` object representing the main image 29 | public static let mainImage = MoltinInclude(rawValue: "main_image") 30 | /// Includes `TaxItem` objects 31 | public static let taxes = MoltinInclude(rawValue: "tax_items") 32 | 33 | public init(rawValue: String) { 34 | self.rawValue = rawValue 35 | } 36 | 37 | } 38 | 39 | /// `MoltinFilterOperator` represents various operations that can be applied to `MoltinRequest.filter(...)` 40 | /// These parameters allow a user to filter resources. 41 | public struct MoltinFilterOperator: RawRepresentable, Equatable { 42 | 43 | public typealias RawValue = String 44 | 45 | public var rawValue: String 46 | 47 | /// Represents an "equals" filter 48 | public static let equal = MoltinFilterOperator(rawValue: "eq") 49 | /// Represents an "equals" filter 50 | public static let like = MoltinFilterOperator(rawValue: "like") 51 | ///Represents a "greater than" filter 52 | public static let greaterThan = MoltinFilterOperator(rawValue: "gt") 53 | ///Represents a "greater than or equal to" filter 54 | public static let greaterThanOrEqual = MoltinFilterOperator(rawValue: "ge") 55 | ///Represents a "less than" filter 56 | public static let lessThan = MoltinFilterOperator(rawValue: "lt") 57 | ///Represents a "less than or equal to" filter 58 | public static let lessThanOrEqual = MoltinFilterOperator(rawValue: "le") 59 | 60 | public init(rawValue: String) { 61 | self.rawValue = rawValue 62 | } 63 | 64 | } 65 | 66 | /// `MoltinQuery` encapsulates all query parameters applied to a request, as well as converting these parameters to `[URLQueryItem]` 67 | open class MoltinQuery { 68 | var withIncludes: [MoltinInclude]? 69 | var withSorting: String? 70 | var withLimit: String? 71 | var withOffset: String? 72 | var withFilter: [(MoltinFilterOperator, String, String)] = [] 73 | 74 | func toURLQueryItems() -> [URLQueryItem] { 75 | var queryParams: [URLQueryItem] = [] 76 | 77 | if let includes = self.withIncludes { 78 | queryParams.append(URLQueryItem(name: "include", value: includes.map { $0.rawValue }.joined(separator: ","))) 79 | } 80 | 81 | if let sort = self.withSorting { 82 | queryParams.append(URLQueryItem(name: "sort", value: sort)) 83 | } 84 | 85 | if let limit = self.withLimit { 86 | queryParams.append(URLQueryItem(name: "page[limit]", value: limit)) 87 | } 88 | 89 | if let offset = self.withOffset { 90 | queryParams.append(URLQueryItem(name: "page[offset]", value: offset)) 91 | } 92 | 93 | if self.withFilter.count > 0 { 94 | let filterString = self.withFilter.map { (op, key, value) -> String in 95 | return "\(op.rawValue)(\(key),\(value))" 96 | }.joined(separator: ":") 97 | queryParams.append(URLQueryItem(name: "filter", value: filterString)) 98 | } 99 | 100 | return queryParams 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Sources/SDK/Utils/URLSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSession.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 22/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void 11 | 12 | protocol URLSessionProtocol { 13 | func dataTask(with urlRequest: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol 14 | } 15 | 16 | extension URLSession: URLSessionProtocol { 17 | 18 | func dataTask(with urlRequest: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol { 19 | let dataTask: URLSessionDataTask = self.dataTask(with: urlRequest, completionHandler: completionHandler) 20 | return dataTask as URLSessionDataTaskProtocol 21 | } 22 | 23 | } 24 | 25 | protocol URLSessionDataTaskProtocol { 26 | func resume() 27 | func cancel() 28 | } 29 | 30 | extension URLSessionDataTask: URLSessionDataTaskProtocol { } 31 | -------------------------------------------------------------------------------- /Sources/moltin.h: -------------------------------------------------------------------------------- 1 | // 2 | // moltin.h 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | @import Foundation; 9 | 10 | FOUNDATION_EXPORT double moltinVersionNumber; 11 | FOUNDATION_EXPORT const unsigned char moltinVersionString[]; 12 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/Data/Brands.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Brands.swift 3 | // moltin iOS Tests 4 | // 5 | // Created by Craig Tweedy on 29/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | class MockBrandDataFactory { 11 | static let brandData = """ 12 | { 13 | "data": { 14 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 15 | "type": "brand", 16 | "name": "Cool Clothing", 17 | "slug": "cool-clothing", 18 | "description": "Cool clothing make cool clothes!", 19 | "status": "live" 20 | } 21 | } 22 | """ 23 | 24 | static let multiBrandData = """ 25 | { 26 | "data": [{ 27 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 28 | "type": "brand", 29 | "name": "Cool Clothing", 30 | "slug": "cool-clothing", 31 | "description": "Cool clothing make cool clothes!", 32 | "status": "live" 33 | }] 34 | } 35 | """ 36 | 37 | static let customBrandData = """ 38 | { 39 | "data": { 40 | "author": { 41 | "name": "Craig" 42 | }, 43 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 44 | "type": "brand", 45 | "name": "Cool Clothing", 46 | "slug": "cool-clothing", 47 | "description": "Cool clothing make cool clothes!", 48 | "status": "live" 49 | } 50 | } 51 | """ 52 | 53 | static let customMultiBrandData = """ 54 | { 55 | "data": [{ 56 | "author": { 57 | "name": "Craig" 58 | }, 59 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 60 | "type": "brand", 61 | "name": "Cool Clothing", 62 | "slug": "cool-clothing", 63 | "description": "Cool clothing make cool clothes!", 64 | "status": "live" 65 | }] 66 | } 67 | """ 68 | 69 | static let treeData = """ 70 | { 71 | "data": [{ 72 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 73 | "type": "brand", 74 | "name": "Cool Clothing", 75 | "slug": "cool-clothing", 76 | "description": "Cool clothing make cool clothes!", 77 | "status": "live", 78 | "children": [{ 79 | "id": "41b56d92-ab99-4802-a2c1-be150848c629", 80 | "type": "brand", 81 | "name": "Sub Brand", 82 | "slug": "sub-brand", 83 | "description": "Sub Brand!", 84 | "status": "live", 85 | }] 86 | }] 87 | } 88 | """ 89 | 90 | static let customTreeData = """ 91 | { 92 | "data": [{ 93 | "author": { 94 | "name": "Craig" 95 | }, 96 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 97 | "type": "brand", 98 | "name": "Cool Clothing", 99 | "slug": "cool-clothing", 100 | "description": "Cool clothing make cool clothes!", 101 | "status": "live", 102 | "children": [{ 103 | "id": "41b56d92-ab99-4802-a2c1-be150848c629", 104 | "type": "brand", 105 | "name": "Sub Brand", 106 | "slug": "sub-brand", 107 | "description": "Sub Brand!", 108 | "status": "live", 109 | }] 110 | }] 111 | } 112 | """ 113 | } 114 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/Data/Category.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Category.swift 3 | // moltin iOS Tests 4 | // 5 | // Created by Craig Tweedy on 03/04/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | class MockCategoryDataFactory { 11 | 12 | static let categoryData = """ 13 | { 14 | "data": { 15 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 16 | "type": "category", 17 | "name": "Clothing", 18 | "slug": "clothing", 19 | "description": "Browse our clothing line", 20 | "status": "live" 21 | } 22 | } 23 | """ 24 | 25 | static let multiCategoryData = """ 26 | { 27 | "data": [{ 28 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 29 | "type": "category", 30 | "name": "Clothing", 31 | "slug": "clothing", 32 | "description": "Browse our clothing line", 33 | "status": "live" 34 | }] 35 | } 36 | """ 37 | 38 | static let customCategoryData = """ 39 | { 40 | "data": { 41 | "author": { 42 | "name": "Craig" 43 | }, 44 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 45 | "type": "category", 46 | "name": "Clothing", 47 | "slug": "clothing", 48 | "description": "Browse our clothing line", 49 | "status": "live" 50 | } 51 | } 52 | """ 53 | 54 | static let customMultiCategoryData = """ 55 | { 56 | "data": [{ 57 | "author": { 58 | "name": "Craig" 59 | }, 60 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 61 | "type": "category", 62 | "name": "Clothing", 63 | "slug": "clothing", 64 | "description": "Browse our clothing line", 65 | "status": "live" 66 | }] 67 | } 68 | """ 69 | 70 | static let treeData = """ 71 | { 72 | "data": [{ 73 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 74 | "type": "category", 75 | "name": "Clothing", 76 | "slug": "clothing", 77 | "description": "Browse our clothing line", 78 | "status": "live", 79 | "children": [{ 80 | "id": "41b56d92-ab99-4802-a2c1-be150848c629", 81 | "type": "category", 82 | "name": "Sub-Category", 83 | "slug": "Sub-Category", 84 | "description": "Sub Cat!", 85 | "status": "live" 86 | }] 87 | }] 88 | } 89 | """ 90 | 91 | static let customTreeData = """ 92 | { 93 | "data": [{ 94 | "author": { 95 | "name": "Craig" 96 | }, 97 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 98 | "type": "category", 99 | "name": "Clothing", 100 | "slug": "clothing", 101 | "description": "Browse our clothing line", 102 | "status": "live", 103 | "children": [{ 104 | "id": "41b56d92-ab99-4802-a2c1-be150848c629", 105 | "type": "category", 106 | "name": "Sub-Category", 107 | "slug": "Sub-Category", 108 | "description": "Sub Cat!", 109 | "status": "live" 110 | }] 111 | }] 112 | } 113 | """ 114 | } 115 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/Data/Collection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Collection.swift 3 | // moltin iOS Tests 4 | // 5 | // Created by Craig Tweedy on 03/04/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | class MockCollectionDataFactory { 11 | 12 | static let collectionData = """ 13 | { 14 | "data": { 15 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 16 | "type": "collection", 17 | "name": "Winter Season", 18 | "slug": "winter-season", 19 | "description": "Our Winter Season is now live!", 20 | "status": "live" 21 | } 22 | } 23 | """ 24 | 25 | static let multiCollectionData = """ 26 | { 27 | "data": [{ 28 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 29 | "type": "collection", 30 | "name": "Winter Season", 31 | "slug": "winter-season", 32 | "description": "Our Winter Season is now live!", 33 | "status": "live" 34 | }] 35 | } 36 | """ 37 | 38 | static let customCollectionData = """ 39 | { 40 | "data": { 41 | "author": { 42 | "name": "Craig" 43 | }, 44 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 45 | "type": "collection", 46 | "name": "Winter Season", 47 | "slug": "winter-season", 48 | "description": "Our Winter Season is now live!", 49 | "status": "live" 50 | } 51 | } 52 | """ 53 | 54 | static let customMultiCollectionData = """ 55 | { 56 | "data": [{ 57 | "author": { 58 | "name": "Craig" 59 | }, 60 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 61 | "type": "collection", 62 | "name": "Winter Season", 63 | "slug": "winter-season", 64 | "description": "Our Winter Season is now live!", 65 | "status": "live" 66 | }] 67 | } 68 | """ 69 | 70 | static let treeData = """ 71 | { 72 | "data": [{ 73 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 74 | "type": "collection", 75 | "name": "Winter Season", 76 | "slug": "winter-season", 77 | "description": "Our Winter Season is now live!", 78 | "status": "live", 79 | "children": [{ 80 | "id": "41b56d92-ab99-4802-a2c1-be150848c629", 81 | "type": "collection", 82 | "name": "Sub Collection!", 83 | "slug": "sub-collection", 84 | "description": "Sub collection", 85 | "status": "live" 86 | }] 87 | }] 88 | } 89 | """ 90 | 91 | static let customTreeData = """ 92 | { 93 | "data": [{ 94 | "author": { 95 | "name": "Craig" 96 | }, 97 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 98 | "type": "collection", 99 | "name": "Winter Season", 100 | "slug": "winter-season", 101 | "description": "Our Winter Season is now live!", 102 | "status": "live", 103 | "children": [{ 104 | "id": "41b56d92-ab99-4802-a2c1-be150848c629", 105 | "type": "collection", 106 | "name": "Sub Collection!", 107 | "slug": "sub-collection", 108 | "description": "Sub collection", 109 | "status": "live" 110 | }] 111 | }] 112 | } 113 | """ 114 | } 115 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/Data/Currency.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Currency.swift 3 | // moltin iOS Tests 4 | // 5 | // Created by Craig Tweedy on 03/04/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | class MockCurrencyDataFactory { 11 | 12 | static let currencyData = """ 13 | { 14 | "data": { 15 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 16 | "type": "currency", 17 | "code": "USD", 18 | "exchange_rate": 1, 19 | "format": "${price}", 20 | "decimal_point": ".", 21 | "thousand_separator": ",", 22 | "decimal_places": 2, 23 | "default": true, 24 | "enabled": true, 25 | "links": { 26 | "self": "https://api.moltin.com/currencies/7e5dd85a-f1eb-4025-8d2a-42ff3a37828f" 27 | }, 28 | "meta": { 29 | "timestamps": { 30 | "created_at": "2017-02-17T19:57:39.882Z", 31 | "updated_at": "2017-09-12T10:50:09.582Z" 32 | } 33 | } 34 | } 35 | } 36 | """ 37 | 38 | static let multiCurrencyData = """ 39 | { 40 | "data": [{ 41 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 42 | "type": "currency", 43 | "code": "USD", 44 | "exchange_rate": 1, 45 | "format": "${price}", 46 | "decimal_point": ".", 47 | "thousand_separator": ",", 48 | "decimal_places": 2, 49 | "default": true, 50 | "enabled": true, 51 | "links": { 52 | "self": "https://api.moltin.com/currencies/7e5dd85a-f1eb-4025-8d2a-42ff3a37828f" 53 | }, 54 | "meta": { 55 | "timestamps": { 56 | "created_at": "2017-02-17T19:57:39.882Z", 57 | "updated_at": "2017-09-12T10:50:09.582Z" 58 | } 59 | } 60 | }] 61 | } 62 | """ 63 | } 64 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/Data/Field.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Field.swift 3 | // moltin iOS Tests 4 | // 5 | // Created by Craig Tweedy on 03/04/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | class MockFieldDataFactory { 11 | 12 | static let fieldData = """ 13 | { 14 | "data": { 15 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 16 | "type": "field", 17 | "name": "Product Rating", 18 | "slug": "product-rating", 19 | "field_type": "integer", 20 | "validation_rules": [{ 21 | "type": "between", 22 | "options": { 23 | "from": 1, 24 | "to": 5 25 | } 26 | }], 27 | "description": "Average rating as given by our users", 28 | "required": false, 29 | "unique": false, 30 | "default": 0, 31 | "enabled": true, 32 | "order": 1, 33 | "relationships": { 34 | "flow": { 35 | "data": { 36 | "type": "flow", 37 | "id": "b2895a6c-8c12-4515-9e4b-a305769e7b25" 38 | } 39 | } 40 | } 41 | } 42 | } 43 | """ 44 | 45 | static let multiFieldData = """ 46 | { 47 | "data": [{ 48 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 49 | "type": "field", 50 | "name": "Product Rating", 51 | "slug": "product-rating", 52 | "field_type": "integer", 53 | "validation_rules": [{ 54 | "type": "between", 55 | "options": { 56 | "from": 1, 57 | "to": 5 58 | } 59 | }], 60 | "description": "Average rating as given by our users", 61 | "required": false, 62 | "unique": false, 63 | "default": 0, 64 | "enabled": true, 65 | "order": 1, 66 | "relationships": { 67 | "flow": { 68 | "data": { 69 | "type": "flow", 70 | "id": "b2895a6c-8c12-4515-9e4b-a305769e7b25" 71 | } 72 | } 73 | } 74 | }] 75 | } 76 | """ 77 | } 78 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/Data/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // moltin iOS Tests 4 | // 5 | // Created by Craig Tweedy on 03/04/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | class MockFileDataFactory { 11 | 12 | static let fileData = """ 13 | { 14 | "data": { 15 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 16 | "type": "file", 17 | "link": { 18 | "href": "https://s3-eu-west-1.amazonaws.com/bkt-api-moltin-com/2a85964e-cb3d-482a-ab02-0f0e47ab5662/273d3ff0-5034-4986-8786-0ff97450745.jpg" 19 | }, 20 | "file_name": "image.jpg", 21 | "mime_type": "image/jpeg", 22 | "file_size": 20953, 23 | "public": true, 24 | "meta": { 25 | "dimensions": { 26 | "width": 1600, 27 | "height": 800 28 | }, 29 | "timestamps": { 30 | "created_at": "2017-08-14T10:47:45.191Z" 31 | } 32 | }, 33 | "links": { 34 | "current": "https://api.moltin.com/v2/files/272d3ff0-5034-4986-8786-0ff97450745d" 35 | } 36 | } 37 | } 38 | """ 39 | 40 | static let multiFileData = """ 41 | { 42 | "data": [{ 43 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 44 | "type": "file", 45 | "link": { 46 | "href": "https://s3-eu-west-1.amazonaws.com/bkt-api-moltin-com/2a85964e-cb3d-482a-ab02-0f0e47ab5662/273d3ff0-5034-4986-8786-0ff97450745.jpg" 47 | }, 48 | "file_name": "image.jpg", 49 | "mime_type": "image/jpeg", 50 | "file_size": 20953, 51 | "public": true, 52 | "meta": { 53 | "dimensions": { 54 | "width": 1600, 55 | "height": 800 56 | }, 57 | "timestamps": { 58 | "created_at": "2017-08-14T10:47:45.191Z" 59 | } 60 | }, 61 | "links": { 62 | "current": "https://api.moltin.com/v2/files/272d3ff0-5034-4986-8786-0ff97450745d" 63 | } 64 | }] 65 | } 66 | """ 67 | 68 | static let fileDataNoDimensions = """ 69 | { 70 | "data": { 71 | "id": "51b56d92-ab99-4802-a2c1-be150848c629", 72 | "type": "file", 73 | "link": { 74 | "href": "https://s3-eu-west-1.amazonaws.com/bkt-api-moltin-com/2a85964e-cb3d-482a-ab02-0f0e47ab5662/273d3ff0-5034-4986-8786-0ff97450745.jpg" 75 | }, 76 | "file_name": "image.jpg", 77 | "mime_type": "image/jpeg", 78 | "file_size": 20953, 79 | "public": true, 80 | "meta": { 81 | "dimensions": { 82 | }, 83 | "timestamps": { 84 | "created_at": "2017-08-14T10:47:45.191Z" 85 | } 86 | }, 87 | "links": { 88 | "current": "https://api.moltin.com/v2/files/272d3ff0-5034-4986-8786-0ff97450745d" 89 | } 90 | } 91 | } 92 | """ 93 | } 94 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/FlowTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowRequest.swift 3 | // moltin 4 | // 5 | // Created by Craig Tweedy on 20/03/2018. 6 | // 7 | 8 | import XCTest 9 | 10 | @testable 11 | import moltin 12 | 13 | class FlowTests: XCTestCase { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Tests/moltin iOS 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 | -------------------------------------------------------------------------------- /Tests/moltin iOS Tests/PaymentMethodTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PaymentMethodTests.swift 3 | // moltin iOS Tests 4 | // 5 | // Created by Craig Tweedy on 16/12/2018. 6 | // 7 | 8 | import XCTest 9 | import moltin 10 | 11 | class PaymentMethodTests: XCTestCase { 12 | 13 | func testStripeTokenPaymentMethodToken() { 14 | let method = StripeToken(withStripeToken: "12345") 15 | let paymentData = method.paymentData 16 | XCTAssertEqual((paymentData["payment"] as? String), "12345") 17 | } 18 | 19 | func testStripeTokenPaymentMethodEmptyOptions() { 20 | let method = StripeToken(withStripeToken: "12345") 21 | let paymentData = method.paymentData 22 | XCTAssertNil(paymentData["options"]) 23 | } 24 | 25 | func testStripeTokenPaymentMethodWithOptions() { 26 | let method = StripeToken(withStripeToken: "12345", withOptions: [ 27 | "destination": "6789" 28 | ]) 29 | let paymentData = method.paymentData 30 | XCTAssertNotNil(paymentData["options"]) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Tests/moltin tvOS 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 | -------------------------------------------------------------------------------- /Tests/moltin tvOS Tests/moltin_tvOS_Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // moltin_tvOS_Tests.swift 3 | // moltin tvOS Tests 4 | // 5 | // Created by Craig Tweedy on 21/02/2018. 6 | // 7 | 8 | import XCTest 9 | 10 | class MoltinTVOSTests: XCTestCase { 11 | 12 | override func setUp() { 13 | super.setUp() 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDown() { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | super.tearDown() 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /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 | 98% 23 | 24 | 25 | 98% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.moltin 7 | CFBundleName 8 | moltin 9 | DocSetPlatformFamily 10 | moltin 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/moltin.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 | 98% 23 | 24 | 25 | 98% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/docsets/moltin.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/docsets/moltin.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/docsets/moltin.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/docsets/moltin.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/moltin.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 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | 39 | // Dumb down quotes within code blocks that delimit strings instead of quotations 40 | // https://github.com/realm/jazzy/issues/714 41 | $("code q").replaceWith(function () { 42 | return ["\"", $(this).contents(), "\""]; 43 | }); 44 | -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var searchIndex = lunr(function() { 3 | this.ref('url'); 4 | this.field('name'); 5 | }); 6 | 7 | var $typeahead = $('[data-typeahead]'); 8 | var $form = $typeahead.parents('form'); 9 | var searchURL = $form.attr('action'); 10 | 11 | function displayTemplate(result) { 12 | return result.name; 13 | } 14 | 15 | function suggestionTemplate(result) { 16 | var t = '
'; 17 | t += '' + result.name + ''; 18 | if (result.parent_name) { 19 | t += '' + result.parent_name + ''; 20 | } 21 | t += '
'; 22 | return t; 23 | } 24 | 25 | $typeahead.one('focus', function() { 26 | $form.addClass('loading'); 27 | 28 | $.getJSON(searchURL).then(function(searchData) { 29 | $.each(searchData, function (url, doc) { 30 | searchIndex.add({url: url, name: doc.name}); 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3 37 | }, 38 | { 39 | limit: 10, 40 | display: displayTemplate, 41 | templates: { suggestion: suggestionTemplate }, 42 | source: function(query, sync) { 43 | var results = searchIndex.search(query).map(function(result) { 44 | var doc = searchData[result.ref]; 45 | doc.url = result.ref; 46 | return doc; 47 | }); 48 | sync(results); 49 | } 50 | } 51 | ); 52 | $form.removeClass('loading'); 53 | $typeahead.trigger('focus'); 54 | }); 55 | }); 56 | 57 | var baseURL = searchURL.slice(0, -"search.json".length); 58 | 59 | $typeahead.on('typeahead:select', function(e, result) { 60 | window.location = baseURL + result.url; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Authentication.md: -------------------------------------------------------------------------------- 1 | # Authentication 2 | 3 | Authentication is handled silently for you as part of the SDK. The SDK will cache credentials to ensure that it is not making unnecessary requests. 4 | 5 | The iOS SDK only supports `Implicit` authentication currently. -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Communication.md: -------------------------------------------------------------------------------- 1 | # Communication 2 | 3 | - If you need help with the SDK or the platform, get in touch on the [forum](https://forum.moltin.com) 4 | - If you found a bug with the SDK, open an issue on GitHub 5 | - If you have a feature request for the SDK, open an issue. 6 | - If you want to contribute to the SDK, submit a pull request. -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Filtering.md: -------------------------------------------------------------------------------- 1 | # Filtering 2 | 3 | ## Operations 4 | - Filter 5 | - Sort 6 | - Offset / Limit 7 | - Include 8 | 9 | ## Filter 10 | ```swift 11 | moltin.product.filter(operator: .eq, key: "name", value: "ProductName").all { 12 | ... 13 | } 14 | ``` 15 | 16 | ## Sort 17 | ```swift 18 | moltin.product.sort("order").all { 19 | ... 20 | } 21 | ``` 22 | ```swift 23 | moltin.product.sort("-order").all { 24 | ... 25 | } 26 | ``` 27 | 28 | ## Offset / Limit 29 | 30 | ```swift 31 | moltin.product.limit(10).offset(20).all { 32 | ... 33 | } 34 | ``` 35 | 36 | ## Include 37 | 38 | ```swift 39 | moltin.product.include([.mainImage, .files]).all { 40 | ... 41 | } 42 | ``` 43 | 44 | ## Combining Operations 45 | 46 | ```swift 47 | moltin.product.sort("-name").include([.mainImage]).limit(20).all { 48 | ... 49 | } 50 | ``` -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Flows.md: -------------------------------------------------------------------------------- 1 | # Flows 2 | 3 | If you've implemented a custom field on a resource by using flows, you can cast this to a type of your choice by type-hinting your result, so long as this type conforms to `Codable`: 4 | 5 | ```swift 6 | moltin.product.all { (result: Result>) in 7 | switch result { 8 | case .success(let response): 9 | print(response.data) // [MyCustomProduct] 10 | case .failure(_): 11 | break 12 | } 13 | } 14 | ``` 15 | 16 | ```swift 17 | moltin.product.get(forID: "") { (result: Result) in 18 | switch result { 19 | case .success(let response): 20 | print(response) // MyCustomProduct 21 | case .failure(_): 22 | break 23 | } 24 | ``` 25 | 26 | We recommend ensuring that your types extend from our base types for safety, then you implement the `required init(from decoder: Decoder)`: 27 | 28 | ```swift 29 | class MyCustomProduct: moltin.Product { 30 | let author: Author 31 | 32 | enum ProductCodingKeys : String, CodingKey { 33 | case author 34 | } 35 | 36 | required init(from decoder: Decoder) throws { 37 | let container = try decoder.container(keyedBy: ProductCodingKeys.self) 38 | self.author = try container.decode(Author.self, forKey: .author) 39 | try super.init(from: decoder) 40 | } 41 | } 42 | ``` 43 | 44 | This will allow you to add additional types as you need, but ensures the base type, such as product, is still parsed correctly. -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Further Documentation.md: -------------------------------------------------------------------------------- 1 | # Further Documentation 2 | 3 | Find more general documentation on the [API docs](https://docs.moltin.com). -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Cocoapods 4 | 5 | Add the following to your `Podfile`: 6 | ``` 7 | pod 'Moltin', '~> 3.1.2' 8 | ``` 9 | 10 | Or, quickly try out our examples: 11 | ```bash 12 | pod try Moltin 13 | ``` 14 | ## Carthage 15 | 16 | Add the following to your `Cartfile`: 17 | ``` 18 | github "Moltin/ios-sdk" ~> 3.1.2 19 | ``` 20 | ## Swift Package Manager 21 | 22 | Add the following to your `dependencies` value in `Package.swift`: 23 | ```swift 24 | dependencies: [ 25 | .package(url: "https://github.com/moltin/ios-sdk.git", from: "3.1.2") 26 | ] 27 | ``` -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/License.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (c) 2017 moltin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | - iOS 10.0+ / tvOS 10.0+ / watchOS 3.0+ 4 | - Swift 4.0+ -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/tmp/docs/Usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | ## Making a request 4 | ```swift 5 | let moltin = Moltin(withClientID: "") 6 | 7 | moltin.product.all { result in 8 | switch result { 9 | case .success(let response): 10 | print(response) 11 | case .failure(let error): 12 | print(error) 13 | } 14 | } 15 | 16 | moltin.product.get("") { result in 17 | switch result { 18 | case .success(let response): 19 | print(response) 20 | case .failure(let error): 21 | print(error) 22 | } 23 | } 24 | 25 | moltin.product.tree { result in 26 | switch result { 27 | case .success(let response): 28 | print(response) 29 | case .failure(let error): 30 | print(error) 31 | } 32 | } 33 | ``` 34 | 35 | ## Checking out & Payment 36 | 37 | Paying for a cart is a two step process in Moltin. 38 | 39 | First, check out your cart, which will return you an order: 40 | 41 | ```swift 42 | self.moltin.cart.checkout( 43 | cart: ..., 44 | withCustomer: ..., 45 | withBillingAddress: ..., 46 | withShippingAddress: ...) { (result) in 47 | switch result { 48 | case .success(let order): 49 | ... 50 | default: break 51 | } 52 | } 53 | ``` 54 | 55 | Now that you have an order, you can pay for your order. Moltin providers several gateways for you to use: 56 | 57 | - Stripe 58 | - BrainTree 59 | - Adyen 60 | - Manual 61 | 62 | Once you've chosen your payment gateway, you can fulfil one of Moltin's `PaymentMethod`'s: 63 | 64 | ```swift 65 | let paymentMethod = StripeToken(withStripeToken: ...) 66 | ``` 67 | 68 | You can then use this payment method to pay for an order: 69 | 70 | ```swift 71 | self.moltin.cart.pay( 72 | forOrderID: order.id, 73 | withPaymentMethod: paymentMethod) { (result) in 74 | ... 75 | } 76 | ``` 77 | 78 | ## Config 79 | 80 | The basic way to set up the Moltin SDK is to create an instance of the `Moltin` class with your client ID and optionally the locale of the application. However, if you'd like to change additional details of the SDK, such as the URL of your `Moltin` instance, you can do so by passing in `MoltinConfig`. 81 | 82 | ```swift 83 | let moltin = Moltin(withClientID: ...) // Takes Locale.current 84 | ``` 85 | 86 | ```swift 87 | let moltin = Moltin(withClientID: ..., withLocale: ...) 88 | ``` 89 | 90 | ```swift 91 | let config = MoltinConfig( 92 | clientID: ..., 93 | scheme: ..., 94 | host: ..., 95 | version: ..., 96 | locale: ...) 97 | 98 | let moltin = Moltin(withConfiguration: config) 99 | ``` 100 | Or: 101 | ```swift 102 | let config = MoltinConfig.default( 103 | withClientID: ..., 104 | withLocale: ...) 105 | 106 | let moltin = Moltin(withConfiguration: config) 107 | ``` 108 | 109 | ## Available Resources 110 | - Brands 111 | - Carts 112 | - Categories 113 | - Collections 114 | - Currencies 115 | - Files 116 | - Flows 117 | - Fields 118 | - Entries 119 | - Products -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/Documents/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 5 | "line": 29, 6 | "symbol": "CartItemPrice", 7 | "symbol_kind": "source.lang.swift.decl.class", 8 | "warning": "undocumented" 9 | }, 10 | { 11 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 12 | "line": 194, 13 | "symbol": "CustomCartItem.init(withName:sku:quantity:description:price:withAttributes:)", 14 | "symbol_kind": "source.lang.swift.decl.function.method.instance", 15 | "warning": "undocumented" 16 | }, 17 | { 18 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 19 | "line": 228, 20 | "symbol": "TaxItem.type", 21 | "symbol_kind": "source.lang.swift.decl.var.instance", 22 | "warning": "undocumented" 23 | }, 24 | { 25 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 26 | "line": 229, 27 | "symbol": "TaxItem.id", 28 | "symbol_kind": "source.lang.swift.decl.var.instance", 29 | "warning": "undocumented" 30 | }, 31 | { 32 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 33 | "line": 230, 34 | "symbol": "TaxItem.jurisdiction", 35 | "symbol_kind": "source.lang.swift.decl.var.instance", 36 | "warning": "undocumented" 37 | }, 38 | { 39 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 40 | "line": 231, 41 | "symbol": "TaxItem.code", 42 | "symbol_kind": "source.lang.swift.decl.var.instance", 43 | "warning": "undocumented" 44 | }, 45 | { 46 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 47 | "line": 232, 48 | "symbol": "TaxItem.name", 49 | "symbol_kind": "source.lang.swift.decl.var.instance", 50 | "warning": "undocumented" 51 | }, 52 | { 53 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 54 | "line": 233, 55 | "symbol": "TaxItem.rate", 56 | "symbol_kind": "source.lang.swift.decl.var.instance", 57 | "warning": "undocumented" 58 | } 59 | ], 60 | "source_directory": "/Users/georgefitzgibbons/dev/ios-sdk" 61 | } -------------------------------------------------------------------------------- /docs/docsets/moltin.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/docsets/moltin.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/moltin.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/docsets/moltin.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/docs/img/spinner.gif -------------------------------------------------------------------------------- /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 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | 39 | // Dumb down quotes within code blocks that delimit strings instead of quotations 40 | // https://github.com/realm/jazzy/issues/714 41 | $("code q").replaceWith(function () { 42 | return ["\"", $(this).contents(), "\""]; 43 | }); 44 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var searchIndex = lunr(function() { 3 | this.ref('url'); 4 | this.field('name'); 5 | }); 6 | 7 | var $typeahead = $('[data-typeahead]'); 8 | var $form = $typeahead.parents('form'); 9 | var searchURL = $form.attr('action'); 10 | 11 | function displayTemplate(result) { 12 | return result.name; 13 | } 14 | 15 | function suggestionTemplate(result) { 16 | var t = '
'; 17 | t += '' + result.name + ''; 18 | if (result.parent_name) { 19 | t += '' + result.parent_name + ''; 20 | } 21 | t += '
'; 22 | return t; 23 | } 24 | 25 | $typeahead.one('focus', function() { 26 | $form.addClass('loading'); 27 | 28 | $.getJSON(searchURL).then(function(searchData) { 29 | $.each(searchData, function (url, doc) { 30 | searchIndex.add({url: url, name: doc.name}); 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3 37 | }, 38 | { 39 | limit: 10, 40 | display: displayTemplate, 41 | templates: { suggestion: suggestionTemplate }, 42 | source: function(query, sync) { 43 | var results = searchIndex.search(query).map(function(result) { 44 | var doc = searchData[result.ref]; 45 | doc.url = result.ref; 46 | return doc; 47 | }); 48 | sync(results); 49 | } 50 | } 51 | ); 52 | $form.removeClass('loading'); 53 | $typeahead.trigger('focus'); 54 | }); 55 | }); 56 | 57 | var baseURL = searchURL.slice(0, -"search.json".length); 58 | 59 | $typeahead.on('typeahead:select', function(e, result) { 60 | window.location = baseURL + result.url; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 5 | "line": 29, 6 | "symbol": "CartItemPrice", 7 | "symbol_kind": "source.lang.swift.decl.class", 8 | "warning": "undocumented" 9 | }, 10 | { 11 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 12 | "line": 194, 13 | "symbol": "CustomCartItem.init(withName:sku:quantity:description:price:withAttributes:)", 14 | "symbol_kind": "source.lang.swift.decl.function.method.instance", 15 | "warning": "undocumented" 16 | }, 17 | { 18 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 19 | "line": 228, 20 | "symbol": "TaxItem.type", 21 | "symbol_kind": "source.lang.swift.decl.var.instance", 22 | "warning": "undocumented" 23 | }, 24 | { 25 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 26 | "line": 229, 27 | "symbol": "TaxItem.id", 28 | "symbol_kind": "source.lang.swift.decl.var.instance", 29 | "warning": "undocumented" 30 | }, 31 | { 32 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 33 | "line": 230, 34 | "symbol": "TaxItem.jurisdiction", 35 | "symbol_kind": "source.lang.swift.decl.var.instance", 36 | "warning": "undocumented" 37 | }, 38 | { 39 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 40 | "line": 231, 41 | "symbol": "TaxItem.code", 42 | "symbol_kind": "source.lang.swift.decl.var.instance", 43 | "warning": "undocumented" 44 | }, 45 | { 46 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 47 | "line": 232, 48 | "symbol": "TaxItem.name", 49 | "symbol_kind": "source.lang.swift.decl.var.instance", 50 | "warning": "undocumented" 51 | }, 52 | { 53 | "file": "/Users/georgefitzgibbons/dev/ios-sdk/Sources/SDK/Models/Cart.swift", 54 | "line": 233, 55 | "symbol": "TaxItem.rate", 56 | "symbol_kind": "source.lang.swift.decl.var.instance", 57 | "warning": "undocumented" 58 | } 59 | ], 60 | "source_directory": "/Users/georgefitzgibbons/dev/ios-sdk" 61 | } -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple email address 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | 9 | # Uncomment the line if you want fastlane to automatically update itself 10 | # update_fastlane 11 | 12 | default_platform(:ios) 13 | 14 | platform :ios do 15 | desc "Run iOS Tests" 16 | lane :test do 17 | scan(scheme: "moltin\ iOS") 18 | sh("mv test_output/report.junit test_output/report.xml") 19 | end 20 | 21 | lane :test_tv do 22 | scan(scheme: "moltin\ tvOS") 23 | sh("mv test_output/report.junit test_output/report.xml") 24 | end 25 | end 26 | 27 | platform :mac do 28 | desc "Run macOS Tests" 29 | lane :test do 30 | scan(scheme: "moltin\ macOS") 31 | sh("mv test_output/report.junit test_output/report.xml") 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## iOS 19 | ### ios test 20 | ``` 21 | fastlane ios test 22 | ``` 23 | Run iOS Tests 24 | ### ios test_tv 25 | ``` 26 | fastlane ios test_tv 27 | ``` 28 | 29 | 30 | ---- 31 | 32 | ## Mac 33 | ### mac test 34 | ``` 35 | fastlane mac test 36 | ``` 37 | Run macOS Tests 38 | 39 | ---- 40 | 41 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 42 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 43 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 44 | -------------------------------------------------------------------------------- /moltin.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import UIKit 4 | import moltin 5 | import PlaygroundSupport 6 | 7 | PlaygroundPage.current.needsIndefiniteExecution = true 8 | 9 | let moltin = Moltin(withClientID: "j6hSilXRQfxKohTndUuVrErLcSJWP15P347L6Im0M4") 10 | 11 | moltin.product.all { (result: Result>) in 12 | switch result { 13 | case .success(let products): 14 | print(products) 15 | case .failure(let error): 16 | print(error) 17 | } 18 | } 19 | 20 | moltin.product.all { (result) in 21 | guard case .success(let products) = result else { 22 | // something went wrong 23 | if case .failure(let error) = result { print(error) } 24 | return 25 | } 26 | 27 | print(products) 28 | } 29 | -------------------------------------------------------------------------------- /moltin.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /moltin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/moltin.png -------------------------------------------------------------------------------- /moltin.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /moltin.xcodeproj/project.xcworkspace/xcuserdata/craigtweedy.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moltin/ios-sdk/a564483d6568c82b8c99629d7d17ba21a2d853b8/moltin.xcodeproj/project.xcworkspace/xcuserdata/craigtweedy.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /moltin.xcodeproj/xcshareddata/xcschemes/moltin iOS Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /moltin.xcodeproj/xcshareddata/xcschemes/moltin iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /moltin.xcodeproj/xcshareddata/xcschemes/moltin tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /moltin.xcodeproj/xcshareddata/xcschemes/moltin watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /moltin.xcodeproj/xcuserdata/craigtweedy.xcuserdatad/xcschemes/moltin iOS Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 16 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 40 | 41 | 42 | 43 | 49 | 50 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /moltin.xcodeproj/xcuserdata/craigtweedy.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | moltin WatchKit Example.xcscheme 8 | 9 | orderHint 10 | 5 11 | 12 | moltin WatchKit Example.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 5 16 | 17 | moltin iOS Example.xcscheme 18 | 19 | orderHint 20 | 4 21 | 22 | moltin iOS Example.xcscheme_^#shared#^_ 23 | 24 | orderHint 25 | 4 26 | 27 | moltin iOS Tests.xcscheme 28 | 29 | orderHint 30 | 3 31 | 32 | moltin iOS.xcscheme_^#shared#^_ 33 | 34 | orderHint 35 | 1 36 | 37 | moltin tvOS Example.xcscheme 38 | 39 | orderHint 40 | 6 41 | 42 | moltin tvOS Example.xcscheme_^#shared#^_ 43 | 44 | orderHint 45 | 6 46 | 47 | moltin tvOS Tests.xcscheme 48 | 49 | orderHint 50 | 5 51 | 52 | moltin tvOS.xcscheme_^#shared#^_ 53 | 54 | orderHint 55 | 2 56 | 57 | moltin watchOS.xcscheme_^#shared#^_ 58 | 59 | orderHint 60 | 0 61 | 62 | 63 | SuppressBuildableAutocreation 64 | 65 | C392EA11203DB7EB006CD1D0 66 | 67 | primary 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /moltin.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /moltin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scripts/doc-preprocessor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'json' 4 | 5 | def parseOptions(paired_args) 6 | options = {args: []} 7 | index = 0 8 | while(index < ARGV.length) 9 | arg = ARGV[index] 10 | 11 | paired_key = paired_args[arg] 12 | if paired_key 13 | if index >= ARGV.length - 1 14 | puts "Unable to find a value for the arg: '" + arg + "'" 15 | exit 1 16 | end 17 | options[paired_key] = ARGV[index+1] 18 | index += 1 19 | elsif /--.+/ =~ arg 20 | puts "unknown option: " + arg 21 | else 22 | options[:args].push arg 23 | end 24 | index += 1 25 | end 26 | options 27 | end 28 | 29 | def requireArgs(args, count) 30 | if args.length != count 31 | puts "usage: docgen [input] [output] [...options]" 32 | exit 1 33 | end 34 | end 35 | 36 | def splitSections(content) 37 | result = {} 38 | current = '' 39 | content.split("\n").each do |line| 40 | if /^#( | \w).*/ =~ line 41 | current = line.sub(/^#/, '').strip 42 | # current = line 43 | result[current] = result[current] || "" 44 | else 45 | result[current] += "\n" + line 46 | end 47 | end 48 | 49 | result.keys.each do |key| 50 | result[key] = result[key].strip 51 | end 52 | result 53 | end 54 | 55 | paired_args = { 56 | '--section' => :section, 57 | '--title' => :title, 58 | '--transforms' => :transforms 59 | } 60 | options = parseOptions paired_args 61 | 62 | requireArgs options[:args], 2 63 | file_in = options[:args][0] 64 | file_out = options[:args][1] 65 | 66 | input = IO.read(file_in) 67 | 68 | result = '' 69 | 70 | # ----- 71 | 72 | if options[:section] 73 | sections = splitSections input 74 | section_name = options[:section] 75 | if sections[section_name] 76 | result = sections[section_name] 77 | else 78 | puts "Unable to find section: '" + section_name + "'" 79 | exit 1 80 | end 81 | else 82 | result = input 83 | end 84 | 85 | 86 | if options[:title] 87 | result = "# #{options[:title]}\n\n" + result 88 | end 89 | 90 | # ----- 91 | 92 | version = `cat Moltin.podspec| grep "version " | cut -d '"' -f 2`.strip 93 | if version 94 | result = result.sub '{{version}}', version 95 | end 96 | 97 | # ----- 98 | 99 | if result == '' 100 | puts "Not writing output as result is empty" 101 | exit 1 102 | end 103 | 104 | IO.write(file_out, result) 105 | 106 | puts "[Processed]: #{file_out}" 107 | -------------------------------------------------------------------------------- /scripts/generator-docs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ----- Check for dependencies 4 | 5 | JAZZY=$(which jazzy) 6 | if [ $? != 0 ]; then 7 | echo -e "Jazzy is required to generate documentation. Install it with:\n" 8 | echo -e " gem install jazzy\n" 9 | exit 10 | fi 11 | echo "Using jazzy: $JAZZY" 12 | 13 | 14 | # ----- Commandline options 15 | 16 | if [ "$OUTPUT_PATH" == "" ]; then 17 | OUTPUT_PATH=$([ "$2" == "" ] && echo "docs/output" || echo "$2") 18 | fi 19 | 20 | 21 | # ----- Configuration 22 | 23 | ORGANISATION=Moltin 24 | NAME=ios-sdk 25 | TMP=docs/tmp 26 | GITHUB=https://github.com/$ORGANISATION/$NAME 27 | 28 | PREPROC=scripts/doc-preprocessor 29 | 30 | # ----- Setup and generate docs 31 | 32 | # Clean $TMP folder 33 | if [ -d "$TMP" ]; then rm -rf "$TMP"; fi 34 | mkdir -p $TMP/{compile,docs,api} 35 | 36 | # cp docs/*.md $TMP/api/ 37 | 38 | # Split the README into sections 39 | $PREPROC README.md "$TMP/docs/Requirements.md" --section "Requirements" --title "Requirements" 40 | $PREPROC README.md "$TMP/docs/Installation.md" --section "Installation" --title "Installation" 41 | $PREPROC README.md "$TMP/docs/Usage.md" --section "Usage" --title "Usage" 42 | $PREPROC README.md "$TMP/docs/Authentication.md" --section "Authentication" --title "Authentication" 43 | $PREPROC README.md "$TMP/docs/Filtering.md" --section "Filtering" --title "Filtering" 44 | $PREPROC README.md "$TMP/docs/Flows.md" --section "Flows" --title "Flows" 45 | $PREPROC README.md "$TMP/docs/Further Documentation.md" --section "Further Documentation" --title "Further Documentation" 46 | $PREPROC README.md "$TMP/docs/Communication.md" --section "Communication" --title "Communication" 47 | 48 | # Copy remaining root docs 49 | # $PREPROC CONTRIBUTING.md "$TMP/docs/Contributing.md" 50 | # $PREPROC CHANGELOG.md "$TMP/docs/Changelog.md" --title "Changelog" 51 | $PREPROC LICENSE "$TMP/docs/License.md" --title "License" 52 | 53 | # Copy over the Getting started guide 54 | # $PREPROC "Docs/Getting Started Guide.md" "$TMP/docs/Getting Started Guide.md" 55 | 56 | # Create the documentation landing page by combining: 57 | # 58 | # - Docs/templates/heading.md 59 | # - README.md#introduction 60 | # - Docs/templates/toc.md 61 | # 62 | # cat docs/templates/heading.md $TMP/compile/intro.md docs/templates/toc.md > $TMP/compile/readme-raw.md 63 | # $PREPROC "$TMP/compile/readme-raw.md" "$TMP/compile/README.md" 64 | # cp $TMP/compile/README.md $TMP/api/Documentation.md 65 | 66 | # Compile our Docs/tmp + generate API docs using jazzy 67 | jazzy 68 | 69 | rm -rf docs/tmp --------------------------------------------------------------------------------