@maccesar/titools 2.2.10 → 2.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/ti-expert/SKILL.md +60 -85
- package/skills/ti-expert/references/alloy-structure.md +60 -25
- package/skills/ti-expert/references/architecture-tiers.md +248 -0
- package/skills/ti-guides/references/app-distribution.md +173 -0
- package/skills/ti-guides/references/cli-reference.md +43 -1
package/package.json
CHANGED
|
@@ -31,78 +31,46 @@ Behavior:
|
|
|
31
31
|
|
|
32
32
|
## Workflow
|
|
33
33
|
|
|
34
|
-
1. Architecture: define structure (`lib/api`, `lib/services`, `lib/helpers`)
|
|
34
|
+
1. Architecture: define structure by technical type with flat folders (`lib/api`, `lib/services`, `lib/actions`, `lib/repositories`, `lib/helpers`)
|
|
35
35
|
2. Data strategy: choose Models (SQLite) or Collections (API)
|
|
36
36
|
3. Contracts: define I/O specs between layers
|
|
37
37
|
4. Implementation: write XML views and ES6+ controllers
|
|
38
38
|
5. Quality: testing, error handling, logging, performance
|
|
39
39
|
6. Cleanup: implement a `cleanup()` pattern for memory management
|
|
40
40
|
|
|
41
|
-
##
|
|
42
|
-
|
|
43
|
-
Minimal example that matches the conventions:
|
|
44
|
-
|
|
45
|
-
View (`views/user/card.xml`)
|
|
46
|
-
```xml
|
|
47
|
-
<Alloy>
|
|
48
|
-
<View id="cardContainer">
|
|
49
|
-
<View id="headerRow">
|
|
50
|
-
<ImageView id="userIcon" image="/images/user.png" />
|
|
51
|
-
<Label id="name" />
|
|
52
|
-
</View>
|
|
53
|
-
<Button id="viewProfileBtn"
|
|
54
|
-
title="L('view_profile')"
|
|
55
|
-
onClick="onViewProfile"
|
|
56
|
-
/>
|
|
57
|
-
</View>
|
|
58
|
-
</Alloy>
|
|
59
|
-
```
|
|
41
|
+
## Architectural Maturity Tiers
|
|
60
42
|
|
|
61
|
-
|
|
62
|
-
```tss
|
|
63
|
-
"#cardContainer": { left: 8, right: 8, top: 8, height: Ti.UI.SIZE, borderRadius: 12, backgroundColor: '#fff' }
|
|
64
|
-
"#headerRow": { layout: 'horizontal', left: 12, right: 12, top: 12, height: Ti.UI.SIZE, width: Ti.UI.FILL }
|
|
65
|
-
"#userIcon": { width: 32, height: 32 }
|
|
66
|
-
"#name": { left: 12, font: { fontSize: 18, fontWeight: 'bold' } }
|
|
67
|
-
"#viewProfileBtn": { left: 12, right: 12, bottom: 12, height: 40, width: Ti.UI.FILL, borderRadius: 6, backgroundColor: '#2563eb', color: '#fff' }
|
|
68
|
-
```
|
|
43
|
+
Choose the appropriate tier based on project complexity:
|
|
69
44
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
$.name.text = user.name
|
|
77
|
-
}
|
|
45
|
+
### Tier 1: Basic (Rapid Prototyping)
|
|
46
|
+
- **Best for**: Simple utility apps or developers transitioning from Classic.
|
|
47
|
+
- **Structure**: Logic resides directly within `index.js`.
|
|
48
|
+
- **UI Access**: Direct usage of the `$` object throughout the file.
|
|
49
|
+
- **Pros**: Zero boilerplate, extremely fast start.
|
|
50
|
+
- **Cons**: Unmaintainable beyond 500 lines of code.
|
|
78
51
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
52
|
+
### Tier 2: Intermediate (Modular Alloy)
|
|
53
|
+
- **Best for**: Standard commercial applications.
|
|
54
|
+
- **Structure**: Business logic extracted to `app/lib/` using a flat technical-type organization.
|
|
55
|
+
- **Pattern**: Slim Controllers that `require()` services.
|
|
56
|
+
- **Memory**: Mandatory implementation of `$.cleanup = cleanup`.
|
|
82
57
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
58
|
+
### Tier 3: Advanced / Enterprise (Service-Oriented)
|
|
59
|
+
- **Best for**: High-complexity apps (IDEs like TiDesigner, multi-state platforms).
|
|
60
|
+
- **Architecture**: Dependency Injection via a `ServiceRegistry`.
|
|
61
|
+
- **ID Scoping**: Services do not receive the entire `$` object. They receive a "Scoped UI" object containing only relevant IDs.
|
|
62
|
+
- **Cognitive Load**: "Black Box" logic—encapsulated units that reduce mental fatigue.
|
|
63
|
+
- **Observability**: Structured logging with a mandatory `service` context.
|
|
86
64
|
|
|
87
|
-
|
|
88
|
-
```
|
|
65
|
+
Detailed examples and full implementation samples are available in: [Architectural Tiers Detail](references/architecture-tiers.md)
|
|
89
66
|
|
|
90
|
-
|
|
91
|
-
```javascript
|
|
92
|
-
exports.Navigation = {
|
|
93
|
-
open(route, params = {}) {
|
|
94
|
-
const controller = Alloy.createController(route, params)
|
|
95
|
-
const view = controller.getView()
|
|
67
|
+
## Organization policy (low freedom)
|
|
96
68
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
69
|
+
- Use technical-type organization in `lib` (for example: `api`, `services`, `actions`, `repositories`, `helpers`, `policies`, `providers`).
|
|
70
|
+
- Keep `lib` flat and predictable: `lib/<type>/<file>.js` only.
|
|
71
|
+
- Do not recommend deep nesting like `lib/services/auth/session/login.js`.
|
|
72
|
+
- Keep UI layers aligned by screen (`controllers/`, `views/`, `styles/`) and avoid unnecessary depth.
|
|
100
73
|
|
|
101
|
-
view.open()
|
|
102
|
-
return controller
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
74
|
|
|
107
75
|
## Code standards (low freedom)
|
|
108
76
|
|
|
@@ -159,40 +127,47 @@ For the complete reference with examples, see [Alloy builtins and globals](refer
|
|
|
159
127
|
|
|
160
128
|
| Question | Answer |
|
|
161
129
|
| ---------------------------------- | -------------------------------------------------------------- |
|
|
162
|
-
| How to create a new Alloy project? | `ti create -t app --alloy` (not `--classic` + `alloy new`)
|
|
130
|
+
| How to create a new Alloy project? | `ti create -t app --alloy` (not `--classic` + `alloy new`) |
|
|
163
131
|
| Fastest way to build? | `tn <recipe>` (using TiNy CLI wrapper) |
|
|
132
|
+
| Controller > 100 lines? | Extract to Tier 2 (Services) |
|
|
133
|
+
| More than 50 IDs in XML? | Use Tier 3 (ID Scoping) |
|
|
164
134
|
| Where does API call go? | `lib/api/` |
|
|
165
135
|
| Where does business logic go? | `lib/services/` |
|
|
136
|
+
| How deep should `lib` folders be? | One level: `lib/<type>/<file>.js` |
|
|
166
137
|
| Where do I store auth tokens? | Keychain (iOS) / KeyStore (Android) via service |
|
|
167
138
|
| Models or Collections? | Collections for API data, Models for SQLite persistence |
|
|
168
139
|
| Ti.App.fireEvent or EventBus? | Always EventBus (Backbone.Events) |
|
|
169
140
|
| Direct navigation or service? | Always Navigation service (auto cleanup) |
|
|
170
|
-
| Inline styles or TSS files? | Always TSS files (per-controller + `app.tss` for global)
|
|
171
|
-
| Controller 100+ lines? | Extract logic to services |
|
|
141
|
+
| Inline styles or TSS files? | Always TSS files (per-controller + `app.tss` for global) |
|
|
172
142
|
|
|
173
143
|
## Reference guides (progressive disclosure)
|
|
174
144
|
|
|
175
|
-
Architecture
|
|
176
|
-
- [
|
|
177
|
-
- [
|
|
178
|
-
- [
|
|
179
|
-
- [
|
|
180
|
-
- [
|
|
181
|
-
- [
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
- [Code
|
|
186
|
-
- [
|
|
187
|
-
- [
|
|
188
|
-
- [
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
- [
|
|
193
|
-
- [
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
- [
|
|
197
|
-
|
|
198
|
-
|
|
145
|
+
### Architecture & Patterns
|
|
146
|
+
- [Architectural Tiers Detail](references/architecture-tiers.md)
|
|
147
|
+
- [Architectural Patterns](references/patterns.md) (Factory, Singleton, Repository)
|
|
148
|
+
- [Structure & Organization](references/alloy-structure.md)
|
|
149
|
+
- [Contracts & Communication](references/contracts.md)
|
|
150
|
+
- [State Management](references/state-management.md)
|
|
151
|
+
- [Anti-patterns to Avoid](references/anti-patterns.md)
|
|
152
|
+
|
|
153
|
+
### Implementation & API
|
|
154
|
+
- [Alloy Builtins & Globals](references/alloy-builtins.md)
|
|
155
|
+
- [Code Conventions](references/code-conventions.md)
|
|
156
|
+
- [Controller Patterns](references/controller-patterns.md)
|
|
157
|
+
- [Theming & Dark Mode](references/theming.md)
|
|
158
|
+
- [Migration Patterns](references/migration-patterns.md)
|
|
159
|
+
- [Examples Collection](references/examples.md)
|
|
160
|
+
|
|
161
|
+
### Quality & Performance
|
|
162
|
+
- [Performance Optimization](references/performance-optimization.md)
|
|
163
|
+
- [ListView & ScrollView Performance](references/performance-listview.md)
|
|
164
|
+
- [Error Handling & Logging](references/error-handling.md)
|
|
165
|
+
- [Unit & Integration Testing](references/testing-unit.md)
|
|
166
|
+
- [E2E Testing & CI/CD](references/testing-e2e-ci.md)
|
|
167
|
+
|
|
168
|
+
### Security
|
|
169
|
+
- [Security Fundamentals](references/security-fundamentals.md)
|
|
170
|
+
- [Device Security](references/security-device.md)
|
|
171
|
+
|
|
172
|
+
### Tools
|
|
173
|
+
- [CLI Expert & TiNy](references/cli-expert.md)
|
|
@@ -4,52 +4,81 @@
|
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
app/
|
|
7
|
-
├── controllers/
|
|
7
|
+
├── controllers/ # View orchestrators
|
|
8
8
|
│ ├── index.js # Bootstrap only (no business logic)
|
|
9
|
-
│
|
|
10
|
-
│
|
|
9
|
+
│ ├── home.js
|
|
10
|
+
│ └── userProfile.js
|
|
11
11
|
├── models/ # OPTIONAL: For persistence with migrations
|
|
12
12
|
│ └── user.js # Model definition (ONLY if using SQLite)
|
|
13
13
|
├── views/ # XML views
|
|
14
14
|
│ ├── index.xml
|
|
15
|
-
│
|
|
16
|
-
│
|
|
15
|
+
│ ├── home.xml
|
|
16
|
+
│ └── userProfile.xml
|
|
17
17
|
├── styles/ # TSS styles (one per view + global)
|
|
18
18
|
│ ├── app.tss # Global application styles
|
|
19
19
|
│ ├── index.tss # Styles for index view
|
|
20
|
-
│
|
|
21
|
-
│
|
|
20
|
+
│ ├── home.tss
|
|
21
|
+
│ └── userProfile.tss
|
|
22
22
|
├── lib/ # Reusable logic (no UI)
|
|
23
23
|
│ ├── api/
|
|
24
|
-
│ │
|
|
24
|
+
│ │ ├── authApi.js
|
|
25
|
+
│ │ ├── userApi.js
|
|
26
|
+
│ │ └── frameApi.js
|
|
25
27
|
│ ├── services/
|
|
26
|
-
│ │ ├──
|
|
27
|
-
│ │ ├──
|
|
28
|
-
│ │ └──
|
|
29
|
-
│
|
|
30
|
-
│
|
|
31
|
-
│
|
|
28
|
+
│ │ ├── authService.js
|
|
29
|
+
│ │ ├── navigationService.js
|
|
30
|
+
│ │ └── notificationService.js
|
|
31
|
+
│ ├── actions/
|
|
32
|
+
│ │ ├── syncUserAction.js
|
|
33
|
+
│ │ └── refreshSessionAction.js
|
|
34
|
+
│ ├── repositories/
|
|
35
|
+
│ │ ├── userRepository.js
|
|
36
|
+
│ │ └── settingsRepository.js
|
|
37
|
+
│ ├── helpers/
|
|
38
|
+
│ │ ├── validator.js
|
|
39
|
+
│ │ ├── formatter.js
|
|
40
|
+
│ │ └── dateHelper.js
|
|
41
|
+
│ ├── policies/
|
|
42
|
+
│ │ ├── permissionPolicy.js
|
|
43
|
+
│ │ └── featurePolicy.js
|
|
44
|
+
│ └── providers/
|
|
45
|
+
│ ├── containerProvider.js
|
|
46
|
+
│ └── loggerProvider.js
|
|
32
47
|
├── widgets/ # Truly reusable components (used in 3+ places)
|
|
33
48
|
│ └── customButton/
|
|
34
49
|
├── config.json # Alloy configuration
|
|
35
50
|
└── alloy.js # Collections & Global services
|
|
36
51
|
```
|
|
37
52
|
|
|
53
|
+
## Organization strategy
|
|
54
|
+
|
|
55
|
+
- `lib/` uses technical-type grouping (Laravel-style naming adapted to Titanium).
|
|
56
|
+
- UI stays in Alloy MVC folders (`controllers`, `views`, `styles`).
|
|
57
|
+
- This is a hybrid approach: technical grouping for reusable logic + screen-based organization for UI files.
|
|
58
|
+
- Keep folder depth low to preserve discoverability.
|
|
59
|
+
- Use clear composed names (`authService.js`, `userRepository.js`, `authApi.js`) and keep multiple files per folder as the normal case.
|
|
60
|
+
|
|
61
|
+
### Folder depth policy (critical)
|
|
62
|
+
|
|
63
|
+
- Allowed in `lib`: `lib/<type>/<file>.js`
|
|
64
|
+
- Avoid in `lib`: `lib/<type>/<domain>/<subdomain>/<file>.js`
|
|
65
|
+
- If a folder grows too much, split by new technical type, not deep tree nesting.
|
|
66
|
+
|
|
38
67
|
## lib/ folder and module require paths
|
|
39
68
|
|
|
40
69
|
:::danger CRITICAL: lib/ Folder is FLATTENED During Build
|
|
41
70
|
When Alloy compiles, the **entire `lib/` folder is flattened to the root of Resources**. This means:
|
|
42
|
-
- `app/lib/services/
|
|
43
|
-
- `app/lib/api/
|
|
71
|
+
- `app/lib/services/authService.js` → `Resources/iphone/services/authService.js`
|
|
72
|
+
- `app/lib/api/authApi.js` → `Resources/iphone/api/authApi.js`
|
|
44
73
|
|
|
45
74
|
**Therefore, require statements should NOT include `lib/` prefix:**
|
|
46
75
|
```javascript
|
|
47
76
|
// ❌ WRONG - Will fail at runtime
|
|
48
|
-
const
|
|
77
|
+
const authApi = require('lib/api/authApi')
|
|
49
78
|
|
|
50
79
|
// ✅ CORRECT - Path relative to flattened lib/
|
|
51
|
-
const
|
|
52
|
-
const
|
|
80
|
+
const authApi = require('api/authApi')
|
|
81
|
+
const authService = require('services/authService')
|
|
53
82
|
```
|
|
54
83
|
|
|
55
84
|
**This applies to:**
|
|
@@ -62,13 +91,18 @@ const picsum = require('services/picsum')
|
|
|
62
91
|
app/
|
|
63
92
|
├── lib/
|
|
64
93
|
│ ├── services/
|
|
65
|
-
│ │ ├──
|
|
66
|
-
│ │ ├──
|
|
67
|
-
│ │ └──
|
|
68
|
-
│
|
|
69
|
-
│
|
|
94
|
+
│ │ ├── authService.js # require('services/navigationService')
|
|
95
|
+
│ │ ├── navigationService.js # require('services/notificationService')
|
|
96
|
+
│ │ └── notificationService.js
|
|
97
|
+
│ ├── api/
|
|
98
|
+
│ │ ├── authApi.js # require('services/authService')
|
|
99
|
+
│ │ ├── userApi.js
|
|
100
|
+
│ │ └── frameApi.js
|
|
101
|
+
│ └── repositories/
|
|
102
|
+
│ ├── userRepository.js
|
|
103
|
+
│ └── settingsRepository.js
|
|
70
104
|
├── controllers/
|
|
71
|
-
│ └── index.js
|
|
105
|
+
│ └── index.js # require('services/authService')
|
|
72
106
|
```
|
|
73
107
|
:::
|
|
74
108
|
|
|
@@ -145,6 +179,7 @@ api.getFrames()
|
|
|
145
179
|
- Handle UI events and delegate to services.
|
|
146
180
|
- Format data for display (simple cases).
|
|
147
181
|
- Manage view lifecycle (including cleanup).
|
|
182
|
+
- Keep `lib` modules flat and easy to locate.
|
|
148
183
|
|
|
149
184
|
**DON'T:**
|
|
150
185
|
- Use inline style attributes in XML (define in TSS files).
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Titanium Architectural Maturity Tiers
|
|
2
|
+
|
|
3
|
+
To ensure scalability and maintainability, Titanium projects should follow one of these three tiers based on their complexity.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Tier 1 — Basic (Rapid Prototyping)
|
|
8
|
+
|
|
9
|
+
**Best for:** Utilities, internal tools, proof of concept, learning Alloy.
|
|
10
|
+
|
|
11
|
+
### Characteristics
|
|
12
|
+
|
|
13
|
+
* Logic directly in `index.js`
|
|
14
|
+
* Direct `$` access everywhere
|
|
15
|
+
* No separation of concerns
|
|
16
|
+
* No cleanup strategy
|
|
17
|
+
|
|
18
|
+
### When to upgrade?
|
|
19
|
+
|
|
20
|
+
* Controller > 100–150 lines
|
|
21
|
+
* Logic duplicated in multiple controllers
|
|
22
|
+
* UI starts feeling coupled
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Example (Tier 1)
|
|
27
|
+
|
|
28
|
+
### Controller (`controllers/index.js`)
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
function onButtonClick() {
|
|
32
|
+
$.label.text = "Clicked!"
|
|
33
|
+
Ti.API.info("Button was clicked")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
$.index.open()
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Simple. Fast. Dangerous beyond small apps.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
# Tier 2 — Intermediate (Modular Alloy)
|
|
44
|
+
|
|
45
|
+
**Best for:** Standard commercial applications.
|
|
46
|
+
|
|
47
|
+
### Characteristics
|
|
48
|
+
|
|
49
|
+
* Business logic extracted to `app/lib/`
|
|
50
|
+
* Slim controllers
|
|
51
|
+
* Flat organization (`lib/services`, `lib/api`)
|
|
52
|
+
* Mandatory `cleanup`
|
|
53
|
+
* Reusable UI components
|
|
54
|
+
|
|
55
|
+
### Why this tier exists
|
|
56
|
+
|
|
57
|
+
This tier separates UI from logic, but still allows services to reference global dependencies.
|
|
58
|
+
|
|
59
|
+
It’s modular — but not yet decoupled.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Example (Tier 2)
|
|
64
|
+
|
|
65
|
+
### View (`views/userCard.xml`)
|
|
66
|
+
|
|
67
|
+
```xml
|
|
68
|
+
<Alloy>
|
|
69
|
+
<View id="cardContainer">
|
|
70
|
+
<View id="headerRow">
|
|
71
|
+
<ImageView id="userIcon" image="/images/user.png" />
|
|
72
|
+
<Label id="name" />
|
|
73
|
+
</View>
|
|
74
|
+
|
|
75
|
+
<Button id="viewProfileBtn"
|
|
76
|
+
title="View Profile"
|
|
77
|
+
onClick="onViewProfile" />
|
|
78
|
+
</View>
|
|
79
|
+
</Alloy>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### Controller (`controllers/userCard.js`)
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
const { Navigation } = require('services/navigation')
|
|
88
|
+
|
|
89
|
+
function init() {
|
|
90
|
+
const user = $.args.user
|
|
91
|
+
$.name.text = user.name
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function onViewProfile() {
|
|
95
|
+
Navigation.open('userProfile', { userId: $.args.user.id })
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function cleanup() {
|
|
99
|
+
$.destroy()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
$.cleanup = cleanup
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### Service (`lib/services/navigation.js`)
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
exports.Navigation = {
|
|
111
|
+
open(route, params = {}) {
|
|
112
|
+
const controller = Alloy.createController(route, params)
|
|
113
|
+
const view = controller.getView()
|
|
114
|
+
|
|
115
|
+
view.addEventListener('close', () => {
|
|
116
|
+
if (controller.cleanup) controller.cleanup()
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
view.open()
|
|
120
|
+
return controller
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### What improves from Tier 1?
|
|
128
|
+
|
|
129
|
+
* Controllers stay under control
|
|
130
|
+
* Logic is reusable
|
|
131
|
+
* Memory is explicitly cleaned
|
|
132
|
+
* Code is easier to test
|
|
133
|
+
|
|
134
|
+
But services can still depend directly on other services or globals.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
# Tier 3 — Advanced / Enterprise (Service-Oriented)
|
|
139
|
+
|
|
140
|
+
**Best for:** Complex applications (IDEs, platforms, multi-state apps).
|
|
141
|
+
|
|
142
|
+
This is where architectural maturity changes fundamentally.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Core Concepts
|
|
147
|
+
|
|
148
|
+
### 1️⃣ Dependency Injection
|
|
149
|
+
|
|
150
|
+
Services receive what they need — they don’t fetch it.
|
|
151
|
+
|
|
152
|
+
### 2️⃣ Service Registry
|
|
153
|
+
|
|
154
|
+
Centralized dependency wiring.
|
|
155
|
+
|
|
156
|
+
### 3️⃣ ID Scoping
|
|
157
|
+
|
|
158
|
+
Services receive only the UI elements they own.
|
|
159
|
+
|
|
160
|
+
### 4️⃣ Encapsulation (Black Box Principle)
|
|
161
|
+
|
|
162
|
+
Once a service method works, treat it as trusted.
|
|
163
|
+
|
|
164
|
+
### 5️⃣ Observability
|
|
165
|
+
|
|
166
|
+
All logs are structured and include service context.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Example (Tier 3)
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### Service (`lib/services/chatService.js`)
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
/**
|
|
178
|
+
* @param {Object} deps
|
|
179
|
+
* @param {Object} deps.ui - Scoped UI elements
|
|
180
|
+
* @param {Object} deps.logger
|
|
181
|
+
* @param {Object} deps.designService
|
|
182
|
+
*/
|
|
183
|
+
module.exports = function createChatService(deps) {
|
|
184
|
+
const { ui, logger, designService } = deps
|
|
185
|
+
|
|
186
|
+
function sendMessage(text) {
|
|
187
|
+
if (!text) return
|
|
188
|
+
|
|
189
|
+
ui.sendBtn.enabled = false // ID Scoping
|
|
190
|
+
designService.trackMessage(text) // Inter-service communication
|
|
191
|
+
|
|
192
|
+
logger.event('message:sent', {
|
|
193
|
+
service: 'ChatService',
|
|
194
|
+
length: text.length
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
sendMessage
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### Registry (`lib/services/indexServiceRegistry.js`)
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
const createChatService = require('services/chatService')
|
|
210
|
+
const createDesignService = require('services/designService')
|
|
211
|
+
|
|
212
|
+
module.exports = function createIndexServiceRegistry(deps) {
|
|
213
|
+
const { $, Ti, logger } = deps
|
|
214
|
+
|
|
215
|
+
// 1. Independent services
|
|
216
|
+
const designService = createDesignService({ Ti, logger })
|
|
217
|
+
|
|
218
|
+
// 2. Dependent services with scoped UI
|
|
219
|
+
const chatService = createChatService({
|
|
220
|
+
ui: {
|
|
221
|
+
sendBtn: $.sendBtn,
|
|
222
|
+
messageInput: $.messageInput
|
|
223
|
+
},
|
|
224
|
+
logger,
|
|
225
|
+
designService
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
chatService,
|
|
230
|
+
designService
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Decision Matrix
|
|
238
|
+
|
|
239
|
+
| Signal | Move To |
|
|
240
|
+
| ----------------------------------- | ------- |
|
|
241
|
+
| Controller > 100 lines | Tier 2 |
|
|
242
|
+
| Controller > 300 lines | Tier 3 |
|
|
243
|
+
| More than 50 IDs in XML | Tier 3 |
|
|
244
|
+
| Services need to talk to each other | Tier 3 |
|
|
245
|
+
| Multiple application states | Tier 3 |
|
|
246
|
+
| Memory leaks appear | Tier 3 |
|
|
247
|
+
|
|
248
|
+
---
|
|
@@ -198,3 +198,176 @@ Installation method:
|
|
|
198
198
|
2. Open Xcode, then Window, then Devices.
|
|
199
199
|
3. Select the device, then Installed Apps, then +.
|
|
200
200
|
4. Select the IPA file.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Mac Catalyst distribution (Mac App Store)
|
|
205
|
+
|
|
206
|
+
Mac Catalyst allows you to run your iPad app on macOS. Titanium SDK 13.1.1.GA and later supports building for Mac Catalyst.
|
|
207
|
+
|
|
208
|
+
### 1. Enable Mac Catalyst for your App ID
|
|
209
|
+
|
|
210
|
+
1. Go to [Apple Developer → Identifiers](https://developer.apple.com/account/resources/identifiers/list)
|
|
211
|
+
2. Select your App ID or create a new one
|
|
212
|
+
3. Enable **Mac Catalyst** capability
|
|
213
|
+
4. Save the changes
|
|
214
|
+
|
|
215
|
+
### 2. Create Mac App Store Distribution Certificate
|
|
216
|
+
|
|
217
|
+
1. Go to [Apple Developer → Certificates](https://developer.apple.com/account/resources/certificates/list)
|
|
218
|
+
2. Click **+** to create a new certificate
|
|
219
|
+
3. Select **Mac App Store Distribution**
|
|
220
|
+
4. Upload your CSR (Certificate Signing Request)
|
|
221
|
+
5. Download and install the certificate
|
|
222
|
+
|
|
223
|
+
### 3. Mac Catalyst build targets
|
|
224
|
+
|
|
225
|
+
Titanium provides two targets for Mac Catalyst:
|
|
226
|
+
|
|
227
|
+
| Target | Description | Configuration |
|
|
228
|
+
|--------|-------------|---------------|
|
|
229
|
+
| `macos` | Development builds for testing on Mac | Debug-maccatalyst |
|
|
230
|
+
| `dist-macappstore` | Production builds for Mac App Store | Release-maccatalyst |
|
|
231
|
+
|
|
232
|
+
### 4. Build for Mac Catalyst (Development)
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
ti build -p ios -T macos
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
The `.app` bundle will be created at:
|
|
239
|
+
```
|
|
240
|
+
build/iphone/build/Products/Debug-maccatalyst/AppName.app
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
For a production-ready build:
|
|
244
|
+
```bash
|
|
245
|
+
ti build -p ios -T macos --deploy-type production
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The `.app` bundle will be at:
|
|
249
|
+
```
|
|
250
|
+
build/iphone/build/Products/Release-maccatalyst/AppName.app
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 5. Build for Mac App Store (Distribution)
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
ti build -p ios -T dist-macappstore [-R <CERTIFICATE_NAME>]
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Example:
|
|
260
|
+
```bash
|
|
261
|
+
ti build -p ios -T dist-macappstore -R "Apple Distribution: Your Team Name (TEAM_ID)"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
If you omit the `-R` flag, Titanium will prompt you to select a certificate.
|
|
265
|
+
|
|
266
|
+
**What happens during the build:**
|
|
267
|
+
- Uses `Release-maccatalyst` configuration
|
|
268
|
+
- Sets code signing to Manual with identity `-`
|
|
269
|
+
- Creates a `.xcarchive` for Mac App Store
|
|
270
|
+
- Installs the archive in Xcode's Organizer
|
|
271
|
+
- Destination: `generic/platform=macOS`
|
|
272
|
+
|
|
273
|
+
### 6. Upload to Mac App Store Connect
|
|
274
|
+
|
|
275
|
+
1. Open Xcode → Window → Organizer
|
|
276
|
+
2. Select your Mac Catalyst archive
|
|
277
|
+
3. Click **Validate App** to check for issues
|
|
278
|
+
4. Click **Distribute App**
|
|
279
|
+
5. Select **Mac App Store**
|
|
280
|
+
6. Follow the prompts to upload
|
|
281
|
+
|
|
282
|
+
### 7. Create app listing in App Store Connect
|
|
283
|
+
|
|
284
|
+
1. Go to [App Store Connect](https://appstoreconnect.apple.com)
|
|
285
|
+
2. **My Apps → + → New App**
|
|
286
|
+
3. Select **Mac** as platform
|
|
287
|
+
4. Enter app details:
|
|
288
|
+
- Name
|
|
289
|
+
- Primary Language
|
|
290
|
+
- Bundle ID (must match your App ID with Mac Catalyst enabled)
|
|
291
|
+
- SKU
|
|
292
|
+
5. Complete required metadata:
|
|
293
|
+
- Description
|
|
294
|
+
- Keywords
|
|
295
|
+
- Screenshots (Mac-specific sizes)
|
|
296
|
+
- Category
|
|
297
|
+
- Age rating
|
|
298
|
+
|
|
299
|
+
### 8. Mac Catalyst entitlements
|
|
300
|
+
|
|
301
|
+
Add Mac-specific entitlements in `tiapp.xml`:
|
|
302
|
+
|
|
303
|
+
```xml
|
|
304
|
+
<ios>
|
|
305
|
+
<entitlements>
|
|
306
|
+
<dict>
|
|
307
|
+
<!-- File access for saving to Downloads -->
|
|
308
|
+
<key>com.apple.security.files.user-selected.read-write</key>
|
|
309
|
+
<true/>
|
|
310
|
+
<key>com.apple.security.files.downloads.read-write</key>
|
|
311
|
+
<true/>
|
|
312
|
+
|
|
313
|
+
<!-- App sandbox (required for Mac App Store) -->
|
|
314
|
+
<key>com.apple.security.app-sandbox</key>
|
|
315
|
+
<true/>
|
|
316
|
+
|
|
317
|
+
<!-- Network access -->
|
|
318
|
+
<key>com.apple.security.network.client</key>
|
|
319
|
+
<true/>
|
|
320
|
+
|
|
321
|
+
<!-- Additional entitlements as needed -->
|
|
322
|
+
<key>com.apple.security.print</key>
|
|
323
|
+
<true/>
|
|
324
|
+
</dict>
|
|
325
|
+
</entitlements>
|
|
326
|
+
</ios>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### 9. Common issues
|
|
330
|
+
|
|
331
|
+
**Issue**: "No suitable signing certificate found"
|
|
332
|
+
- Ensure you have a **Mac App Store Distribution Certificate** (not iOS Distribution)
|
|
333
|
+
- The certificate must be installed in your Keychain
|
|
334
|
+
|
|
335
|
+
**Issue**: Build fails with code signing errors
|
|
336
|
+
- Verify your App ID has Mac Catalyst enabled
|
|
337
|
+
- Check that the certificate matches the App ID
|
|
338
|
+
- Try cleaning the build: `ti clean -p ios`
|
|
339
|
+
|
|
340
|
+
**Issue**: App crashes on launch
|
|
341
|
+
- Verify entitlements are correctly configured
|
|
342
|
+
- Check Console.app for crash logs
|
|
343
|
+
- Ensure all required capabilities are enabled
|
|
344
|
+
|
|
345
|
+
### 10. Versioning
|
|
346
|
+
|
|
347
|
+
Update version numbers in `tiapp.xml`:
|
|
348
|
+
|
|
349
|
+
```xml
|
|
350
|
+
<ti:app xmlns:ti="http://ti.tidev.io">
|
|
351
|
+
<id>com.yourcompany.yourapp</id>
|
|
352
|
+
<name>Your App Name</name>
|
|
353
|
+
<version>1.0.0</version>
|
|
354
|
+
<publisher>Your Company</publisher>
|
|
355
|
+
...
|
|
356
|
+
</ti:app>
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
- `version`: Display version (e.g., "1.0.0")
|
|
360
|
+
- For iOS, use `pv-version-code` in `<ios>` section for build number
|
|
361
|
+
- For Mac, the build number can be set in Xcode or via `CFBundleVersion`
|
|
362
|
+
|
|
363
|
+
### 11. Testing on Mac
|
|
364
|
+
|
|
365
|
+
Before submitting to Mac App Store:
|
|
366
|
+
|
|
367
|
+
1. Build with `macos` target for testing
|
|
368
|
+
2. Run the app on different Mac architectures (Intel and Apple Silicon)
|
|
369
|
+
3. Test all features that use file system, network, and other sandboxed resources
|
|
370
|
+
4. Verify entitlements are working correctly
|
|
371
|
+
5. Test on macOS versions you plan to support
|
|
372
|
+
|
|
373
|
+
---
|
|
@@ -258,7 +258,7 @@ Omit `-V` and `-P` to be prompted.
|
|
|
258
258
|
| `-P, --pp-uuid <uuid>` | Provisioning profile UUID (for device/dist targets). |
|
|
259
259
|
| `-R, --distribution-name <name>` | iOS Distribution Certificate (for dist targets). |
|
|
260
260
|
| `--sim-focus` | Focus the iOS Simulator after launching (default: true). Use --no-sim-focus to disable. |
|
|
261
|
-
| `-T, --target <value>` | Target: simulator, device, dist-appstore, or dist-
|
|
261
|
+
| `-T, --target <value>` | Target: simulator, device, dist-appstore, dist-adhoc, macos, or dist-macappstore. |
|
|
262
262
|
| `-V, --developer-name <name>` | iOS Developer Certificate (required for device target). |
|
|
263
263
|
| `-W, --watch-device-id <udid>` | Watch simulator UDID (simulator only). |
|
|
264
264
|
| `--watch-app-name <name>` | Name of the watch app to launch (simulator only). |
|
|
@@ -394,6 +394,48 @@ ti build -p ios -T dist-appstore -R "Pseudo, Inc." -P "AAAAAAAA-0000-9999-8888-7
|
|
|
394
394
|
|
|
395
395
|
Installs the package to Xcode Organizer.
|
|
396
396
|
|
|
397
|
+
### Mac Catalyst (macOS Development)
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
ti build -p ios -T macos
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Builds a Mac Catalyst version of your iOS app for local testing. The resulting `.app` bundle is located at:
|
|
404
|
+
```
|
|
405
|
+
build/iphone/build/Products/Debug-maccatalyst/AppName.app
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
For a production build:
|
|
409
|
+
```bash
|
|
410
|
+
ti build -p ios -T macos --deploy-type production
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
The release `.app` bundle will be at:
|
|
414
|
+
```
|
|
415
|
+
build/iphone/build/Products/Release-maccatalyst/AppName.app
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Mac App Store (Mac Catalyst Distribution)
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
ti build -p ios -T dist-macappstore [-R <CERT_NAME>]
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Example:
|
|
425
|
+
```bash
|
|
426
|
+
ti build -p ios -T dist-macappstore -R "Apple Distribution: Your Name (TEAM_ID)"
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Important Notes:**
|
|
430
|
+
- Requires a Mac App Store Distribution Certificate (not iOS Distribution)
|
|
431
|
+
- The build creates an archive for Mac App Store distribution
|
|
432
|
+
- The `.xcarchive` is installed in Xcode's Organizer
|
|
433
|
+
- Uses `Release-maccatalyst` configuration
|
|
434
|
+
- Destination: `generic/platform=macOS`
|
|
435
|
+
- Code signing is set to Manual with identity `-`
|
|
436
|
+
|
|
437
|
+
The target `dist-macappstore` is available in Titanium SDK 13.1.1.GA and later.
|
|
438
|
+
|
|
397
439
|
---
|
|
398
440
|
|
|
399
441
|
## SDK management
|