{"_id":"56a0bda03697d80d002ac5f7","user":"54ad5f42921992210075173e","parentDoc":null,"project":"54774d9af3736008009e9e0e","__v":18,"category":{"_id":"56a0bd9f3697d80d002ac5e8","pages":["56a0bda03697d80d002ac5f4","56a0bda03697d80d002ac5f5","56a0bda03697d80d002ac5f6","56a0bda03697d80d002ac5f7","56a0bda03697d80d002ac5f8","56a0bda03697d80d002ac5f9","56a0bda03697d80d002ac5fa","56a0bda03697d80d002ac5fb","56a0bda03697d80d002ac5fc","56a0bda03697d80d002ac5fd","56a0bda03697d80d002ac5fe"],"__v":1,"project":"54774d9af3736008009e9e0e","version":"56a0bd9e3697d80d002ac5e7","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2014-11-27T16:46:04.014Z","from_sync":false,"order":8,"slug":"libraries-quick-starts","title":"Libraries Quick Starts"},"version":{"_id":"56a0bd9e3697d80d002ac5e7","project":"54774d9af3736008009e9e0e","__v":20,"createdAt":"2016-01-21T11:14:38.131Z","releaseDate":"2016-01-21T11:14:38.131Z","categories":["56a0bd9f3697d80d002ac5e8","56a0bd9f3697d80d002ac5e9","56a0bd9f3697d80d002ac5ea","56a0bd9f3697d80d002ac5eb","56b098a43a5b810d00745e3f","56b098da8f7a4f0d0029dd10","56d5a8ecf612b80b00fb69cd","570989853ab43c0e0072b2d6","571006aa10300c0e007f6133","5735a52431a73b1700887ca0","5744b0529e045c0e00b7a7a2","5744b1d0b56d431700d8a4fb","5744b1fb55d65a0e00b436e1","5744b218f9c7411700ce560e","5744b220f9c7411700ce560f","5744b2259e045c0e00b7a7a7","5744b22cf8b79f0e00de6a2d","5744b23155d65a0e00b436e4","5744b242c758290e00fbc235","5744b2569e045c0e00b7a7a8","5756a3f2bb92962900dafe3d","57ee23e81e42900e0014a42f","581c822c98676e0f00d240ef"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"Sockets","version_clean":"1.1.0","version":"1.1"},"updates":["57268d8055e8ec0e004026d3","572cf2fc30d8263600d95204","576ef4fa9c84a31900958af6","57718d775fc6cc190052517e"],"next":{"pages":[],"description":""},"createdAt":"2015-02-18T00:55:28.752Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":3,"body":"## Chapter Contents:\n1. [Overview](#overview)\n2. [Installing the Syncano iOS Library](#installing-the-syncano-ios-library)\n3. [Using the Syncano Library](#using-the-syncano-library)\n4. [Support](#section-support)\n5. [License](#section-license)\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Sockets release\",\n  \"body\": \"We recently released a new version of our API that uses Scripts instead of CodeBoxes and Script Endpoints instead of Webhooks.\\nFor us, it's not just about the name change - you can [read about it](https://www.syncano.io/blog/what-are-we-cooking-up-with-sockets/) on our blog.\\nFor now, you can keep using old methods, and when we release new version of this library, we will update our docs here.\"\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Overview\"\n}\n[/block]\nThis guide will walk you through the installation steps of the Syncano Library for iOS as well as give you a couple of usage examples.\n\nIf you don't have a Syncano account yet, you can read about how to create one [here](doc:getting-started-with-syncano).\n\nFor the full API listing, view the [syncano-ios reference](http://cocoadocs.org/docsets/syncano-ios/4.0.8/).\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Installing the Syncano iOS Library\"\n}\n[/block]\n1. [Source](#section-source)\n2. [CocoaPods](#section-cocoapods)\n3. [XCode](#section-xcode)\n4. [Create New XCode project](#section-create-new-xcode-project)\n5. [Add the Syncano Library to the project](#section-add-the-syncano-library-to-the-project)\n\nSource\n======\n\nWe recommend installing the syncano-ios library through CocoaPods, but you can always grab the source from [our GitHub](https://github.com/syncano/syncano-ios).\n\nCocoaPods\n=========\n\nTo install our Syncano iOS Library, you need to use [CocoaPods](http://cocoapods.org/) . If you don’t already have it, open the Terminal app and paste the following line:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sudo gem install cocoapods\",\n      \"language\": \"shell\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Installing CocoaPods\",\n  \"body\": \"If you didn't have CocoaPods installed before, please make sure to close and open a new Terminal window before proceeding to the next steps - otherwise `pod` command might not be recognized when you will try to use it.\"\n}\n[/block]\nXCode\n=====\n\nWe assume you already have an XCode IDE. If not, you can find it and install using the MacOS [AppStore](https://itunes.apple.com/us/app/xcode/id497799835?mt=12)\n\nCreate New XCode project\n====================\n\nLaunch XCode and do the following:\n\n1. Using the XCode menu, choose **File -> New -> Project**\n2. Choose the template that best suits your project. For now, to keep it simple, we’ll use **Single View Application**\n3. Name the product as you want, or just type **SyncanoProject**. In the prefix field enter SYN (you can keep devices in **Universal** state, or change it to either **iPhone** or **iPad**)\n4. Choose where you want to keep your project on the disk and hit the **Create** button\n5. Close the XCode project for now\n\nAdd the Syncano Library to the project\n==========================\n\nOpen the Terminal app and `cd` into the folder where you chose to save your project. If you don’t know how to do this, type\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"cd\",\n      \"language\": \"shell\",\n      \"name\": \"Shell\"\n    }\n  ]\n}\n[/block]\nand drag & drop the folder that contains your XCode project in Finder into the Terminal app. If you followed previous steps, you should have a file named **SyncanoProject.xcodeproj** inside a folder **SyncanoProject**. That’s the folder you need to drag from Finder into Terminal. When you do this, go to the Terminal app and press **ENTER** to confirm.\n\nIf you don’t have CocoaPods yet, check how to install it [here](#section-cocoapods). Once you have it, in Terminal (in folder where **.xcodeproj** file is) type:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"pod init\",\n      \"language\": \"shell\",\n      \"name\": \"Shell\"\n    }\n  ]\n}\n[/block]\nNow, open the file named **Podfile** in your desired text editor and under app target, before the end, add this line:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"pod 'syncano-ios'\",\n      \"language\": \"shell\",\n      \"name\": \"Shell\"\n    }\n  ]\n}\n[/block]\nIf you followed the suggested naming, your **Podfile** should now look like below.\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Using Syncano in a Swift project\",\n  \"body\": \"If you use Syncano in Swift with CocoaPods - please make sure to uncomment in the `Podfile` below line saying `use_frameworks!`. \\n\\nIf for some reason you cannot use pods as frameworks - read more about [how to use a bridging header](#section-swift-bridging-header).\"\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"# Uncomment this line if you're using Swift\\n# use_frameworks!\\n\\ntarget \\\"SyncanoProject\\\" do\\npod 'syncano-ios'\\nend\\n\\ntarget \\\"SyncanoProjectTests\\\" do\\n\\nend\",\n      \"language\": \"ruby\",\n      \"name\": \"Obj-C project\"\n    },\n    {\n      \"code\": \"# Uncomment this line if you're using Swift\\nuse_frameworks!\\n\\ntarget \\\"SyncanoProject\\\" do\\npod 'syncano-ios'\\nend\\n\\ntarget \\\"SyncanoProjectTests\\\" do\\n\\nend\",\n      \"language\": \"ruby\",\n      \"name\": \"Swift project\"\n    }\n  ]\n}\n[/block]\nNow you can save the **Podfile**. Go back to Terminal and install the Syncano library by typing:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"pod install\",\n      \"language\": \"shell\",\n      \"name\": \"Shell\"\n    }\n  ]\n}\n[/block]\nWhen the process is finished, you should see in the project’s folder a new **.xworkspace** (presumably **SyncanoProject.xcworkspace**) and from now on, you should work using this file, instead of the **.xcodeproj** one. You can open the workspace by double clicking it in Finder or just stay in Terminal and typing:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"open SyncanoProject.xcworkspace\",\n      \"language\": \"shell\",\n      \"name\": \"Shell\"\n    }\n  ]\n}\n[/block]\nThe library is already downloaded and added to your project. Every time you want to use it, just import Syncano:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import <Syncano.h>\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"import syncano_ios\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Using the Syncano Library\"\n}\n[/block]\n1. [Importing Syncano Headers](#section-importing-syncano-headers)\n2. [Connecting to Syncano](#section-connecting-to-syncano)\n3. [Adding your Data Class](#section-adding-your-class)\n   1. [Using references type field](#section-using-reference-type-field)\n4. [Download the most recent Objects](#section-download-the-most-recent-objects)\n5. [Create New Object](#section-create-new-object)\n6. [Modify Object](#section-modify-object)\n7. [Download objects matching specified criteria](#section-download-objects-matching-specified-criteria)\n   1. [Using Predicates](#section-using-predicates)\n   2. [Page Size and Order By](#section-page-size-and-order-by)\n   3. [Using Pages](#section-using-pages)\n   4. [Field filtering](#section-field-filtering) \n8. [Data Endpoints](#section-data-endpoints)\n9. [Delete Objects](#section-delete-objects)\n10. [Download files](#section-download-files)\n11. [Running Scripts and Script Endpoints](#section-running-scripts)\n   1. [Scripts](#section-scripts)\n   2. [Script Endpoints](#section-script-endpoints) \n11. [Synchronization and Real-Time Channels](#section-synchronization-and-real-time-channels)\n12. [Handling users](#section-handling-users)\n   1. [Register new user](#section-register-new-user)\n   2. [Log in user with password](#section-log-in-user-with-password)\n   3. [Log in user with social backend](#section-log-in-user-with-social-backend)\n   3. [Get user profile](#section-get-user-profile) \n   4. [Subclassing User and User Profile](#section-subclassing-user-and-user-profile)\n13. [Support](#section-support)\n14. [License](#section-license)\n\nImporting Syncano Headers\n=====================\n\n+ Click a chosen file, we’ll use **SYNViewController.m** for now\n+ Import Syncano libraries on top of your file, by adding following lines:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import <Syncano.h>\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"import syncano_ios\\n// or, if you used a bridging header in Swift, \\n// there's no need to import anything\\n// you already imported Syncano header in the bridging file\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nConnecting to Syncano\n=====================\n\n+ Add a property, that will store the main Syncano object.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \":::at:::interface SYNViewController ()\\[email protected] (strong) Syncano *syncano;\\[email protected]\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"//In Swift we can both declare and initialize our constant Syncano object, there's no need to do it in two places like in Obj-C\\n\\nlet syncano = Syncano.sharedInstanceWithApiKey(\\\"API_KEY\\\", instanceName: \\\"INSTANCE_NAME\\\")\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n+ In the **viewDidLoad** method, initialize a Syncano object:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)viewDidLoad {\\n  [super viewDidLoad];\\n  // Do any additional setup after loading the view, typically from a nib.\\n  self.syncano = [Syncano sharedInstanceWithApiKey:@\\\"API_KEY\\\" instanceName:@\\\"INSTANCE_NAME\\\"];\\n  \\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"//No need to add anything in Swift, Syncano object is already initialized\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nAdding your Data Class\n================\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Using data classes in Syncano\",\n  \"body\": \"Before you will use the Data Class in your app, remember that the Data Class should already exist in Syncano. You can add your data classes quickly using our [Dashboard](https://dashboard.syncano.io)!\\n\\nYou can read more about data classes [here](doc:classes).\"\n}\n[/block]\nNow we will add the Data Class you had implemented on Syncano. We will be using a simple Book data class which only has two attributes: \"title\" and \"subtitle\", both being of type \"string\".\nThe Schema for this Data Class would look like this:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[\\n  {\\\"type\\\": \\\"string\\\",\\\"name\\\": \\\"title\\\"},\\n  {\\\"type\\\": \\\"string\\\",\\\"name\\\": \\\"subtitle\\\"}\\n]\",\n      \"language\": \"json\"\n    }\n  ]\n}\n[/block]\nWe will add this data class in the app as well. Either in a separate file, or in the same view controller. For simplicity, we will assume you're adding this data class to the `SYNViewController.m` file (or in `ViewController.swift`), in this case, add the following code right under `#import <Syncano.h>` (or for Swift, right above `class ViewController: UINavigationController` line)\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import <Syncano.h>\\n\\[email protected] Book : SCDataObject\\[email protected] (nonatomic,copy) NSString *title;\\[email protected] (nonatomic,copy) NSString *subtitle;\\[email protected]\",\n      \"language\": \"objectivec\",\n      \"name\": \"\"\n    },\n    {\n      \"code\": \"class Book : SCDataObject {\\n  var title = \\\"\\\"\\n  var subtitle = \\\"\\\"\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nAdd the data class implementation below (or in the Book.m file if you are choosing to use separate files for this data class). For Swift we don't need a separate implementation, as declaration and implementation happens in one place.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"@implementation Book\\[email protected]\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"//No need to add anything more, we already implemented this function.\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Registering a data class\",\n  \"body\": \"Before you continue, make sure to register all your Data Classes. We recommend adding this code for all your data classes in your app delegate.\"\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\\n  // Override point for customization after application launch.\\n  [Book registerClass];\\n  [Author registerClass];\\n  //register this way all data classes, including subclasses of SCUser and SCUserProfile\\n  [MyUserProfile registerClass];\\n  [MyUser registerClassWithProfileClass:[MyUserProfile class]];\\n  return YES;\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {\\n\\t// Override point for customization after application launch.\\n\\tBook.registerClass()\\n\\tAuthor.registerClass()\\n  //register this way all classes, including subclasses of SCUser and SCUserProfile\\n  MyUserProfile.registerClass()\\n  MyUser.registerClassWithProfileClass(MyUserProfile.self)\\n\\treturn true\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n### Using `Reference` type field\n\nIf you'd like to use `Reference` type field in your data class -- it's possible to do that in iOS. \n\nFor example, if you defined `Author` data class in the same way as `Book` above, and now you wanted to have reference to author from book (and you defined its data class schema e.g. through our [dashboard](doc:classes))  - you just need to add the field to your data class.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import <Syncano.h>\\n\\[email protected] Book : SCDataObject\\[email protected] (nonatomic,copy) NSString *title;\\[email protected] (nonatomic,copy) NSString *subtitle;\\[email protected] (nonatoic,strong) Author *author;\\[email protected]\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"class Book : SCDataObject {\\n  var title = \\\"\\\"\\n  var subtitle = \\\"\\\"\\n  var author : Author?\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nBy defining Data Class as above, whenever you save your `book` object - it will save reference to `author` automatically.\n\nIf `author` object was just created in your app, and doesn't exist on Syncano yet - library will save it on Syncano as well. If `author` object already exists on Syncano - library won't save it, but will update reference to it in `book` object. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Case nr. 1\\n// Book already exists, but we're adding to it a new Author\\n// book will be saved, author will be saved on Syncano as well\\n// and book will hold a reference to the new author object\\nBook *book = [self getExistingBook];\\nAuthor *author = [[Author alloc] init];\\nauthor.first_name = @\\\"Tom\\\";\\nauthor.last_name = @\\\"Tomson\\\";\\nbook.author = author;\\n[book saveWithCompletionBlock: ^(NSError *error){\\n}];\\n\\n// Case nr. 2\\n// Book already exists and Author already exists on Syncano\\n// book will saved and reference to author wll be saved\\n// author object will not even be sent to Syncano\\nlet book : Book = [self getExistingBook];\\nlet author = [self getExistingAuthor];\\nbook.author = author;\\n[book saveWithCompletionBlock: ^(NSError *error){\\n}];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Case nr. 1\\n// Book already exists, but we're adding to it a new Author\\n// book will be saved, author will be saved on Syncano as well\\n// and book will hold a reference to the new author object\\nlet book : Book = getExistingBook()\\nlet author = Author()\\nauthor.first_name = \\\"Tom\\\"\\nauthor.last_name = \\\"Tomson\\\"\\nbook.author = author\\nbook.saveWithCompletionBlock() { error in }\\n\\n// Case nr. 2\\n// Book already exists and Author already exists on Syncano\\n// book will saved and reference to author wll be saved\\n// author object will not even be sent to Syncano\\nlet book : Book = getExistingBook()\\nlet author = getExistingAuthor()\\nbook.author = author\\nbook.saveWithCompletionBlock() { error in }\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nDownload the most recent Objects\n==============================\n\nAdd the following function to download the most recent Books, somewhere in the view controller data class implementation\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadBooks {\\n    [[Book please] giveMeDataObjectsWithCompletion:^(NSArray *books, NSError *error) {\\n        //handle error\\n        //do something with downloaded objects\\n    }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadBooks() {\\n  Book.please().giveMeDataObjectsWithCompletion { books, error in\\n    //handle error\\n    //do something with downloaded objects\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nMake sure you call that function from inside of the `viewDidLoad` method, so the code runs when you launch your app.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)viewDidLoad {\\n    [super viewDidLoad];\\n  \\n    self.syncano = [Syncano sharedInstanceWithApiKey:@\\\"YOUR_KEY\\\" instanceName:@\\\"YOUR_INSTANCE\\\"];\\n    [self downloadBooks];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"override func viewDidLoad() {\\n  super.viewDidLoad()\\n  self.downloadBooks()\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nThat's it, you just download a list of your books stored on Syncano!\n\nCreate New Object\n===============\n\nOften, you will not be only downloading data created by others, but you will also want to create new objects yourself.\nTo do it, you need to create a new object of the data class you'd like to work with, set chosen attributes, and save the object to Syncano.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"//define a data class like shown earlier, in \\\"Adding your Data Class\\\"\\n\\n- (void)addNewBook {\\n  Book *book = [Book new];\\n  book.title = @\\\"How to be a Pirate\\\";\\n  book.subtitle = @\\\"10 tips that will change your life.\\\";\\n  [book saveWithCompletionBlock:^(NSError *error) {\\n    //process saved book\\n    //handle error\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"//define a class like shown earlier, in \\\"Adding your class\\\"\\n\\nfunc addNewBook() {\\n  let book = Book()\\n  book.title = \\\"How to be a Pirate\\\"\\n  book.subtitle = \\\"10 tips that will change your life.\\\"\\n  book.saveWithCompletionBlock { error in\\n    //handle error\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nModify Object\n================\n\nYou can modify Syncano objects exactly in the same way as you modify every object in iOS. The only difference is that you have to save the changes at the end, otherwise your modifications will not be saved in Syncano.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)updateBook:(Book *)book {\\n  book.title = @\\\"Pirate ships\\\";\\n  book.subtitle = @\\\"Which was the biggest?\\\";\\n  [book saveWithCompletionBlock:^(NSError *error) {\\n    //error handling\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func updateBook(book: Book) {\\n  book.title = \\\"Pirate ships\\\"\\n  book.subtitle = \\\"Which was the biggest?\\\"\\n  book.saveWithCompletionBlock { error in\\n    //error handling\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nAfter adding this method, you will be able to pass it any book object, and it will be updated with a new `title` and `subtitle`.\n\nDownload objects matching specified criteria\n==================================\n\nThere are couple of ways to filter results while getting a Data Object list.\nMore detailed information about filtering can be found [here](http://docs.syncano.com/v0.1.1/docs/filtering-data-objects).\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Indexes\",\n  \"body\": \"To be able to filter and sort based on chosen fields, please remember you have to first set an index on those fields. You can read more about it [here](doc:classes).\"\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Permissions\",\n  \"body\": \"If you are trying to get objects from other users, you must add a read permission under other_permissions. In swift this looks like \\\"book.other_permissions = .Read\\\". If this is not done, when you try and get objects you will only get objects the current user has created.\"\n}\n[/block]\n### Using Predicates\n\nUsually we don't need to download all objects - we need only specific ones, matching our needs. E.g. books written only by one chosen author, or books with more than 300 pages - this can all be done by Syncano as well.\n\nFor the example below, we'll try to find a book titled `Pirate ship`. In the previous code examples above, that's how we titled one of our existing books.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadPirateBooks {\\n    SCPredicate *pirateBookPredicate = [SCPredicate whereKey:@\\\"title\\\" isEqualToString:@\\\"Pirate ships\\\"];\\n    [[Book please] giveMeDataObjectsWithPredicate:pirateBookPredicate parameters:nil completion:^(NSArray *books, NSError *error) {\\n        //error handling\\n    }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadPirateBooks() {\\n  let pirateBookPredicate = SCPredicate.whereKey(\\\"title\\\", isEqualToString: \\\"Pirate ships\\\")\\n  Book.please().giveMeDataObjectsWithPredicate(pirateBookPredicate, parameters: nil) { books, error in\\n    //error handling\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n**Using multiple predicates**\n\nIf you want to filter by more than one attribute at the same time, or to apply multiple criteria to one attribute - you can do that using `SCCompoundPredicate`\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadPirateBooks {\\n    SCPredicate *pirateBookPredicate = [SCPredicate whereKey:@\\\"title\\\" isEqualToString:@\\\"Pirate ships\\\"];\\n  SCPredicate *numberOfPagesPredicate = [SCPredicate whereKey:@\\\"numberOfPages\\\" isGreaterThanOrEqualToNumber:@500];\\n  SCCompoundPredicate *compoundPredicate = [SCCompoundPredicate compoundPredicateWithPredicates:@[pirateBookPredicate,numberOfPagesPredicate]];\\n    [[Book please] giveMeDataObjectsWithPredicate:compoundPredicate parameters:nil completion:^(NSArray *books, NSError *error) {\\n        //error handling\\n    }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadLongPirateBooks() {\\n  let pirateBookPredicate = SCPredicate.whereKey(\\\"title\\\", isEqualToString: \\\"Pirate ships\\\")\\n  let numberOfPagesPredicate = SCPredicate.whereKey(\\\"numberOfPages\\\", isGreaterThanOrEqualToNumber:500)\\n  let compoundPredicate = SCCompoundPredicate(predicates: [pirateBookPredicate,numberOfPagesPredicate])\\n  Book.please().giveMeDataObjectsWithPredicate(compoundPredicate, parameters: nil) { books, error in\\n    //error handling\\n  }\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Using Multiple Predicates\",\n  \"body\": \"When joining predicates using `SCCompoundPredicate`, please remember they are joined using `AND` - returned objects will have to match all passed criteria. \\nAt the moment there is no way to pass predicates joined by an `OR` statement.\"\n}\n[/block]\n**Using referenced data class queries**\nIf you want to find books written by authors from given country, you will find referenced data class queries useful. Keep in mind you must have the filter checkbox marked in the data class field you want to use or you will get failed requests:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadBooksFromNorway {\\n    SCPredicate *predicate = [SCPredicate whereKey:@\\\"author\\\" satisfiesPredicate:\\n                     [SCPredicate whereKey:@\\\"country\\\" isEqualToString:@\\\"Norway\\\"]];\\n    [[Book please] giveMeDataObjectsWithPredicate:predicate parameters:nil completion:^(NSArray *books, NSError *error) {\\n        //error handling\\n    }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadBooksFromNorway() {\\n    let innerPredicate = SCPredicate.whereKey(\\\"country\\\", isEqualToString: \\\"Norway\\\")\\n    let predicate = SCPredicate.whereKey(\\\"author\\\", satisfiesPredicate: innerPredicate)\\n    Book.please().giveMeDataObjectsWithPredicate(predicate, parameters: nil) { books, error in\\n        //error handling\\n    }\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n### Page Size and Order By\n\nWhen you don't need to display a lot of data on the screen at once, you may want to limit the number of objects downloaded at once. You can do that by setting the `Page Size` parameter.\n\nYou can also modify the way the data returned from Syncano is sorted - if you want to sort on a different field from `id`, e.g. `title` of the book, you can also configure it this way.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadBooksOrderedByTitle:(NSInteger)count {\\n  [Book.please giveMeDataObjectsWithParameters:@{\\n    SCPleaseParameterPageSize : @(count),\\n    SCPleaseParameterOrderBy : @\\\"title\\\" \\n  } completion:^(NSArray *books, NSError *error) {\\n     //handle error\\n   }];\\n}\\n\\n//if you wanted to download only 5 books, you could do e.g.\\n//[self downloadBooksOrderedByTitle:5];\",\n      \"language\": \"objectivec\",\n      \"name\": \"\"\n    },\n    {\n      \"code\": \"func downloadBooksOrderedByTitle(count: Int) {\\n  Book.please().giveMeDataObjectsWithParameters(\\n    [SCPleaseParameterPageSize:count,\\n     SCPleaseParameterOrderBy:\\\"title\\\"]) { books, error in\\n    //handle error\\n  }\\n}\\n\\n//if you wanted to download only 5 books, you could do e.g.\\n//self.downloadBooksOrderedByTitle(5)\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n### Using Pages\n\nBy default, when you use function  `giveMeDataObjectsWithCompletion`, or `giveMeDataObjectsWithParameters` etc - they return up to 100 objects in request (less if you have less than 100 objects in your data class, or if you set page size to be less than 100).\n\nTo get more objects then only the ones from the first page, you need to other methods on our `SCPlease` object.\n\nYou can ask for next/previous page of results (e.g. in response to user clicking \"load more\" button).\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"SCPlease *please = [Book please];\\n// at some point in your coode you already downloaded some data, using predicate, parameters or not\\n// e.g. by using 'giveMeDataObjectsWithCompletion' method\\n// you should save SCPlease object as a variable and pass it around to get next or previous page\\n[please giveMeNextPageOfDataObjectsWithCompletion:^(NSArray *objects, NSError *error) {        \\n\\t//work with your objects\\n}];\\n\\n//or\\n\\n[please giveMePreviousPageOfDataObjectsWithCompletion:^(NSArray *objects, NSError *error) {        \\n\\t//work with your objects\\n}];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"let please = Book.please()\\n// at some point in your coode you already downloaded some data, using predicate, parameters or not\\n// e.g. by using 'giveMeDataObjectsWithCompletion' method\\n// you should save SCPlease object as a variable and pass it around to get next or previous page\\nplease.giveMeNextPageOfDataObjectsWithCompletion { books, error in\\n  //work with your objects\\n}\\n\\n//or\\n\\nplease.giveMePreviousPageOfDataObjectsWithCompletion { books, error in\\n  //work with your objects\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Get next or previous page\",\n  \"body\": \"Remember, that when using `[Book please]` in Obj-C or `Book.please()` in Swift - it always returns a new SCPlease object.\\nUsing `giveMeNextPageOfDataObjectsWithCompletion` or `giveMePreviousPageOfDataObjectsWithCompletion` on a new SCPlease object will always return 0 objects, as `next` or `previous` must be used in context of a request done earlier.\\n\\nTo use these methods properly:\\n1) Make sure to store SCPlease \\n2) Make a normal request, using e.g. `giveMeDataObjectsWithCompletion` method on your stored SCPlease object\\n3) only after 2) was done, ask for next/previous pages.\\n\\nIf you ever need to start from page 0 - just get the new SCPlease object using `please` method on your data class.\"\n}\n[/block]\nIf you know you will need all the objects from your data class, or more than one page, but you don't know how many pages - you can use convenient method `enumaratePagesWithPredicate: parameters:`.\n\nIt takes a predicate and/or parameters (both can be nil) and a block (closure). \n\nBlock gives you an error (if one was set), array with objects from Syncano and a pointer to BOOL value - you should set it to true if you'd like to stop enumerating.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[[Book please] enumaratePagesWithPredicate:nil parameters:@{SCPleaseParameterPageSize : @(5)} withBlock:^(BOOL *stop, NSArray *objects, NSError *error) {\\n  if (error) {\\n    //handle error\\n  }\\n  for (Book *book in objects) {\\n    //do something with my books\\n  }\\n  // if you got all the results you wanted and don't need to get more pages - set *stop to true\\n  if (/*you have enough results == */ YES) {\\n    *stop = YES;\\n  } else {\\n    //it will keep enumarating and block will be called for next page of results\\n  }\\n}];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"Book.please().enumaratePagesWithPredicate(nil, parameters: [SCPleaseParameterPageSize : 5]) { stop, books, error in\\n  guard error == nil, let books = books as? [Book] else {\\n    //handle error\\n    return\\n  }\\n  for book in books {\\n  \\t//do something with my books\\n  \\tprint(\\\"Title: \\\\(book.title)\\\")\\n  }\\n  // if you got all the results you wanted and don't need to get more pages - set Bool 'stop' points to, to true\\n  if (/*you have enough results == */ true) {\\n  \\tstop.memory = true;\\n  } else {\\n  \\t//it will keep enumarating and block will be called for next page of results\\n  }\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"If you need to enumerate all pages, starting from page 0, it's safe to use `[Book please]` or `Book.please()` directly (as in the example above).\",\n  \"title\": \"Enumerate pages\"\n}\n[/block]\n### Field filtering\n\nSometimes you may not need to display all the data inside your objects - e.g. your Book data class may contain info about author, number of pages, title, subtitle, publisher, year it was published, category, summary and much more. But your view shows only book title and you'd like to download only this one field - title - allowing for smaller data transfer thus faster download time.\n\nYou can do that as well, using the following code:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadBookTitles {\\n  [Book.please giveMeDataObjectsWithParameters:@{ \\n    SCPleaseParameterFields : @[@\\\"title\\\"] \\n  } completion:^(NSArray *books, NSError *error) {\\n        //handle error\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadBookTitles() {\\n  Book.please().giveMeDataObjectsWithParameters([SCPleaseParameterFields:[\\\"title\\\"]]) \\n  { books, error in\\n    //handle error\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nYou can find list of all possible parameters in `SCPlease.h` file.\n\nData Endpoints\n=====================\nData endpoints are very handy and iOS library support all their features - you can filter on them and have the relations between objects expanded. \n\nLet's start with modifying our base data classes:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"@interface Author : SCDataObject\\[email protected] (nonatomic,copy) NSString *name;\\[email protected] (nonatomic,copy) NSString *country;\\[email protected]\\n\\[email protected] Book : SCDataObject\\[email protected] (nonatomic,copy) NSString *title;\\[email protected] (nonatomic,copy) NSString *subtitle;\\[email protected] (nonatomic,copy) SCFile *cover;\\[email protected] (nonatomic,strong) Author *author;\\[email protected]\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"class Author : SCDataObject {\\n    var name = \\\"\\\"\\n    var country = \\\"\\\"\\n}\\n\\nclass Book : SCDataObject {\\n    var title = \\\"\\\"\\n    var subtitle = \\\"\\\"\\n    var cover: SCFile?\\n    var author: Author?\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nOur view name is **\"books_with_authors\"**. The following code fetches data from the view:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[Book registerClass];\\n[Author registerClass];\\n\\n- (void)downloadBooksAndAuthors {\\n  [[Book pleaseForDataEndpoint:@\\\"books_with_authors\\\"] giveMeDataObjectsWithCompletion:^(NSArray *objects, NSError *error) {\\n    //error handling\\n    Book *book = objects.firstObject;\\n    //If your view is properly configured you can now access book.author.country\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"Book.registerClass()\\nAuthor.registerClass()\\n\\nfunc downloadBooksAndAuthors() {\\tBook.pleaseForDataEndpoint(\\\"books_with_authors\\\").giveMeDataObjectsWithCompletion { books, error in\\n    //handle error\\n    let book = books.first\\n    //If your view is properly configured you can now access book.author.country\\n  }\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nYou can easily filter on data endpoints:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadBooksWithNbOfPages:(NSNumber*)nbOfPages {\\n  [[Book pleaseForDataEndpoint:@\\\"books_with_authors\\\"] giveMeDataObjectsWithPredicate:[SCPredicate whereKey:@\\\"numofpages\\\" isEqualToNumber:nbOfPages] parameters:nil completion:^(NSArray *objects, NSError *error) {\\n    //error handling\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadBooksWithNbOfPages(nbOfPages: NSNumber!) {\\n\\tBook.pleaseForDataEndpoint(\\\"books_with_authors\\\").giveMeDataObjectsWithPredicate(SCPredicate.whereKey(\\\"numofpages\\\", isEqualToNumber: nbOfPages), parameters: nil) { books, error in\\n  //handle error\\n  }\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nIf you have a data class which represents Data Endpoint you can specify data endpoint name for this data class and type less:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"@interface BooksView : Book\\[email protected]\\n\\[email protected] BooksView\\n+ (NSString *)dataEndpointNameForAPI {\\n    return @\\\"books_with_authors\\\";\\n}\\[email protected]\\n  \\n- (void)downloadBooksAndAuthors {\\n  [[BooksView please] giveMeDataObjectsWithCompletion:^(NSArray *objects, NSError *error) {\\n    //error handling\\n  }];\\n}  \",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"class BooksView : Book {\\n    override class func dataEndpointNameForAPI() -> String! {\\n        return \\\"books_with_authors\\\"\\n    }\\n}\\n\\nfunc downloadBooksAndAuthors() {\\n\\tBooksView.please().giveMeDataObjectsWithCompletion { books, error in\\n  //handle error\\n  //do something with downloaded objects\\n  }\\n} \",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nDelete Objects\n=====================\nAdd this following function to delete a passed Book object.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)deleteBook:(Book *)book {\\n  [book deleteWithCompletion:^(NSError *error) {\\n    //handle error\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func deleteBook(book: Book) {\\n  book.deleteWithCompletion { error in\\n    //handle error\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nNow you can call this method whenever you want to delete a chosen objects.\n\nDownload files\n=====================\nFile can be saved to memory, use this for smaller files:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadCoverForBook:(Book *)book {\\n  book.cover.storeDataAfterFetch = YES;//set to YES when you want to access data via: book.cover.data property\\n  [book.cover fetchInBackgroundWithCompletion:^(NSData *data, NSError *error) {\\n    //handle error\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadCoverForBook(book: Book) {\\n    book.cover?.storeDataAfterFetch = true//set to true when you want to access data via: book.cover.data\\n    book.cover?.fetchInBackgroundWithCompletion { data, error in\\n        //handle error\\n    }\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nFor bigger files or when it's useful, a file can be saved to disk (temporary location):\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadCoverToDiskForBook:(Book *)book {\\n  [book.cover fetchToFileInBackgroundWithProgress:^(NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {\\n    //display progress NSLog(@\\\"%lld %lld %.0f\\\",totalBytesWritten,totalBytesExpectedToWrite,((float)(totalBytesWritten * 100) / totalBytesExpectedToWrite));\\n  } completion:^(NSURLResponse *response, NSURL *filePath, NSError *error) {\\n    //handle error\\n    NSData *data = [NSData dataWithContentsOfURL:filePath];\\n    //use data\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadCoverToDiskForBook(book: Book) {\\n    book.cover?.fetchToFileInBackgroundWithProgress({ downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in\\n            //display progress\\n        }, completion: { response, filePath, error in\\n            //handle error\\n            let data = NSData(contentsOfURL: filePath)\\n            //use data\\n    })\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nA file can be also saved to documents:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)downloadCoverToDocumentsForBook:(Book *)book {\\n  NSURL *storePathDocuments = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];\\n  NSURL *storePath = [storePathDocuments URLByAppendingPathComponent:@\\\"bookCover.png\\\"];\\n  \\n  [book.cover fetchToFileInBackground:storePath withProgress:^(NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {\\n            //display progress\\n        } completion:^(NSURLResponse *response, NSURL *filePath, NSError *error) {\\n    //handle error\\n    NSData *data = [NSData dataWithContentsOfURL:storePath];\\n    //use data\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func downloadCoverToDocumentsForBook(book: Book) throws {\\n    let storePathDocuments = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)\\n    let storePath = storePathDocuments.URLByAppendingPathComponent(\\\"bookCover.png\\\")\\n    \\n    book.cover?.fetchToFileInBackground(storePath, withProgress: { downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in\\n            //display progress\\n        }, completion: { response, filePath, error in\\n            //handle error\\n            let data = NSData(contentsOfURL: filePath)\\n            //use data\\n    })\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nRunning Scripts\n============================\nLearn more about [Script](doc:snippets-scripts) and [Script Endpoints](doc:endpoints-scripts) in our Developer Manual. Here, you can see how to quickly run a Script or Script with endpoint.\n\n###Scripts\n\nRun a script with a specified ID and get back its Trace. You can find out what is a Trace in the [Traces](doc:traces) chapter.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)runScript:(NSInteger)codeboxID {\\n  [SCScript runScriptWithId:@(codeboxID) params:nil completion:^(SCTrace *trace, NSError *error) {\\n    //handle error\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func runScript(codeboxID: Int) {\\n  SCScript.runScriptWithId(codeboxID, params: nil) { trace, error in\\n    //handle error\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n###Script Endpoints\n\nYou can run a script endpoint by passing its name. You will receive the details of the execution in a response (`SCScriptEndpointResponse` class).\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)runScriptEndpoint:(NSString *)webhookName {\\n  [SCScriptEndpoint runScriptEndpointWithName:webhookName completion:^(SCWebhookResponseObject *response, NSError *error) {\\n    //handle error\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func runScriptEndpoint(name: String) {\\n  SCScriptEndpoint.runScriptEndpointWithName(name) { response, error in\\n    //handle error\\n   }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nIn case your script endpoint uses your own (custom) response format, you can use the following code snippet:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)runScriptEndpoint:(NSString *)webhookName {\\n  [SCScriptEndpoint runCustomScriptEndpointWithName:webhookName completion:^(id responseObject, NSError *error) {\\n    //handle error\\n    //responseObject is most likely an instance of NSData*\\n    NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:NULL];\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func runScriptEndpoint(name: String) {\\n    SCScriptEndpoint.runCustomScriptEndpointWithName(name) { response, error in\\n        //handle error\\n        do {\\n            let array = try NSJSONSerialization.JSONObjectWithData(response as! NSData!, options: []);\\n        } catch let jsonError {\\n            //handle parse error\\n        }\\n    }\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nSynchronization and Real-Time Channels\n========================\nYou can read more about Real-Time Syncing and channels in the [Realtime communication](doc:realtime-communication) chapter, but here you can quickly learn how to listen for notifications sent to a given channel.\n\nFirst, add the channel object to your view controller and add information. For this you will implement SCChannelDelegate protocol.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"@interface ViewController ()<SCChannelDelegate>\\[email protected] (strong) Syncano *syncano;\\[email protected] (strong) SCChannel *channel;\\[email protected]\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"class SwiftViewController: SCChannelDelegate {\\n  let syncano = Syncano.sharedInstanceWithApiKey(\\\"API_KEY\\\", instanceName: \\\"INSTANCE_NAME\\\")\\n  var channel: SCChannel? = nil\\n  //rest of the class\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nInitialize the channel object and start listening to incoming messages. In our example we will do it in the viewDidLoad method by adding the following lines there.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"self.channel = [[SCChannel alloc] initWithName:@\\\"channel\\\" andDelegate:self];\\n[self.channel subscribeToChannel];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"self.channel = SCChannel(name: \\\"channel\\\", andDelegate: self)\\nself.channel?.subscribeToChannel()\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nImplement the SCChannelDelegate protocol by pasting this function into your data class implementation (e.g. right above `viewDidLoad`).\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)chanellDidReceivedNotificationMessage:(SCChannelNotificationMessage *)notificationMessage {\\n    //handle notification\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func chanellDidReceivedNotificationMessage(notificationMessage: SCChannelNotificationMessage) {\\n  //handle notification\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nThat's it! From now on, whenever there's an incoming message, such as an object added/changed/deleted, the `chanellDidReceivedNotificationMessage` method will be called.\n\nMake sure to read [Realtime communication](doc:realtime-communication) chapter to learn more about how it works!\n\nHandling users\n============\n\nTo find out more about the Users module on Syncano - read [User management](doc:user-management) chapter. Here, we'll quickly teach you how to create a new user, log the user in and get their profile data.\n\n### Register new user\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)registerUser:(NSString *)username password:(NSString *)password {\\n    [SCUser registerWithUsername:username password:password completion:^(NSError *error) {\\n      //handle user registration\\n    }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func registerUser(username: String, password: String) {\\n  SCUser.registerWithUsername(username, password: password) { error in\\n    //handle user registration\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n### Log in user with password\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)loginUser:(NSString *)username password:(NSString *)password {\\n  [SCUser loginWithUsername:username password:password completion:^(NSError *error) {\\n    //handle logged in user\\n  }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func loginUser(username: String, password: String) {\\n  SCUser.loginWithUsername(username, password: password) { error in\\n    //handle logged in user\\n  }\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n### Log in user with social backend\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Available backends:\\n// SCSocialAuthenticationBackendFacebook\\n// SCSocialAuthenticationBackendGoogle\\n// SCSocialAuthenticationBackendLinkedIn\\n// SCSocialAuthenticationBackendTwitter\\n[SCUser loginWithSocialBackend:SCSocialAuthenticationBackendFacebook authToken:@\\\"FACEBOOK_AUTH_TOKEN\\\" completion:^(NSError *error) {\\n\\t//handle logged in user\\n}];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Available backends:\\n// .Facebook\\n// .Google\\n// .LinkedIn\\n// .Twitter\\nSCUser.loginWithSocialBackend(.Facebook, authToken: \\\"FACEBOOK_AUTH_TOKEN\\\") { error in\\n\\t//handle logged in user\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n### Get user profile\n\nFirst we need to grab a currently logged in user, next we can easily get their profile.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)getCurrentUserProfile {\\n  SCUser *currentUser = [SCUser currentUser];\\n  SCUserProfile *profile = currentUser.profile;\\n  //display profile in your app\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func getCurrentUserProfile() {\\n  let currentUser = SCUser.currentUser()\\n  let profile = currentUser.profile\\n  //display profile in your app\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\n### Subclassing user and user profile\n\nYou can also subclass SCUser and SCUserProfile class - really handy when you want to expand user's profile and some extra properties. For more details on how that works, go to [User management](doc:user-management#user-profiles) and see below how to prepare your data classes on iOS.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"//declare new User Profile class, by subclassing SCUserProfile\\[email protected] MyUserProfile : SCUserProfile\\[email protected] (strong) SCFile *avatar;\\[email protected] (strong) NSString *first_name;\\[email protected] (strong) NSString *last_name;\\[email protected] (strong) NSString *email;\\[email protected]\\n  \\[email protected] MyUserProfile\\n//implementation can be empty - no need to add anything\\[email protected]\\n\\n//subclass SCUser to type of 'profile' property to MyUserProfile\\[email protected] MyUser : SCUser\\[email protected] (strong, nonatomic) MyUserProfile *profile;\\[email protected]\\n  \\[email protected] MyUser\\n//we won't be implementing this property here - it's already implemented in superclass, so we will tell that to compiler by using '@dynamic' \\[email protected] profile;\\[email protected]\\n  \\n//now, in your code, before you use for the first time SCUser class (or your subclass):\\n[MyUser registerClassWithProfileClass:[MyUserProfile class]];\\n\\n//from now on you can keep using your own classes\\n[MyUser loginWithUsername:@\\\"a\\\" password:@\\\"a\\\" completion:^(NSError *error) {\\n  MyUser *user = [MyUser currentUser];\\n  MyUserProfile *profile = user.profile;\\n  //work with user profile and user objects\\n}];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"//declare new User Profile class, by subclassing SCUserProfile\\nclass MyUserProfile : SCUserProfile {\\n    var avatar : SCFile? = nil;\\n    var name = \\\"\\\"\\n    var first_name = \\\"\\\"\\n    var email = \\\"\\\"\\n}\\n\\n//subclass SCUser to add new profile variable for the profile\\n//Swift doesn't allow overring superclass' property type\\n//(so we cannot 'profile' to be of MyUserProfile type)\\n//but we can add a convenient setter and getter\\nclass MyUser : SCUser {\\n    var myProfile : MyUserProfile? {\\n        get {\\n            return self.profile as? MyUserProfile\\n        }\\n        set {\\n            self.profile = newValue\\n        }\\n    }\\n}\\n\\n//now, in your code, before you use for the first time SCUser class (or your subclass):\\nMyUser.registerClassWithProfileClass(MyUserProfile.self)\\n  \\n//from now on you can keep using your own classes\\nMyUser.loginWithUsername(\\\"login\\\", password: \\\"password\\\") { error in\\n  let user = MyUser.currentUser()\\n  let profile = user.myProfile\\n  //work with user profile and user objects\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nSupport\n======\n\nNow you’re ready to use Syncano in your iOS project. We really hope you will enjoy working with our platform. If you have any issues or suggestions, just let us know at [email protected]\n\nLicense\n======\n\nSyncano’s iOS Library (syncano-ios) is available under the MIT license. See the LICENSE file inside library sources for more info.\n\n------------------------------------------------------------------------------------------------------------------\n\nSwift Bridging Header\n========================\n\nIf for any reason, you need to use syncano-ios in Swift, using with a bridging header, follow the steps below.\n\n**If you don't have the bridging header yet:**\n\nYou can add a bridging header manually, but the easiest way is to use XCode:\n1. Add a new **.m** file to your project, by choosing **File -> New -> File -> “Objective-C File”** (the one with **.m** icon)\n2. Give it some random name e.g. “SyncanoSwift” (it’s not important, as we will not be using this file for anything later)\n3. As for file type, you can leave the selection on **Empty File** and click **Next**\n4. Save the file anywhere in the project, by choosing a directory and clicking **Create**\n5. When XCode asks you “Would you like to configure an Objective-C bridging header?”, choose **Yes**\n6. Now XCode should add a bridging file to your project named YourProjectName-Bridging-Header.h, so in our case it would be **SyncanoProject-Bridging-Header.h**\n7. You can remove the previously created **.m** file. Choose it in your project, right click on it, choose **Delete** and **Move to Trash**\n8. Add syncano-ios header to your bridging header as in `When you have the bridging header` section below\n\n**When you have the bridging header:**\n\nChoose the bridging header in XCode and paste this **#import** directive:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import <Syncano.h>\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nBecause you already have library files included in the bridging header, there’s no need to import anything else in your `.swift` files.","excerpt":"iOS SDK Quick Start Guide","slug":"ios","type":"basic","title":"iOS"}

iOS

iOS SDK Quick Start Guide

## Chapter Contents: 1. [Overview](#overview) 2. [Installing the Syncano iOS Library](#installing-the-syncano-ios-library) 3. [Using the Syncano Library](#using-the-syncano-library) 4. [Support](#section-support) 5. [License](#section-license) [block:callout] { "type": "info", "title": "Sockets release", "body": "We recently released a new version of our API that uses Scripts instead of CodeBoxes and Script Endpoints instead of Webhooks.\nFor us, it's not just about the name change - you can [read about it](https://www.syncano.io/blog/what-are-we-cooking-up-with-sockets/) on our blog.\nFor now, you can keep using old methods, and when we release new version of this library, we will update our docs here." } [/block] [block:api-header] { "type": "basic", "title": "Overview" } [/block] This guide will walk you through the installation steps of the Syncano Library for iOS as well as give you a couple of usage examples. If you don't have a Syncano account yet, you can read about how to create one [here](doc:getting-started-with-syncano). For the full API listing, view the [syncano-ios reference](http://cocoadocs.org/docsets/syncano-ios/4.0.8/). [block:api-header] { "type": "basic", "title": "Installing the Syncano iOS Library" } [/block] 1. [Source](#section-source) 2. [CocoaPods](#section-cocoapods) 3. [XCode](#section-xcode) 4. [Create New XCode project](#section-create-new-xcode-project) 5. [Add the Syncano Library to the project](#section-add-the-syncano-library-to-the-project) Source ====== We recommend installing the syncano-ios library through CocoaPods, but you can always grab the source from [our GitHub](https://github.com/syncano/syncano-ios). CocoaPods ========= To install our Syncano iOS Library, you need to use [CocoaPods](http://cocoapods.org/) . If you don’t already have it, open the Terminal app and paste the following line: [block:code] { "codes": [ { "code": "sudo gem install cocoapods", "language": "shell" } ] } [/block] [block:callout] { "type": "info", "title": "Installing CocoaPods", "body": "If you didn't have CocoaPods installed before, please make sure to close and open a new Terminal window before proceeding to the next steps - otherwise `pod` command might not be recognized when you will try to use it." } [/block] XCode ===== We assume you already have an XCode IDE. If not, you can find it and install using the MacOS [AppStore](https://itunes.apple.com/us/app/xcode/id497799835?mt=12) Create New XCode project ==================== Launch XCode and do the following: 1. Using the XCode menu, choose **File -> New -> Project** 2. Choose the template that best suits your project. For now, to keep it simple, we’ll use **Single View Application** 3. Name the product as you want, or just type **SyncanoProject**. In the prefix field enter SYN (you can keep devices in **Universal** state, or change it to either **iPhone** or **iPad**) 4. Choose where you want to keep your project on the disk and hit the **Create** button 5. Close the XCode project for now Add the Syncano Library to the project ========================== Open the Terminal app and `cd` into the folder where you chose to save your project. If you don’t know how to do this, type [block:code] { "codes": [ { "code": "cd", "language": "shell", "name": "Shell" } ] } [/block] and drag & drop the folder that contains your XCode project in Finder into the Terminal app. If you followed previous steps, you should have a file named **SyncanoProject.xcodeproj** inside a folder **SyncanoProject**. That’s the folder you need to drag from Finder into Terminal. When you do this, go to the Terminal app and press **ENTER** to confirm. If you don’t have CocoaPods yet, check how to install it [here](#section-cocoapods). Once you have it, in Terminal (in folder where **.xcodeproj** file is) type: [block:code] { "codes": [ { "code": "pod init", "language": "shell", "name": "Shell" } ] } [/block] Now, open the file named **Podfile** in your desired text editor and under app target, before the end, add this line: [block:code] { "codes": [ { "code": "pod 'syncano-ios'", "language": "shell", "name": "Shell" } ] } [/block] If you followed the suggested naming, your **Podfile** should now look like below. [block:callout] { "type": "info", "title": "Using Syncano in a Swift project", "body": "If you use Syncano in Swift with CocoaPods - please make sure to uncomment in the `Podfile` below line saying `use_frameworks!`. \n\nIf for some reason you cannot use pods as frameworks - read more about [how to use a bridging header](#section-swift-bridging-header)." } [/block] [block:code] { "codes": [ { "code": "# Uncomment this line if you're using Swift\n# use_frameworks!\n\ntarget \"SyncanoProject\" do\npod 'syncano-ios'\nend\n\ntarget \"SyncanoProjectTests\" do\n\nend", "language": "ruby", "name": "Obj-C project" }, { "code": "# Uncomment this line if you're using Swift\nuse_frameworks!\n\ntarget \"SyncanoProject\" do\npod 'syncano-ios'\nend\n\ntarget \"SyncanoProjectTests\" do\n\nend", "language": "ruby", "name": "Swift project" } ] } [/block] Now you can save the **Podfile**. Go back to Terminal and install the Syncano library by typing: [block:code] { "codes": [ { "code": "pod install", "language": "shell", "name": "Shell" } ] } [/block] When the process is finished, you should see in the project’s folder a new **.xworkspace** (presumably **SyncanoProject.xcworkspace**) and from now on, you should work using this file, instead of the **.xcodeproj** one. You can open the workspace by double clicking it in Finder or just stay in Terminal and typing: [block:code] { "codes": [ { "code": "open SyncanoProject.xcworkspace", "language": "shell", "name": "Shell" } ] } [/block] The library is already downloaded and added to your project. Every time you want to use it, just import Syncano: [block:code] { "codes": [ { "code": "#import <Syncano.h>", "language": "objectivec" }, { "code": "import syncano_ios", "language": "objectivec", "name": "Swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Using the Syncano Library" } [/block] 1. [Importing Syncano Headers](#section-importing-syncano-headers) 2. [Connecting to Syncano](#section-connecting-to-syncano) 3. [Adding your Data Class](#section-adding-your-class) 1. [Using references type field](#section-using-reference-type-field) 4. [Download the most recent Objects](#section-download-the-most-recent-objects) 5. [Create New Object](#section-create-new-object) 6. [Modify Object](#section-modify-object) 7. [Download objects matching specified criteria](#section-download-objects-matching-specified-criteria) 1. [Using Predicates](#section-using-predicates) 2. [Page Size and Order By](#section-page-size-and-order-by) 3. [Using Pages](#section-using-pages) 4. [Field filtering](#section-field-filtering) 8. [Data Endpoints](#section-data-endpoints) 9. [Delete Objects](#section-delete-objects) 10. [Download files](#section-download-files) 11. [Running Scripts and Script Endpoints](#section-running-scripts) 1. [Scripts](#section-scripts) 2. [Script Endpoints](#section-script-endpoints) 11. [Synchronization and Real-Time Channels](#section-synchronization-and-real-time-channels) 12. [Handling users](#section-handling-users) 1. [Register new user](#section-register-new-user) 2. [Log in user with password](#section-log-in-user-with-password) 3. [Log in user with social backend](#section-log-in-user-with-social-backend) 3. [Get user profile](#section-get-user-profile) 4. [Subclassing User and User Profile](#section-subclassing-user-and-user-profile) 13. [Support](#section-support) 14. [License](#section-license) Importing Syncano Headers ===================== + Click a chosen file, we’ll use **SYNViewController.m** for now + Import Syncano libraries on top of your file, by adding following lines: [block:code] { "codes": [ { "code": "#import <Syncano.h>", "language": "objectivec" }, { "code": "import syncano_ios\n// or, if you used a bridging header in Swift, \n// there's no need to import anything\n// you already imported Syncano header in the bridging file", "language": "javascript", "name": "Swift" } ] } [/block] Connecting to Syncano ===================== + Add a property, that will store the main Syncano object. [block:code] { "codes": [ { "code": "@interface SYNViewController ()\[email protected] (strong) Syncano *syncano;\[email protected]", "language": "objectivec" }, { "code": "//In Swift we can both declare and initialize our constant Syncano object, there's no need to do it in two places like in Obj-C\n\nlet syncano = Syncano.sharedInstanceWithApiKey(\"API_KEY\", instanceName: \"INSTANCE_NAME\")", "language": "javascript", "name": "Swift" } ] } [/block] + In the **viewDidLoad** method, initialize a Syncano object: [block:code] { "codes": [ { "code": "- (void)viewDidLoad {\n [super viewDidLoad];\n // Do any additional setup after loading the view, typically from a nib.\n self.syncano = [Syncano sharedInstanceWithApiKey:@\"API_KEY\" instanceName:@\"INSTANCE_NAME\"];\n \n}", "language": "objectivec" }, { "code": "//No need to add anything in Swift, Syncano object is already initialized", "language": "javascript", "name": "Swift" } ] } [/block] Adding your Data Class ================ [block:callout] { "type": "info", "title": "Using data classes in Syncano", "body": "Before you will use the Data Class in your app, remember that the Data Class should already exist in Syncano. You can add your data classes quickly using our [Dashboard](https://dashboard.syncano.io)!\n\nYou can read more about data classes [here](doc:classes)." } [/block] Now we will add the Data Class you had implemented on Syncano. We will be using a simple Book data class which only has two attributes: "title" and "subtitle", both being of type "string". The Schema for this Data Class would look like this: [block:code] { "codes": [ { "code": "[\n {\"type\": \"string\",\"name\": \"title\"},\n {\"type\": \"string\",\"name\": \"subtitle\"}\n]", "language": "json" } ] } [/block] We will add this data class in the app as well. Either in a separate file, or in the same view controller. For simplicity, we will assume you're adding this data class to the `SYNViewController.m` file (or in `ViewController.swift`), in this case, add the following code right under `#import <Syncano.h>` (or for Swift, right above `class ViewController: UINavigationController` line) [block:code] { "codes": [ { "code": "#import <Syncano.h>\n\[email protected] Book : SCDataObject\[email protected] (nonatomic,copy) NSString *title;\[email protected] (nonatomic,copy) NSString *subtitle;\[email protected]", "language": "objectivec", "name": "" }, { "code": "class Book : SCDataObject {\n var title = \"\"\n var subtitle = \"\"\n}", "language": "javascript", "name": "Swift" } ] } [/block] Add the data class implementation below (or in the Book.m file if you are choosing to use separate files for this data class). For Swift we don't need a separate implementation, as declaration and implementation happens in one place. [block:code] { "codes": [ { "code": "@implementation Book\[email protected]", "language": "objectivec" }, { "code": "//No need to add anything more, we already implemented this function.", "language": "javascript", "name": "Swift" } ] } [/block] [block:callout] { "type": "warning", "title": "Registering a data class", "body": "Before you continue, make sure to register all your Data Classes. We recommend adding this code for all your data classes in your app delegate." } [/block] [block:code] { "codes": [ { "code": "- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n // Override point for customization after application launch.\n [Book registerClass];\n [Author registerClass];\n //register this way all data classes, including subclasses of SCUser and SCUserProfile\n [MyUserProfile registerClass];\n [MyUser registerClassWithProfileClass:[MyUserProfile class]];\n return YES;\n}", "language": "objectivec" }, { "code": "func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {\n\t// Override point for customization after application launch.\n\tBook.registerClass()\n\tAuthor.registerClass()\n //register this way all classes, including subclasses of SCUser and SCUserProfile\n MyUserProfile.registerClass()\n MyUser.registerClassWithProfileClass(MyUserProfile.self)\n\treturn true\n}", "language": "swift" } ] } [/block] ### Using `Reference` type field If you'd like to use `Reference` type field in your data class -- it's possible to do that in iOS. For example, if you defined `Author` data class in the same way as `Book` above, and now you wanted to have reference to author from book (and you defined its data class schema e.g. through our [dashboard](doc:classes)) - you just need to add the field to your data class. [block:code] { "codes": [ { "code": "#import <Syncano.h>\n\[email protected] Book : SCDataObject\[email protected] (nonatomic,copy) NSString *title;\[email protected] (nonatomic,copy) NSString *subtitle;\[email protected] (nonatoic,strong) Author *author;\[email protected]", "language": "objectivec" }, { "code": "class Book : SCDataObject {\n var title = \"\"\n var subtitle = \"\"\n var author : Author?\n}", "language": "objectivec", "name": "Swift" } ] } [/block] By defining Data Class as above, whenever you save your `book` object - it will save reference to `author` automatically. If `author` object was just created in your app, and doesn't exist on Syncano yet - library will save it on Syncano as well. If `author` object already exists on Syncano - library won't save it, but will update reference to it in `book` object. [block:code] { "codes": [ { "code": "// Case nr. 1\n// Book already exists, but we're adding to it a new Author\n// book will be saved, author will be saved on Syncano as well\n// and book will hold a reference to the new author object\nBook *book = [self getExistingBook];\nAuthor *author = [[Author alloc] init];\nauthor.first_name = @\"Tom\";\nauthor.last_name = @\"Tomson\";\nbook.author = author;\n[book saveWithCompletionBlock: ^(NSError *error){\n}];\n\n// Case nr. 2\n// Book already exists and Author already exists on Syncano\n// book will saved and reference to author wll be saved\n// author object will not even be sent to Syncano\nlet book : Book = [self getExistingBook];\nlet author = [self getExistingAuthor];\nbook.author = author;\n[book saveWithCompletionBlock: ^(NSError *error){\n}];", "language": "objectivec" }, { "code": "// Case nr. 1\n// Book already exists, but we're adding to it a new Author\n// book will be saved, author will be saved on Syncano as well\n// and book will hold a reference to the new author object\nlet book : Book = getExistingBook()\nlet author = Author()\nauthor.first_name = \"Tom\"\nauthor.last_name = \"Tomson\"\nbook.author = author\nbook.saveWithCompletionBlock() { error in }\n\n// Case nr. 2\n// Book already exists and Author already exists on Syncano\n// book will saved and reference to author wll be saved\n// author object will not even be sent to Syncano\nlet book : Book = getExistingBook()\nlet author = getExistingAuthor()\nbook.author = author\nbook.saveWithCompletionBlock() { error in }", "language": "objectivec", "name": "Swift" } ] } [/block] Download the most recent Objects ============================== Add the following function to download the most recent Books, somewhere in the view controller data class implementation [block:code] { "codes": [ { "code": "- (void)downloadBooks {\n [[Book please] giveMeDataObjectsWithCompletion:^(NSArray *books, NSError *error) {\n //handle error\n //do something with downloaded objects\n }];\n}", "language": "objectivec" }, { "code": "func downloadBooks() {\n Book.please().giveMeDataObjectsWithCompletion { books, error in\n //handle error\n //do something with downloaded objects\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] Make sure you call that function from inside of the `viewDidLoad` method, so the code runs when you launch your app. [block:code] { "codes": [ { "code": "- (void)viewDidLoad {\n [super viewDidLoad];\n \n self.syncano = [Syncano sharedInstanceWithApiKey:@\"YOUR_KEY\" instanceName:@\"YOUR_INSTANCE\"];\n [self downloadBooks];\n}", "language": "objectivec" }, { "code": "override func viewDidLoad() {\n super.viewDidLoad()\n self.downloadBooks()\n}", "language": "javascript", "name": "Swift" } ] } [/block] That's it, you just download a list of your books stored on Syncano! Create New Object =============== Often, you will not be only downloading data created by others, but you will also want to create new objects yourself. To do it, you need to create a new object of the data class you'd like to work with, set chosen attributes, and save the object to Syncano. [block:code] { "codes": [ { "code": "//define a data class like shown earlier, in \"Adding your Data Class\"\n\n- (void)addNewBook {\n Book *book = [Book new];\n book.title = @\"How to be a Pirate\";\n book.subtitle = @\"10 tips that will change your life.\";\n [book saveWithCompletionBlock:^(NSError *error) {\n //process saved book\n //handle error\n }];\n}", "language": "objectivec" }, { "code": "//define a class like shown earlier, in \"Adding your class\"\n\nfunc addNewBook() {\n let book = Book()\n book.title = \"How to be a Pirate\"\n book.subtitle = \"10 tips that will change your life.\"\n book.saveWithCompletionBlock { error in\n //handle error\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] Modify Object ================ You can modify Syncano objects exactly in the same way as you modify every object in iOS. The only difference is that you have to save the changes at the end, otherwise your modifications will not be saved in Syncano. [block:code] { "codes": [ { "code": "- (void)updateBook:(Book *)book {\n book.title = @\"Pirate ships\";\n book.subtitle = @\"Which was the biggest?\";\n [book saveWithCompletionBlock:^(NSError *error) {\n //error handling\n }];\n}", "language": "objectivec" }, { "code": "func updateBook(book: Book) {\n book.title = \"Pirate ships\"\n book.subtitle = \"Which was the biggest?\"\n book.saveWithCompletionBlock { error in\n //error handling\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] After adding this method, you will be able to pass it any book object, and it will be updated with a new `title` and `subtitle`. Download objects matching specified criteria ================================== There are couple of ways to filter results while getting a Data Object list. More detailed information about filtering can be found [here](http://docs.syncano.com/v0.1.1/docs/filtering-data-objects). [block:callout] { "type": "info", "title": "Indexes", "body": "To be able to filter and sort based on chosen fields, please remember you have to first set an index on those fields. You can read more about it [here](doc:classes)." } [/block] [block:callout] { "type": "info", "title": "Permissions", "body": "If you are trying to get objects from other users, you must add a read permission under other_permissions. In swift this looks like \"book.other_permissions = .Read\". If this is not done, when you try and get objects you will only get objects the current user has created." } [/block] ### Using Predicates Usually we don't need to download all objects - we need only specific ones, matching our needs. E.g. books written only by one chosen author, or books with more than 300 pages - this can all be done by Syncano as well. For the example below, we'll try to find a book titled `Pirate ship`. In the previous code examples above, that's how we titled one of our existing books. [block:code] { "codes": [ { "code": "- (void)downloadPirateBooks {\n SCPredicate *pirateBookPredicate = [SCPredicate whereKey:@\"title\" isEqualToString:@\"Pirate ships\"];\n [[Book please] giveMeDataObjectsWithPredicate:pirateBookPredicate parameters:nil completion:^(NSArray *books, NSError *error) {\n //error handling\n }];\n}", "language": "objectivec" }, { "code": "func downloadPirateBooks() {\n let pirateBookPredicate = SCPredicate.whereKey(\"title\", isEqualToString: \"Pirate ships\")\n Book.please().giveMeDataObjectsWithPredicate(pirateBookPredicate, parameters: nil) { books, error in\n //error handling\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] **Using multiple predicates** If you want to filter by more than one attribute at the same time, or to apply multiple criteria to one attribute - you can do that using `SCCompoundPredicate` [block:code] { "codes": [ { "code": "- (void)downloadPirateBooks {\n SCPredicate *pirateBookPredicate = [SCPredicate whereKey:@\"title\" isEqualToString:@\"Pirate ships\"];\n SCPredicate *numberOfPagesPredicate = [SCPredicate whereKey:@\"numberOfPages\" isGreaterThanOrEqualToNumber:@500];\n SCCompoundPredicate *compoundPredicate = [SCCompoundPredicate compoundPredicateWithPredicates:@[pirateBookPredicate,numberOfPagesPredicate]];\n [[Book please] giveMeDataObjectsWithPredicate:compoundPredicate parameters:nil completion:^(NSArray *books, NSError *error) {\n //error handling\n }];\n}", "language": "objectivec" }, { "code": "func downloadLongPirateBooks() {\n let pirateBookPredicate = SCPredicate.whereKey(\"title\", isEqualToString: \"Pirate ships\")\n let numberOfPagesPredicate = SCPredicate.whereKey(\"numberOfPages\", isGreaterThanOrEqualToNumber:500)\n let compoundPredicate = SCCompoundPredicate(predicates: [pirateBookPredicate,numberOfPagesPredicate])\n Book.please().giveMeDataObjectsWithPredicate(compoundPredicate, parameters: nil) { books, error in\n //error handling\n }\n}", "language": "objectivec", "name": "Swift" } ] } [/block] [block:callout] { "type": "info", "title": "Using Multiple Predicates", "body": "When joining predicates using `SCCompoundPredicate`, please remember they are joined using `AND` - returned objects will have to match all passed criteria. \nAt the moment there is no way to pass predicates joined by an `OR` statement." } [/block] **Using referenced data class queries** If you want to find books written by authors from given country, you will find referenced data class queries useful. Keep in mind you must have the filter checkbox marked in the data class field you want to use or you will get failed requests: [block:code] { "codes": [ { "code": "- (void)downloadBooksFromNorway {\n SCPredicate *predicate = [SCPredicate whereKey:@\"author\" satisfiesPredicate:\n [SCPredicate whereKey:@\"country\" isEqualToString:@\"Norway\"]];\n [[Book please] giveMeDataObjectsWithPredicate:predicate parameters:nil completion:^(NSArray *books, NSError *error) {\n //error handling\n }];\n}", "language": "objectivec" }, { "code": "func downloadBooksFromNorway() {\n let innerPredicate = SCPredicate.whereKey(\"country\", isEqualToString: \"Norway\")\n let predicate = SCPredicate.whereKey(\"author\", satisfiesPredicate: innerPredicate)\n Book.please().giveMeDataObjectsWithPredicate(predicate, parameters: nil) { books, error in\n //error handling\n }\n}", "language": "swift" } ] } [/block] ### Page Size and Order By When you don't need to display a lot of data on the screen at once, you may want to limit the number of objects downloaded at once. You can do that by setting the `Page Size` parameter. You can also modify the way the data returned from Syncano is sorted - if you want to sort on a different field from `id`, e.g. `title` of the book, you can also configure it this way. [block:code] { "codes": [ { "code": "- (void)downloadBooksOrderedByTitle:(NSInteger)count {\n [Book.please giveMeDataObjectsWithParameters:@{\n SCPleaseParameterPageSize : @(count),\n SCPleaseParameterOrderBy : @\"title\" \n } completion:^(NSArray *books, NSError *error) {\n //handle error\n }];\n}\n\n//if you wanted to download only 5 books, you could do e.g.\n//[self downloadBooksOrderedByTitle:5];", "language": "objectivec", "name": "" }, { "code": "func downloadBooksOrderedByTitle(count: Int) {\n Book.please().giveMeDataObjectsWithParameters(\n [SCPleaseParameterPageSize:count,\n SCPleaseParameterOrderBy:\"title\"]) { books, error in\n //handle error\n }\n}\n\n//if you wanted to download only 5 books, you could do e.g.\n//self.downloadBooksOrderedByTitle(5)", "language": "javascript", "name": "Swift" } ] } [/block] ### Using Pages By default, when you use function `giveMeDataObjectsWithCompletion`, or `giveMeDataObjectsWithParameters` etc - they return up to 100 objects in request (less if you have less than 100 objects in your data class, or if you set page size to be less than 100). To get more objects then only the ones from the first page, you need to other methods on our `SCPlease` object. You can ask for next/previous page of results (e.g. in response to user clicking "load more" button). [block:code] { "codes": [ { "code": "SCPlease *please = [Book please];\n// at some point in your coode you already downloaded some data, using predicate, parameters or not\n// e.g. by using 'giveMeDataObjectsWithCompletion' method\n// you should save SCPlease object as a variable and pass it around to get next or previous page\n[please giveMeNextPageOfDataObjectsWithCompletion:^(NSArray *objects, NSError *error) { \n\t//work with your objects\n}];\n\n//or\n\n[please giveMePreviousPageOfDataObjectsWithCompletion:^(NSArray *objects, NSError *error) { \n\t//work with your objects\n}];", "language": "objectivec" }, { "code": "let please = Book.please()\n// at some point in your coode you already downloaded some data, using predicate, parameters or not\n// e.g. by using 'giveMeDataObjectsWithCompletion' method\n// you should save SCPlease object as a variable and pass it around to get next or previous page\nplease.giveMeNextPageOfDataObjectsWithCompletion { books, error in\n //work with your objects\n}\n\n//or\n\nplease.giveMePreviousPageOfDataObjectsWithCompletion { books, error in\n //work with your objects\n}", "language": "objectivec", "name": "Swift" } ] } [/block] [block:callout] { "type": "warning", "title": "Get next or previous page", "body": "Remember, that when using `[Book please]` in Obj-C or `Book.please()` in Swift - it always returns a new SCPlease object.\nUsing `giveMeNextPageOfDataObjectsWithCompletion` or `giveMePreviousPageOfDataObjectsWithCompletion` on a new SCPlease object will always return 0 objects, as `next` or `previous` must be used in context of a request done earlier.\n\nTo use these methods properly:\n1) Make sure to store SCPlease \n2) Make a normal request, using e.g. `giveMeDataObjectsWithCompletion` method on your stored SCPlease object\n3) only after 2) was done, ask for next/previous pages.\n\nIf you ever need to start from page 0 - just get the new SCPlease object using `please` method on your data class." } [/block] If you know you will need all the objects from your data class, or more than one page, but you don't know how many pages - you can use convenient method `enumaratePagesWithPredicate: parameters:`. It takes a predicate and/or parameters (both can be nil) and a block (closure). Block gives you an error (if one was set), array with objects from Syncano and a pointer to BOOL value - you should set it to true if you'd like to stop enumerating. [block:code] { "codes": [ { "code": "[[Book please] enumaratePagesWithPredicate:nil parameters:@{SCPleaseParameterPageSize : @(5)} withBlock:^(BOOL *stop, NSArray *objects, NSError *error) {\n if (error) {\n //handle error\n }\n for (Book *book in objects) {\n //do something with my books\n }\n // if you got all the results you wanted and don't need to get more pages - set *stop to true\n if (/*you have enough results == */ YES) {\n *stop = YES;\n } else {\n //it will keep enumarating and block will be called for next page of results\n }\n}];", "language": "objectivec" }, { "code": "Book.please().enumaratePagesWithPredicate(nil, parameters: [SCPleaseParameterPageSize : 5]) { stop, books, error in\n guard error == nil, let books = books as? [Book] else {\n //handle error\n return\n }\n for book in books {\n \t//do something with my books\n \tprint(\"Title: \\(book.title)\")\n }\n // if you got all the results you wanted and don't need to get more pages - set Bool 'stop' points to, to true\n if (/*you have enough results == */ true) {\n \tstop.memory = true;\n } else {\n \t//it will keep enumarating and block will be called for next page of results\n }\n}", "language": "objectivec", "name": "Swift" } ] } [/block] [block:callout] { "type": "info", "body": "If you need to enumerate all pages, starting from page 0, it's safe to use `[Book please]` or `Book.please()` directly (as in the example above).", "title": "Enumerate pages" } [/block] ### Field filtering Sometimes you may not need to display all the data inside your objects - e.g. your Book data class may contain info about author, number of pages, title, subtitle, publisher, year it was published, category, summary and much more. But your view shows only book title and you'd like to download only this one field - title - allowing for smaller data transfer thus faster download time. You can do that as well, using the following code: [block:code] { "codes": [ { "code": "- (void)downloadBookTitles {\n [Book.please giveMeDataObjectsWithParameters:@{ \n SCPleaseParameterFields : @[@\"title\"] \n } completion:^(NSArray *books, NSError *error) {\n //handle error\n }];\n}", "language": "objectivec" }, { "code": "func downloadBookTitles() {\n Book.please().giveMeDataObjectsWithParameters([SCPleaseParameterFields:[\"title\"]]) \n { books, error in\n //handle error\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] You can find list of all possible parameters in `SCPlease.h` file. Data Endpoints ===================== Data endpoints are very handy and iOS library support all their features - you can filter on them and have the relations between objects expanded. Let's start with modifying our base data classes: [block:code] { "codes": [ { "code": "@interface Author : SCDataObject\[email protected] (nonatomic,copy) NSString *name;\[email protected] (nonatomic,copy) NSString *country;\[email protected]\n\[email protected] Book : SCDataObject\[email protected] (nonatomic,copy) NSString *title;\[email protected] (nonatomic,copy) NSString *subtitle;\[email protected] (nonatomic,copy) SCFile *cover;\[email protected] (nonatomic,strong) Author *author;\[email protected]", "language": "objectivec" }, { "code": "class Author : SCDataObject {\n var name = \"\"\n var country = \"\"\n}\n\nclass Book : SCDataObject {\n var title = \"\"\n var subtitle = \"\"\n var cover: SCFile?\n var author: Author?\n}", "language": "swift" } ] } [/block] Our view name is **"books_with_authors"**. The following code fetches data from the view: [block:code] { "codes": [ { "code": "[Book registerClass];\n[Author registerClass];\n\n- (void)downloadBooksAndAuthors {\n [[Book pleaseForDataEndpoint:@\"books_with_authors\"] giveMeDataObjectsWithCompletion:^(NSArray *objects, NSError *error) {\n //error handling\n Book *book = objects.firstObject;\n //If your view is properly configured you can now access book.author.country\n }];\n}", "language": "objectivec" }, { "code": "Book.registerClass()\nAuthor.registerClass()\n\nfunc downloadBooksAndAuthors() {\tBook.pleaseForDataEndpoint(\"books_with_authors\").giveMeDataObjectsWithCompletion { books, error in\n //handle error\n let book = books.first\n //If your view is properly configured you can now access book.author.country\n }\n}", "language": "swift" } ] } [/block] You can easily filter on data endpoints: [block:code] { "codes": [ { "code": "- (void)downloadBooksWithNbOfPages:(NSNumber*)nbOfPages {\n [[Book pleaseForDataEndpoint:@\"books_with_authors\"] giveMeDataObjectsWithPredicate:[SCPredicate whereKey:@\"numofpages\" isEqualToNumber:nbOfPages] parameters:nil completion:^(NSArray *objects, NSError *error) {\n //error handling\n }];\n}", "language": "objectivec" }, { "code": "func downloadBooksWithNbOfPages(nbOfPages: NSNumber!) {\n\tBook.pleaseForDataEndpoint(\"books_with_authors\").giveMeDataObjectsWithPredicate(SCPredicate.whereKey(\"numofpages\", isEqualToNumber: nbOfPages), parameters: nil) { books, error in\n //handle error\n }\n}", "language": "swift" } ] } [/block] If you have a data class which represents Data Endpoint you can specify data endpoint name for this data class and type less: [block:code] { "codes": [ { "code": "@interface BooksView : Book\[email protected]\n\[email protected] BooksView\n+ (NSString *)dataEndpointNameForAPI {\n return @\"books_with_authors\";\n}\[email protected]\n \n- (void)downloadBooksAndAuthors {\n [[BooksView please] giveMeDataObjectsWithCompletion:^(NSArray *objects, NSError *error) {\n //error handling\n }];\n} ", "language": "objectivec" }, { "code": "class BooksView : Book {\n override class func dataEndpointNameForAPI() -> String! {\n return \"books_with_authors\"\n }\n}\n\nfunc downloadBooksAndAuthors() {\n\tBooksView.please().giveMeDataObjectsWithCompletion { books, error in\n //handle error\n //do something with downloaded objects\n }\n} ", "language": "swift" } ] } [/block] Delete Objects ===================== Add this following function to delete a passed Book object. [block:code] { "codes": [ { "code": "- (void)deleteBook:(Book *)book {\n [book deleteWithCompletion:^(NSError *error) {\n //handle error\n }];\n}", "language": "objectivec" }, { "code": "func deleteBook(book: Book) {\n book.deleteWithCompletion { error in\n //handle error\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] Now you can call this method whenever you want to delete a chosen objects. Download files ===================== File can be saved to memory, use this for smaller files: [block:code] { "codes": [ { "code": "- (void)downloadCoverForBook:(Book *)book {\n book.cover.storeDataAfterFetch = YES;//set to YES when you want to access data via: book.cover.data property\n [book.cover fetchInBackgroundWithCompletion:^(NSData *data, NSError *error) {\n //handle error\n }];\n}", "language": "objectivec" }, { "code": "func downloadCoverForBook(book: Book) {\n book.cover?.storeDataAfterFetch = true//set to true when you want to access data via: book.cover.data\n book.cover?.fetchInBackgroundWithCompletion { data, error in\n //handle error\n }\n}", "language": "swift" } ] } [/block] For bigger files or when it's useful, a file can be saved to disk (temporary location): [block:code] { "codes": [ { "code": "- (void)downloadCoverToDiskForBook:(Book *)book {\n [book.cover fetchToFileInBackgroundWithProgress:^(NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {\n //display progress NSLog(@\"%lld %lld %.0f\",totalBytesWritten,totalBytesExpectedToWrite,((float)(totalBytesWritten * 100) / totalBytesExpectedToWrite));\n } completion:^(NSURLResponse *response, NSURL *filePath, NSError *error) {\n //handle error\n NSData *data = [NSData dataWithContentsOfURL:filePath];\n //use data\n }];\n}", "language": "objectivec" }, { "code": "func downloadCoverToDiskForBook(book: Book) {\n book.cover?.fetchToFileInBackgroundWithProgress({ downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in\n //display progress\n }, completion: { response, filePath, error in\n //handle error\n let data = NSData(contentsOfURL: filePath)\n //use data\n })\n}", "language": "swift" } ] } [/block] A file can be also saved to documents: [block:code] { "codes": [ { "code": "- (void)downloadCoverToDocumentsForBook:(Book *)book {\n NSURL *storePathDocuments = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];\n NSURL *storePath = [storePathDocuments URLByAppendingPathComponent:@\"bookCover.png\"];\n \n [book.cover fetchToFileInBackground:storePath withProgress:^(NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {\n //display progress\n } completion:^(NSURLResponse *response, NSURL *filePath, NSError *error) {\n //handle error\n NSData *data = [NSData dataWithContentsOfURL:storePath];\n //use data\n }];\n}", "language": "objectivec" }, { "code": "func downloadCoverToDocumentsForBook(book: Book) throws {\n let storePathDocuments = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)\n let storePath = storePathDocuments.URLByAppendingPathComponent(\"bookCover.png\")\n \n book.cover?.fetchToFileInBackground(storePath, withProgress: { downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in\n //display progress\n }, completion: { response, filePath, error in\n //handle error\n let data = NSData(contentsOfURL: filePath)\n //use data\n })\n}", "language": "swift" } ] } [/block] Running Scripts ============================ Learn more about [Script](doc:snippets-scripts) and [Script Endpoints](doc:endpoints-scripts) in our Developer Manual. Here, you can see how to quickly run a Script or Script with endpoint. ###Scripts Run a script with a specified ID and get back its Trace. You can find out what is a Trace in the [Traces](doc:traces) chapter. [block:code] { "codes": [ { "code": "- (void)runScript:(NSInteger)codeboxID {\n [SCScript runScriptWithId:@(codeboxID) params:nil completion:^(SCTrace *trace, NSError *error) {\n //handle error\n }];\n}", "language": "objectivec" }, { "code": "func runScript(codeboxID: Int) {\n SCScript.runScriptWithId(codeboxID, params: nil) { trace, error in\n //handle error\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] ###Script Endpoints You can run a script endpoint by passing its name. You will receive the details of the execution in a response (`SCScriptEndpointResponse` class). [block:code] { "codes": [ { "code": "- (void)runScriptEndpoint:(NSString *)webhookName {\n [SCScriptEndpoint runScriptEndpointWithName:webhookName completion:^(SCWebhookResponseObject *response, NSError *error) {\n //handle error\n }];\n}", "language": "objectivec" }, { "code": "func runScriptEndpoint(name: String) {\n SCScriptEndpoint.runScriptEndpointWithName(name) { response, error in\n //handle error\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] In case your script endpoint uses your own (custom) response format, you can use the following code snippet: [block:code] { "codes": [ { "code": "- (void)runScriptEndpoint:(NSString *)webhookName {\n [SCScriptEndpoint runCustomScriptEndpointWithName:webhookName completion:^(id responseObject, NSError *error) {\n //handle error\n //responseObject is most likely an instance of NSData*\n NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:NULL];\n }];\n}", "language": "objectivec" }, { "code": "func runScriptEndpoint(name: String) {\n SCScriptEndpoint.runCustomScriptEndpointWithName(name) { response, error in\n //handle error\n do {\n let array = try NSJSONSerialization.JSONObjectWithData(response as! NSData!, options: []);\n } catch let jsonError {\n //handle parse error\n }\n }\n}", "language": "swift" } ] } [/block] Synchronization and Real-Time Channels ======================== You can read more about Real-Time Syncing and channels in the [Realtime communication](doc:realtime-communication) chapter, but here you can quickly learn how to listen for notifications sent to a given channel. First, add the channel object to your view controller and add information. For this you will implement SCChannelDelegate protocol. [block:code] { "codes": [ { "code": "@interface ViewController ()<SCChannelDelegate>\[email protected] (strong) Syncano *syncano;\[email protected] (strong) SCChannel *channel;\[email protected]", "language": "objectivec" }, { "code": "class SwiftViewController: SCChannelDelegate {\n let syncano = Syncano.sharedInstanceWithApiKey(\"API_KEY\", instanceName: \"INSTANCE_NAME\")\n var channel: SCChannel? = nil\n //rest of the class\n}", "language": "javascript", "name": "Swift" } ] } [/block] Initialize the channel object and start listening to incoming messages. In our example we will do it in the viewDidLoad method by adding the following lines there. [block:code] { "codes": [ { "code": "self.channel = [[SCChannel alloc] initWithName:@\"channel\" andDelegate:self];\n[self.channel subscribeToChannel];", "language": "objectivec" }, { "code": "self.channel = SCChannel(name: \"channel\", andDelegate: self)\nself.channel?.subscribeToChannel()", "language": "javascript", "name": "Swift" } ] } [/block] Implement the SCChannelDelegate protocol by pasting this function into your data class implementation (e.g. right above `viewDidLoad`). [block:code] { "codes": [ { "code": "- (void)chanellDidReceivedNotificationMessage:(SCChannelNotificationMessage *)notificationMessage {\n //handle notification\n}", "language": "objectivec" }, { "code": "func chanellDidReceivedNotificationMessage(notificationMessage: SCChannelNotificationMessage) {\n //handle notification\n}", "language": "javascript", "name": "Swift" } ] } [/block] That's it! From now on, whenever there's an incoming message, such as an object added/changed/deleted, the `chanellDidReceivedNotificationMessage` method will be called. Make sure to read [Realtime communication](doc:realtime-communication) chapter to learn more about how it works! Handling users ============ To find out more about the Users module on Syncano - read [User management](doc:user-management) chapter. Here, we'll quickly teach you how to create a new user, log the user in and get their profile data. ### Register new user [block:code] { "codes": [ { "code": "- (void)registerUser:(NSString *)username password:(NSString *)password {\n [SCUser registerWithUsername:username password:password completion:^(NSError *error) {\n //handle user registration\n }];\n}", "language": "objectivec" }, { "code": "func registerUser(username: String, password: String) {\n SCUser.registerWithUsername(username, password: password) { error in\n //handle user registration\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] ### Log in user with password [block:code] { "codes": [ { "code": "- (void)loginUser:(NSString *)username password:(NSString *)password {\n [SCUser loginWithUsername:username password:password completion:^(NSError *error) {\n //handle logged in user\n }];\n}", "language": "objectivec" }, { "code": "func loginUser(username: String, password: String) {\n SCUser.loginWithUsername(username, password: password) { error in\n //handle logged in user\n }\n}", "language": "javascript", "name": "Swift" } ] } [/block] ### Log in user with social backend [block:code] { "codes": [ { "code": "// Available backends:\n// SCSocialAuthenticationBackendFacebook\n// SCSocialAuthenticationBackendGoogle\n// SCSocialAuthenticationBackendLinkedIn\n// SCSocialAuthenticationBackendTwitter\n[SCUser loginWithSocialBackend:SCSocialAuthenticationBackendFacebook authToken:@\"FACEBOOK_AUTH_TOKEN\" completion:^(NSError *error) {\n\t//handle logged in user\n}];", "language": "objectivec" }, { "code": "// Available backends:\n// .Facebook\n// .Google\n// .LinkedIn\n// .Twitter\nSCUser.loginWithSocialBackend(.Facebook, authToken: \"FACEBOOK_AUTH_TOKEN\") { error in\n\t//handle logged in user\n}", "language": "objectivec", "name": "Swift" } ] } [/block] ### Get user profile First we need to grab a currently logged in user, next we can easily get their profile. [block:code] { "codes": [ { "code": "- (void)getCurrentUserProfile {\n SCUser *currentUser = [SCUser currentUser];\n SCUserProfile *profile = currentUser.profile;\n //display profile in your app\n}", "language": "objectivec" }, { "code": "func getCurrentUserProfile() {\n let currentUser = SCUser.currentUser()\n let profile = currentUser.profile\n //display profile in your app\n}", "language": "javascript", "name": "Swift" } ] } [/block] ### Subclassing user and user profile You can also subclass SCUser and SCUserProfile class - really handy when you want to expand user's profile and some extra properties. For more details on how that works, go to [User management](doc:user-management#user-profiles) and see below how to prepare your data classes on iOS. [block:code] { "codes": [ { "code": "//declare new User Profile class, by subclassing SCUserProfile\[email protected] MyUserProfile : SCUserProfile\[email protected] (strong) SCFile *avatar;\[email protected] (strong) NSString *first_name;\[email protected] (strong) NSString *last_name;\[email protected] (strong) NSString *email;\[email protected]\n \[email protected] MyUserProfile\n//implementation can be empty - no need to add anything\[email protected]\n\n//subclass SCUser to type of 'profile' property to MyUserProfile\[email protected] MyUser : SCUser\[email protected] (strong, nonatomic) MyUserProfile *profile;\[email protected]\n \[email protected] MyUser\n//we won't be implementing this property here - it's already implemented in superclass, so we will tell that to compiler by using '@dynamic' \[email protected] profile;\[email protected]\n \n//now, in your code, before you use for the first time SCUser class (or your subclass):\n[MyUser registerClassWithProfileClass:[MyUserProfile class]];\n\n//from now on you can keep using your own classes\n[MyUser loginWithUsername:@\"a\" password:@\"a\" completion:^(NSError *error) {\n MyUser *user = [MyUser currentUser];\n MyUserProfile *profile = user.profile;\n //work with user profile and user objects\n}];", "language": "objectivec" }, { "code": "//declare new User Profile class, by subclassing SCUserProfile\nclass MyUserProfile : SCUserProfile {\n var avatar : SCFile? = nil;\n var name = \"\"\n var first_name = \"\"\n var email = \"\"\n}\n\n//subclass SCUser to add new profile variable for the profile\n//Swift doesn't allow overring superclass' property type\n//(so we cannot 'profile' to be of MyUserProfile type)\n//but we can add a convenient setter and getter\nclass MyUser : SCUser {\n var myProfile : MyUserProfile? {\n get {\n return self.profile as? MyUserProfile\n }\n set {\n self.profile = newValue\n }\n }\n}\n\n//now, in your code, before you use for the first time SCUser class (or your subclass):\nMyUser.registerClassWithProfileClass(MyUserProfile.self)\n \n//from now on you can keep using your own classes\nMyUser.loginWithUsername(\"login\", password: \"password\") { error in\n let user = MyUser.currentUser()\n let profile = user.myProfile\n //work with user profile and user objects\n}", "language": "objectivec", "name": "Swift" } ] } [/block] Support ====== Now you’re ready to use Syncano in your iOS project. We really hope you will enjoy working with our platform. If you have any issues or suggestions, just let us know at [email protected] License ====== Syncano’s iOS Library (syncano-ios) is available under the MIT license. See the LICENSE file inside library sources for more info. ------------------------------------------------------------------------------------------------------------------ Swift Bridging Header ======================== If for any reason, you need to use syncano-ios in Swift, using with a bridging header, follow the steps below. **If you don't have the bridging header yet:** You can add a bridging header manually, but the easiest way is to use XCode: 1. Add a new **.m** file to your project, by choosing **File -> New -> File -> “Objective-C File”** (the one with **.m** icon) 2. Give it some random name e.g. “SyncanoSwift” (it’s not important, as we will not be using this file for anything later) 3. As for file type, you can leave the selection on **Empty File** and click **Next** 4. Save the file anywhere in the project, by choosing a directory and clicking **Create** 5. When XCode asks you “Would you like to configure an Objective-C bridging header?”, choose **Yes** 6. Now XCode should add a bridging file to your project named YourProjectName-Bridging-Header.h, so in our case it would be **SyncanoProject-Bridging-Header.h** 7. You can remove the previously created **.m** file. Choose it in your project, right click on it, choose **Delete** and **Move to Trash** 8. Add syncano-ios header to your bridging header as in `When you have the bridging header` section below **When you have the bridging header:** Choose the bridging header in XCode and paste this **#import** directive: [block:code] { "codes": [ { "code": "#import <Syncano.h>", "language": "objectivec" } ] } [/block] Because you already have library files included in the bridging header, there’s no need to import anything else in your `.swift` files.