Want to see how the rest of your backend is evolving? Check out the timeline.
Cartfile.resolved
UIWebView
was removed. (#370)DataStore.save()
(#359 #361)pendingSyncCount()
, pendingSyncEntities()
and clearSync()
to match with other platforms. Auto DataStore
tests and changes around it. (#356)Client
instance usage for aggregate functions on DataStore
(#357)DataStore.syncCount()
deprecated in favor of DataStore.pendingSyncCount()
(#356)Client.userChangedListener
public
so changes to the logged in user can be listened to (#352)DataStore
type .auto
that prioritizes network requests and if they fail for network reasons, returns local cache data without calling the callback twice (#337)DataStore
type .cache
is now deprecated in favor of .auto
clearCache()
to delete nested objects (#348)$not
queries (#339)GeoPoint
Codable Support (#334)ISO 8601
strings (#335)$and
queries (#336)XCGLogger
in favor of SwiftyBeaver
and OSLog
(#325)compactCacheOnLaunch
for the Client.initialize()
method with the default value true
(#327)Codable
support for Social Identities (#323)User.refresh(anotherUser: refreshCustomProperties:)
method in your User
subclass if you have non-objc properties in your subclass (#326)Codable
support for EmailVerification
and PasswordReset
metadata objects (#319)instanceId
and encrypted
parameters (#322)User.encode()
changed from public
to open
(#316)User.refresh()
should not update authentication tokens (#317)DataStore.pull()
calls (#318)List<T>
for Swift.Codable
entities (#310)ThreadSafeReference
usage during transactions (#313)DataStore<T>.deltaSet
is now deprecated in favor of DataStore<T>.options
Swift.Codable
in cache (#309)Error()
instead of fatalError()
everywhere to allow for recovery (#305)DataStore.removeAll()
(#304)ObjectMapper
is now deprecated in favor of Swift.Codable
Entity.CodingKeys
renamed to Entity.EntityCodingKeys
to avoid confusion when subclassing Entity
DataStore<MyEntity>.collection()
can now throw errors, so a try
is now required, for example: try DataStore<MyEntity>.collection()
Options()
constructor can now throw erros, so a try
is now required, for example: try Options(timeout: 120)
CustomEndpoint
calls now require you to implement the JSONDecodable
protocolKinvey.Error.blRuntimeError
to handle BL runtime errors (#301)Client.ping(completionHandler:)
Push.unRegisterDeviceToken(_:)
Client.initialize(appKey:appSecret:accessGroup:apiHostName:authHostName:encrypted:schema:completionHandler:)
Client.initialize(appKey:appSecret:accessGroup:apiHostName:authHostName:encryptionKey:schema:completionHandler:)
CustomEndpoint.CompletionHandler
CustomEndpoint.execute(_:params:client:completionHandler:)
DataStore.pull(_:deltaSetCompletionHandler:deltaSet:completionHandler:)
DataStore.purge(_:completionHandler:)
DataStore.push(timeout:completionHandler:)
DataStore.sync(_:deltaSetCompletionHandler:deltaSet:completionHandler:)
Entity.Key.acl
Entity.Key.entityId
Entity.Key.metadata
FileStore.download(_:storeType:ttl:completionHandler:)
FileStore.download(_:ttl:completionHandler:)
FileStore.find(_:ttl:completionHandler:)
FileStore.refresh(_:ttl:completionHandler:)
FileStore.remove(_:completionHandler:)
FileStore.upload(_:data:ttl:completionHandler:)
FileStore.upload(_:image:imageRepresentation:ttl:completionHandler:)
FileStore.upload(_:path:ttl:completionHandler:)
FileStore.upload(_:stream:ttl:completionHandler:)
Metadata.Key.authtoken
Metadata.Key.entityCreationTime
Metadata.Key.lastModifiedTime
User.PersistableUsernameKey
User.changePassword(newPassword:completionHandler:)
User.exists(username:client:completionHandler:)
User.forgotUsername(email:client:completionHandler:)
User.get(userId:client:completionHandler:)
User.login(authSource:_:createIfNotExists:authServiceId:client:completionHandler:)
User.lookup(_:completionHandler:)
User.resetPassword(usernameOrEmail:client:completionHandler:)
User.save(newPassword:completionHandler:)
User.sendEmailConfirmation(forUsername:client:completionHandler:)
User.signup(username:password:user:client:completionHandler:)
fields
is used in the query (#293)User.login(redirectURI:username:password:)
. Use User.login(username:password:provider:options:completionHandler:)
insteadX-Kinvey-Device-Info
is a new header that includes more information about the device (#282).warning
(#283)ValidationStrategy
work as intended (#285)DataStore.find()
requests not working in some situations (#288)scope
parameter to all MIC versions (#273)let person: Person = <#...#>
let notificationToken = person.observe { (objectChange: Kinvey.ObjectChange<Person>) in
switch objectChange {
case .change(let person):
print("person object changed")
case .deleted:
print("person object deleted")
case .error(let error):
print("\(error)")
}
}
let dataStore = DataStore<Person>.collection(.sync)
let notificationToken = dataStore.observe {
switch $0 {
case .initial(let results):
print("initial results")
case .update(let results, let deletions, let insertions, let modifications):
print("dataStore changed")
case .error(let error):
print("\(error)")
}
}
authServiceID
for MIC calls (#277)User
and Entity
now conforms to Hashable
and Equatable
(#279)DataStore.removeAll()
request was wrong (#274)Acl
for example (#278)User-Agent
now includes the Swift version (#270)instanceID
(#267)Kinvey.sharedClient.initialize(appKey: "myAppKey", appSecret: "myAppSecret", instanceId: "my-instance-id") {
switch $0 {
case .success(let user):
print("\(user?.userId ?? "nil")")
case .failure(let error):
print("\(error)")
}
}
DataStore<T>.find()
calls (#268)FileStore
now returns the proper result types for sync wait calls (#266)Client.timeout
is now a shortcut for Client.options.timeout
(#271)v3
(#269)Options
for Client
(#264)Thread.threadDictionary
to avoid memory leaks (#263)Error.entityNotFound
error when no entity was found. (#257)Realm 3.x
, ObjectMapper 3.x
and XCGLogger 6.x
.User.socialIdentity
was not being persisted. (#255)ValidationStrategy
. A ValidationStrategy
allows you to validate data from the backend before it is parsed into your client-side model. You can choose from predefined options: .all
, .randomSample(percentage:)
or define your own strategy with .custom(validationBlock: (Array<Dictionary<String, Any>>) -> Swift.Error?)
. By default, the SDK does not validate individual entities before they are parsed. Please see Reference Docs for details on using ValidationStrategy
. (#248)let dataStore = DataStore<Person>.collection(.network, validationStrategy: .all)
Date
objects for example (#250)FileStore.create()
creates a file entry in the server so the fileId
can be used to perform resumable uploads (#239)URLSession
for each call using Options
(#244)SFAuthenticationSession
for iOS 11, otherwise use SFSafariViewController
(#246)dynamic
not being used in the property declaration on Entity
subclasses (#243)DataStore.syncCount()
and DataStore.sync()
considering entities that are not the generic class specified on DataStore
(#247)_id
is required (#234)DataStore<T>.collection(autoPagination: true)
enables auto pagination, so no need to do it manually. This feature is disabled by default, so DataStore<T>.collection()
means DataStore<T>.collection(autoPagination: false)
(#231)User.logout()
is now async since it performs a network request to invalidate the token in the backend (#237)File.mapping()
was not open
(#233)save()
the cache implementation now performs deletes on nested objects (#229)~/Documents/<kinvey app key>
, but should be ~/Library/Application Support/<bundle id>/<kinvey app key>
(#236)clientId
parameter was renamed to authServiceId
in User.presentMICViewController()
, User.login()
and Options()
(#238)Kinvey.Error.unauthorized
also includes a debug
property. So now a switch
case
should look like case .unauthorized(let httpResponse, let data, let error, let debug, let description):
(#235)List<T>
(https://github.com/Kinvey/swift-sdk/pull/228)LiveStreamAcl
constructor missing (https://github.com/Kinvey/swift-sdk/pull/232)find()
, pull()
and sync()
that now has a Result<AnyRandomAccessCollection<T>, Swift.Error>
as the result for the completion handler.DataStore.count()
pull()
has now a new (optional) parameter called deltaSetCompletionHandler
which is a completion block that returns only the results that have changed during the delta set computation. The regular completionHandler
parameter will continue to return all the results (including the unchanged results)Options
is introduced to hold custom optional values such as client
, clientId
, ttl
, deltaSet
, readPolicy
, writePolicy
, timeout
, clientAppVersion
and customRequestProperties
. Maybe more to be added in the future.count()
were not translating queries when alias were usedDataStore
class that takes a Client
instance, ReadPolicy
, WritePolicy
, deltaSet
and timeout
values as optional parameters are now deprecated in favor of the Options
new struct.Client.clientAppVersion
and Client.customRequestProperties
were removed in favor of Client.options
which contains both clientAppVersion
and customRequestProperties
.User.refresh()
was losing authenticationQuery
objects with values containing the character +
were causing a wrong URL encodingFile
class and specifying when create your FileStore
instance, for example: let fileStore = FileStore<MyFileSubclass>()
Push.unRegisterDeviceToken()
now returns an error instead of crash if the device token was not foundclientId
parameter when called MIC.urlForLogin()
or User.presentMICViewController()
Query
using dates were being misconstructed when sent to the backendFileStore.getInstance()
in favor of the usage of constructors FileStore<File>()
PersistableIdKey
, PersistableAclKey
and PersistableMetadataKey
in favor of Entity.Key.entityId
, Entity.Key.acl
and Entity.Key.metadata
respectivelyMetadata.LmtKey
, Metadata.EctKey
and Metadata.AuthTokenKey
in favor of Metadata.Key.lastModifiedTime
, Metadata.Key.entityCreationTime
and Metadata.Key.authToken
respectivelyUser.socialIdentity
properties were not public
User
custom type was not properly loaded after a relaunchClient
class now have a new method ping()
to test if the client was initialized correctly.Kinvey.sharedClient.ping() { (result: Result<EnvironmentInfo, Swift.Error>) in
switch result {
case .success(let envInfo):
//succeed
case .failure(let error):
//failed
}
}
DataStore.group()
is a set of new methods that allows group / aggregation functions. You can write your own reduce function using JavaScript
or use pre-defined functions like count
, sum
, avg
(average), min
(minimum) and max
(maximum).dataStore.group(keys: ["country"], avg: "age") { (result: Result<[AggregationAvgResult<Person, Int>], Swift.Error>) in
switch result {
case .success(let array):
//succeed
case .failure(let error):
//failed
}
}
User.refresh()
will reload the user's data. Also applies for Client.activeUser
which will refresh and persist the new user's data.Kinvey.sharedClient.activeUser?.refresh() { result in
switch result {
case .success:
//succeed
case .failure(let error):
//failed
}
}
Improvement: a new pattern for all completionHandler
s in the library was introduced. All methods containing completionHandler
s has now overloaded versions which returns a Result<SuccessType, FailureType>
instance where you can use a switch
statement to figure if the call succeed (case .success(let successObject):
) or failed (case .failure(let error):
)
Bugfix: User.lookup()
now returns the correct User
type if a custom user type was set.
Breaking Change: User.destroy()
, User.sendEmailConfirmation()
, User.resetPassword()
and User.forgotUsername()
were changed to have the completionHandler
block following the Result
enumeration pattern described above.
user.sendEmailConfirmation() { result in
switch result {
case .success:
//succeed
case .failure:
//failed
}
}
DataStore.push()
and DataStore.sync()
Push.unRegisterDeviceToken()
Error
case for situations when there's some missing configuration in the console3.3.8
and 3.3.9
DataStore.removeById()
method is now deprecatedfileId
@objc
is used inside of an Entity
subclassFileStore
FileStore.download(_ file: inout File)
is now deprecated. Please use FileStore.download(_ file: File)
. The download
method does not require you to pass the file reference anymore.Acl
and Metadata
fixed for File
objectsGeoPoint
class to store geolocation coordinates (latitude, longitude)class Vehicle: Entity {
dynamic var name: String?
dynamic var geolocation: GeoPoint?
override class func collectionName() -> String {
return "Vehicle"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
name <- ("name", map["name"])
geolocation <- ("geolocation", map["geolocation"])
}
}
let deliveryTruck = Person()
deliveryTruck.name = "Package Delivery Truck"
deliveryTruck.geolocation = GeoPoint(latitude: 42.3133521, longitude: -71.1271963)
String
, Int
, Float
, Double
and Bool
import Kinvey
class Book: Entity {
dynamic var title: String?
let authorNames = List<StringValue>()
override class func collectionName() -> String {
return "Book"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
title <- ("title", map["title"])
authorNames <- ("authorNames", map["authorNames"])
}
}
let book = Book()
book.title = "Swift for the win!"
book.authorNames.append("Victor Barros")
Kinvey.Error
objects now conforms to LocalizedError
CustomEndpoint
now supports arrays and custom typesKinveyDateTransform
missing a public constructorUserMetadata
properties FileStore.getInstance()
if invoked before the creation of a DataStore
instanceKinveyDateTransform
missing a public constructorUserMetadata
properties without a public accesssuper.propertyMapping()
was not called inside of the propertyMapping()
method for a Entity
subclassPush
now supports the new Push API in iOS 10. A new method was added for apps to register for push: Push.registerForNotifications()
(all parameters optional). The previous method Push.registerForPush()
is now deprecated for iOS 10 and above.Kinvey.sharedClient.push.registerForNotifications { granted, error in
if granted {
//registered successfully
} else {
//failed to register
}
}
nil
after MIC login succeedsKinvey.Error
now implements CustomStringConvertible
and CustomDebugStringConvertible
which means you can now print()
and debugPrint()
errors. Also, errors now have the httpResponse
and data
properties, which allows you to check http status codes and the response body.User.resetPassword(email:)
and User.resetPassword(username:)
are now deprecated. Please use the class method User.resetPassword(usernameOrEmail:)
instead._ids
values are being sent to the backend.Content-Length
header was not present in the response.Kinvey.Error
now implements CustomStringConvertible
and CustomDebugStringConvertible
which means you can now print()
and debugPrint()
errors. Also errors now also have the httpResponse
and data
properties allowing you to check things like http status codes and the response body for example.client.userType = MyUser.self
let user = MyUser()
user.foo = "bar"
User.signup(user: user) { user, error in
if let user = user {
//success
} else {
//failure
}
}
User-Agent
X-Kinvey-Client-App-Version
X-Kinvey-Device-Information
X-Kinvey-Client-App-Version
request header.Kinvey.sharedClient.clientAppVersion = "1.0.0"
// Version 1
class Person: Entity {
dynamic var firstName: String?
dynamic var lastName: String?
override class func collectionName() -> String {
return "Person"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
firstName <- map["firstName"]
lastName <- map["lastName"]
}
}
// Version 2
class Person: Entity {
dynamic var fullName: String?
override class func collectionName() -> String {
return "Person"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
fullName <- map["fullName"]
}
}
// Migrating your data during client initialization
Kinvey.sharedClient.initialize(appKey: "<#appKey#>", appSecret: "<#appSecret#>", schemaVersion: 2) { migration, oldSchemaVersion in
migration.execute(Person.self) { (oldEntity) in
var newEntity = oldEntity
if oldSchemaVersion < 2 {
newEntity["fullName"] = "\(oldEntity["firstName"]!) \(oldEntity["lastName"]!)"
newEntity.removeValue(forKey: "firstName")
newEntity.removeValue(forKey: "lastName")
}
return newEntity
}
}
User.presentMICViewController()
now uses SFSafariViewController
as the default option to login using MIC. To explore other options add the parameter micUserInterface
using one of the options in the MICUserInterface
enum.User.presentMICViewController(redirectURI: URL(string: "<#myRedirectURL://#>")!) { (user, error) -> Void in
if let user = user {
self.userIdLabel.text = user.userId
}
self.completionHandler?(user, error)
}
If you are using the default .safari
option for the micUserInterface
, remember to add the code below in your app delegate.
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if User.login(redirectURI: URL(string: "<#myRedirectURL://#>")!, micURL: url) {
return true
}
return false
}
User.presentMICViewController()
now uses SFSafariViewController
as the default option to login using MIC. To explore other options add the parameter micUserInterface
using one of the options in the MICUserInterface
enum.User.presentMICViewController(redirectURI: NSURL(string: "<#myRedirectURL://#>")!) { (user, error) -> Void in
if let user = user {
self.userIdLabel.text = user.userId
}
self.completionHandler?(user, error)
}
If you are using the default .Safari
option for the micUserInterface
, remember to add the code below in your app delegate.
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
if User.login(redirectURI: NSURL(string: "<#myRedirectURL://#>")!, micURL: url) {
return true
}
return false
}
This version of the SDK requires Swift 3 and XCode 8 (or above).
Known issue: several Kinvey app developers have reported seeing a Keychain issue when the app logs in a user on iOS 10 Simulator. The error message looks like the following: Terminating app due to uncaught exception 'KinveyException', reason: 'Could not write token to keychain. Err (null) (-34018)'
This is a known issue with the iOS 10 simulator. To work around it, you can share the keychain.
Query
objects not working properlyDataStore
operations like find()
and pull()
can now report progress of the requestlet request = store.find() { (events, error) in
//completion handler
}
request.progress = {
//progress handler
print("Progress: \($0.countOfBytesReceived)/\($0.countOfBytesExpectedToReceive)")
}
DataStore.count()
returns the total number of records in a colletion.let store = DataStore<Event>.collection(.Network)
store.count { (count, error) in
//completion handler
}
let query = Query {
$0.skip = 10
$0.limit = 50
}
store.find(query) {
//it will get max of 50 results starting from the 11th record
}
User.loginWithAuthorization(redirectURI: "myRedirectURI://", username: "myUsername", password: "myPassword") { user, error in
if (user != nil) {
//logged in successfully
} else if (error != nil) {
//something went wrong if the error object is not nil
} else {
//should never happen!
}
}
find()
operations returning wrong results for StoreType.Network
User.forgotUsername(email: "your@email.com") { error in
}
let userQuery = UserQuery {
$0.username = username
}
user.lookup(userQuery) { users, error in
}
let file: File = ...
fileStore.download(file) { (file, url: NSURL?, error) in
//file is now cached
}
With an optional parameter to not cache the downloaded files:
let file: File = ...
fileStore.download(file, storeType: .Network) { (file, url: NSURL?, error) in
//file is not cached
}
client.micApiVersion = "v2" //v1 is the default value
DataStore
you now have to call the collection()
method instead of getInstance()
let user: User = ...
user.changePassword(newPassword: "myNewPassword") { user, error in
}
let user: User = ...
user.sendEmailConfirmation { error in
}
User.login(authSource: .Facebook, facebookAuthDictionary) { user, error in
}
push()
, pull()
, sync()
and purge()
operations on DataStoreType.Cache
DataStore.syncCount()
method addedClient.logNetworkEnabled
enables the ability of log network requests and responses. Default value is false
Kinvey.sharedClient.logNetworkEnabled = true //it will start log network requests and responses
DataStore<MyCollection>.getInstance(.Network, tag: "MyCustomDataContainer")
Acl
now includes readers and writers permissions.Error
now includes a localized description error. Available only in English for now.FileStore
now supports resumable uploads and downloads.DataStore<MyCollection>.getInstance(.Network, deltaSet: true)
push()
methodPersistable
objects using fromJson()
and toJson()
methodslet store = DataStore<MyPersistableClass>.getInstance(filePath: customPath)
The beta version of the iOS 3.0 SDK is now available!
New in this release:
Full support for data synchronization between your app and backend. Refer to the Data Store Guide to learn how to use the new data manipulation APIs.
We've simplified data stores and caching policies, making it easier for you to start building apps with caching and offline built in.
The Kinvey framework is now a published as a module.
Code samples and API docs for Objective-C are coming soon.
We recommend using the latest version.
Version | Download | Date |
---|---|---|
6.0.0 | Download | Feb 17, 2022 |
6.0.0-rc.1 | Download | Sep 30, 2021 |
5.0.1 | Download | Nov 3, 2020 |
5.0.0 | Download | Jul 30, 2020 |
4.1.2 | Download | Apr 7, 2020 |
4.1.1 | Download | Oct 31, 2019 |
4.1.0 | Download | Oct 2, 2019 |
4.0.0 | Download | Sep 11, 2019 |
3.27.0 | Download | Aug 23, 2019 |
3.26.1 | Download | Aug 23, 2019 |
3.26.0 | Download | Jul 11, 2019 |
3.25.1 | Download | Jul 1, 2019 |
3.25.0 | Download | May 28, 2019 |
3.24.0 | Download | Apr 29, 2019 |
3.23.0 | Download | Apr 12, 2019 |
3.22.0 | Download | Feb 27, 2019 |
3.21.3 | Download | Feb 12, 2019 |
3.21.2 | Download | Jan 31, 2019 |
3.21.1 | Download | Jan 15, 2019 |
3.21.0 | Download | Dec 20, 2018 |
3.20.4 | Download | Nov 15, 2018 |
3.20.3 | Download | Nov 1, 2018 |
3.20.2 | Download | Oct 26, 2018 |
3.20.1 | Download | Oct 18, 2018 |
3.20.0 | Download | Oct 3, 2018 |
3.19.0 | Download | Sep 21, 2018 |
3.18.5 | Download | Sep 20, 2018 |
3.18.4 | Download | Sep 6, 2018 |
3.18.3 | Download | Aug 24, 2018 |
3.18.2 | Download | Jul 31, 2018 |
3.18.1 | Download | Jul 23, 2018 |
3.18.0 | Download | Jul 12, 2018 |
3.17.1 | Download | Jun 26, 2018 |
3.17.0 | Download | Jun 14, 2018 |
3.16.0 | Download | May 31, 2018 |
3.15.0 | Download | May 15, 2018 |
3.14.0 | Download | Apr 13, 2018 |
3.13.0 | Download | Mar 15, 2018 |
3.12.2 | Download | Feb 12, 2018 |
3.12.1 | Download | Jan 24, 2018 |
3.12.0 | Download | Jan 12, 2018 |
3.11.0 | Download | Nov 21, 2017 |
3.10.1 | Download | Nov 8, 2017 |
3.10.0 | Download | Oct 25, 2017 |
3.9.1 | Download | Oct 18, 2017 |
3.9.0 | Download | Oct 3, 2017 |
3.8.0 | Download | Sep 13, 2017 |
3.7.1 | Download | Aug 26, 2017 |
3.7.0 | Download | Aug 18, 2017 |
3.6.1 | Download | Jul 20, 2017 |
3.6.0 | Download | Jul 17, 2017 |
3.5.4 | Download | Jun 23, 2017 |
3.5.3 | Download | Jun 12, 2017 |
3.5.2 | Download | Jun 5, 2017 |
3.5.1 | Download | May 2, 2017 |
3.5.0 | Download | Apr 21, 2017 |
3.4.0 | Download | Apr 3, 2017 |
3.3.9 | Download | Mar 10, 2017 |
3.3.8 | Download | Feb 14, 2017 |
3.3.7 | Download | Jan 31, 2017 |
3.3.6 | Download | Jan 11, 2017 |
3.2.6 | Download | Jan 11, 2017 |
3.3.5 | Download | Dec 19, 2016 |
3.2.5 | Download | Dec 19, 2016 |
3.3.4 | Download | Dec 8, 2016 |
3.2.4 | Download | Dec 8, 2016 |
3.3.3 | Download | Nov 21, 2016 |
3.2.3 | Download | Nov 21, 2016 |
3.3.2 | Download | Oct 28, 2016 |
3.2.2 | Download | Oct 28, 2016 |
3.3.1 | Download | Oct 20, 2016 |
3.2.1 | Download | Oct 21, 2016 |
3.3.0 | Download | Sep 15, 2016 |
3.2.0 | Download | Sep 14, 2016 |
3.1.0 | Download | Sep 12, 2016 |
3.0.25 | Download | Aug 25, 2016 |
3.0.24 | Download | Aug 12, 2016 |
3.0.23 | Download | Jul 18, 2016 |
3.0.22 | Download | Jul 15, 2016 |
3.0.21 Beta | Download | Jun 21, 2016 |
3.0.20 Beta | Download | May 24, 2016 |
3.0.19 Beta | Download | May 20, 2016 |
3.0.18 Beta | Download | May 19, 2016 |
3.0.17 Beta | Download | Apr 28, 2016 |
3.0.16 Beta | Download | Apr 27, 2016 |
3.0.15 Beta | Download | Apr 21, 2016 |
3.0.14 Beta | Download | Apr 18, 2016 |
3.0.13 Beta | Download | Mar 22, 2016 |
3.0.11 Beta | Download | Mar 21, 2016 |
3.0.7 Beta | Download | Mar 2, 2016 |
3.0 Beta | Download | Feb 25, 2016 |