Creating a New CC6 App
Whilst customizing one of the SDK reference apps can be an efficient approach to meeting many project requirements, the following topics provide information for developers choosing to create their own app.
Creating a New Platform-Specific Project
Implementing Core Functions
CC6 defines a number of core functions that need to be implemented as part of initial application setup. The required implementations include:
- CantaraExecutor - Inherits from CantaraExecutor in CC6 and has httpClient and context properties
- FunctionResource - Inherits from FunctionResource in CC6, and has a CantaraExecutor property
- QueryResource - Inherits from QueryResource in CC6 and has a CantaraExecutor property
- HttpClient - Inherits from HttpClient in CC6 and has CantaraApplication and Logger properties
- LoginResource - Inherits from UserResource in CC6 and has a CantaraExecutor Property
- UserStore- Inherits from DaoInMemory and UserDao in CC6, and has a function that finds the matching username in the Dao
- CantaraObjectFactory - The CantaraObjectFactory should create objects for all of the implementations that are needed for your application. It should also create a server model that creates an instance of a CantaraServer and initializes all of its properties.
Implementations are required for other objects including PurchaseOrders, WorkOrders, etc. Each of those will need Worker, Interactor, and ViewController implementations.
Implementing Managed Objects
You will need implementations for any Managed Object that you are using from the SDK. These should inherit from the Managed Objects in the client and satisfy all of the needed property requirements.
As Managed Objects keep strong references to each other, it is easy to create strong reference cycles and interacting directly with managed objects might therefore create memory issues. For this reason, an interface called "domain" is created for each managed object. This domain exists only to interface with the managed object, removing the need for direct interaction and reducing the risk of memory leaks.
Creating the managed object class
- In your project, create a new folder under Source/Schema and name it as per the entity you are targeting (eg. User).
- Create a new file called <EntityName>ManagedObject under this folder.
- The name of the managed object class should be <EntityName>ManagedObject (eg. UserManagedObject) and should match the name of the Cantara Client implementation.
The class should be final, inherit from the CCImplementation class and implement the ManagedObject and DomainConvertibleType protocols.
To find the managed object class you should inherit from, search the CCImplementation framework. The class should be located under Source/Common/Schema.
UserManagedObject Example
import CCCore
import CCImplementation
import Foundation
final class UserManagedObject: CCImplementation.UserManagedObject {
func asDomain() -> UserDomain {
let entity = UserDomain()
entity.uuid = uuid
entity.username = username
entity.token = token
entity.passwordHashed = passwordHashed
entity.branch = branch
entity.addressNoAlphaName = addressNoAlphaName
entity.addressNo = addressNo
entity.mediaAccessMethod = mediaAccessMethod
return entity
}
}
extension UserManagedObject: DomainConvertibleType {}
extension UserManagedObject: ManagedObject {}
Creating the domain class
- Create a new file called <EntityName>Domain under the same folder as the managed object
- The name of the domain class should be <EntityName>Domain (eg. UserDomain)
- The class should be final, inherit from the target entity CCSchema class (eg. CCSchema.User) and implement the CoreDataRepresentable protocol.
UserDomain Example
import CCCore
import CCSchema
final class UserDomain: CCSchema.User {
func update(entity:UserManagedObject) {
entity.uuid = uuid
entity.username = username
entity.token = token
entity.passwordHashed = passwordHashed
entity.branch = branch
entity.addressNoAlphaName = addressNoAlphaName
entity.addressNo = addressNo
entity.mediaAccessMethod = mediaAccessMethod
}
}
extension UserDomain: CoreDataRepresentable {}
Implementing Workers
Every entity should have a worker/dao/service set, where responsibilities can be defined as follow:
- The database access object (or dao) is a protocol whose responsibility is to define how one should perform CRUD operations on an entity.
The service is responsible for creating and executing Cantara requests and handling Cantara responses.
- The worker is responsible for coordinating the dao and the service.
There can also be some sets that don't have a corresponding entity, in which case there will only be a worker and a service but no dao. This can happen when there is logic linked to an aggregation of entities: a purchase order for example doesn't have a corresponding entity, it is an aggregation of a purchase header and of purchase lines. There is however logic that is specific to the purchase order and does not relate exclusively to the purchase header or the purchase line and this will warrant the creation of a worker and a service for the purchase order object.
Create a new folder under Source/Resource and name it as per the entity you are targeting, if the set you are creating is not linked to any entity, give the folder a meaningful name. The worker, dao and service files will be created here.
Creating the database access object (dao)
- Create a file called < EntityName >Dao in the previously created folder.
- It should inherit the Cantara Client implementation of the target entity’s dao (eg.UserDao).
- Its type should be the domain type for the entity (eg. UserDomain). See Implementing Managed Objects (above) if you haven't implemented the domain type for the target entity.
- On initialisation, it should retrieve the background context and pass it as argument to the super class initialisation.
UserDao Example
import CCImplementation
class UserDao: CCImplementation.UserDao<UserDomain> {
init() {
let backgroundContext = AppDelegate.shared.backgroundContext
super.init(context: backgroundContext)
}
}
Creating the service
- Create a file called <EntityName>Service in the same folder.
- It should inherit the Cantara Client implementation of the target entity’s service (eg. UserService).
- Its type should be the domain type for the entity (eg. UserDomain).
- On initialisation, it should initialise its super class with the required parameters.
UserService Example
import CCImplementation
class UserService: CCImplementation.UserService<UserDomain> {
init() {
super.init(executor: CantaraExecutor(),
queryService: QueryService(),
functionService: FunctionService())
}
}
Creating the worker
- Create a file called <EntityName>Worker in the same folder for the worker.
- It should inherit the Cantara Client implementation of the target entity’s worker (eg. UserWorker).
- Its type parameters should be the domain type, the service class created above and the dao class created above (eg. UserDomain, UserService, UserDao).
- On initialisation, it should initialise its super class with the application context, an instance of the created service and an instance of the created dao.
UserWorker Example
import CCCore
import CCImplementation
class UserWorker: CCImplementation.UserWorker<UserDomain, UserService, UserDao> {
init() {
super.init(context: CantaraApplication.shared,
service: UserService(),
dao: UserDao())
}
}
On This Page