Skip to content

Commit adee08a

Browse files
authored
Self hosted runners, very basic testing, fixed bad webview start, fixed navbar bounce (#11)
* test self hosted runner * fix yaml * fix runs on * ugh * bunch of stuff * minor tweaks * fix * more tweaks * even more fixes * missed one * ok more * fix nav bar jitter * cleanup
1 parent eb34c31 commit adee08a

File tree

13 files changed

+195
-94
lines changed

13 files changed

+195
-94
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 33 deletions
This file was deleted.

.github/workflows/publish.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# name: build-ios-app
2+
# on:
3+
# release:
4+
# types: [published]
5+
# concurrency:
6+
# group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
7+
# cancel-in-progress: true
8+
# jobs:
9+
# build:
10+
# if: github.event.release.prerelease == false
11+
# runs-on: runs-on: ['self-hosted', 'macOS', 'ARM64']
12+
# steps:
13+
# - name: "Add SSH Key"
14+
# uses: webfactory/[email protected]
15+
# with:
16+
# ssh-private-key: ${{ secrets.SSH_KEY }}
17+
# - uses: actions/checkout@v4
18+
# - name: "Publish App to App Store"
19+
# run: fastlane appstore
20+
# env:
21+
# MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
22+
# MATCH_URL: [email protected]:django-files/ios-match-certs.git
23+
# CI: false
24+
# APPLE_ID: ${{ secrets.APPLE_ID }}
25+
# ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
26+
# ASC_API_KEY: ${{ secrets.ASC_API_KEY }}
27+
# ASC_API_ID: ${{ secrets.ASC_API_ID }}
28+
# BUILD_NUMBER: ${{ github.run_number }}
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
name: build-ios-app
1+
name: build-ios-app-testflight
22
on:
3-
push:
4-
branches:
5-
- 'main'
3+
release:
4+
types: [published]
5+
concurrency:
6+
group: ${{ github.workflow }}
7+
cancel-in-progress: true
68
jobs:
79
build:
8-
runs-on: macos-15
10+
if: ${{ github.event.release.prerelease }}
11+
runs-on: runs-on: ['self-hosted', 'macOS', 'ARM64']
912
steps:
10-
- name: "Debug"
11-
run: |
12-
fastlane env
13-
/usr/bin/xcodebuild -version
1413
- name: "Add SSH Key"
1514
uses: webfactory/[email protected]
1615
with:
@@ -26,3 +25,4 @@ jobs:
2625
ASC_API_KEY: ${{ secrets.ASC_API_KEY }}
2726
ASC_API_ID: ${{ secrets.ASC_API_ID }}
2827
BUILD_NUMBER: ${{ github.run_number }}
28+
VERSION_NUMBER: ${{ github.event.release.tag_name }}

.github/workflows/tests.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: "Django Files iOS CI"
2+
on:
3+
push:
4+
branches:
5+
- '**'
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.ref_name }}
8+
cancel-in-progress: true
9+
jobs:
10+
build:
11+
name: "Tests"
12+
runs-on: ['self-hosted', 'macOS', 'ARM64']
13+
steps:
14+
- name: "Add Match Repo SSH Key"
15+
uses: webfactory/[email protected]
16+
with:
17+
ssh-private-key: ${{ secrets.SSH_KEY }}
18+
- uses: actions/checkout@v4
19+
- name: "Fastlane Tests"
20+
run: fastlane tests
21+
env:
22+
CI: true

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ xcuserdata/
1313
*.dSYM
1414

1515
.build/
16-
fastlane/
16+
.DS_Store
17+
report.xml
18+
report.html
19+
report.junit

Django Files/Django_FilesApp.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,37 @@ struct Django_FilesApp: App {
2222
}
2323
}()
2424

25+
init() {
26+
// Handle reset arguments
27+
if CommandLine.arguments.contains("--DeleteAllData") {
28+
// Clear UserDefaults
29+
if let bundleID = Bundle.main.bundleIdentifier {
30+
UserDefaults.standard.removePersistentDomain(forName: bundleID)
31+
}
32+
// Clear SwiftData store
33+
do {
34+
let context = sharedModelContainer.mainContext
35+
// Delete all DjangoFilesSession objects
36+
try context.delete(model: DjangoFilesSession.self)
37+
try context.save()
38+
} catch {
39+
print("Error clearing SwiftData store: \(error)")
40+
}
41+
// Clear any files in the app's documents directory
42+
if let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
43+
do {
44+
let fileURLs = try FileManager.default.contentsOfDirectory(at: documentsPath,
45+
includingPropertiesForKeys: nil)
46+
for fileURL in fileURLs {
47+
try FileManager.default.removeItem(at: fileURL)
48+
}
49+
} catch {
50+
print("Error clearing documents directory: \(error)")
51+
}
52+
}
53+
}
54+
}
55+
2556
var body: some Scene {
2657
WindowGroup {
2758
ContentView()

Django Files/Views/Auth.swift

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class AuthController: NSObject, WKNavigationDelegate, WKDownloadDelegate, UIScro
2424

2525
var url: URL?
2626

27-
let webView: WKWebView = FullScreenWebView()
27+
let webView: WKWebView = WKWebView()
2828

2929
let customUserAgent = "DjangoFiles iOS \(String(describing: Bundle.main.releaseVersionNumber ?? "Unknown"))(\(String(describing: Bundle.main.buildVersionNumber ?? "-")))"
3030

@@ -55,13 +55,15 @@ class AuthController: NSObject, WKNavigationDelegate, WKDownloadDelegate, UIScro
5555

5656
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping @MainActor @Sendable (WKNavigationResponsePolicy) -> Void){
5757
webView.isHidden = false
58+
webView.scrollView.contentInsetAdjustmentBehavior = .never
5859
onLoadedAction?()
5960

6061
if authComplete{
6162
decisionHandler(.allow)
6263
return
6364
}
64-
65+
66+
6567
if navigationResponse.response.url?.absoluteString == url!.appendingPathComponent("/").absoluteString {
6668
let code = (navigationResponse.response as! HTTPURLResponse).statusCode
6769
switch code{
@@ -105,9 +107,6 @@ class AuthController: NSObject, WKNavigationDelegate, WKDownloadDelegate, UIScro
105107
reloadState = false
106108
}
107109
}
108-
else{
109-
updatePageSafeArea()
110-
}
111110
}
112111

113112
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy{
@@ -195,14 +194,6 @@ class AuthController: NSObject, WKNavigationDelegate, WKDownloadDelegate, UIScro
195194
safeAreaInsets = insets
196195
}
197196

198-
public func updatePageSafeArea(){
199-
if webView.url?.host() == url?.host(){
200-
webView.evaluateJavaScript("document.body.getElementsByClassName(\"navbar\")[0].style.paddingTop = \"\(safeAreaInsets.top)px\";", completionHandler: { (jsonRaw: Any?, error: Error?) in })
201-
webView.evaluateJavaScript("document.body.getElementsByClassName(\"flex-grow-1\")[0].style.paddingLeft = \"\(safeAreaInsets.trailing)px\";", completionHandler: { (jsonRaw: Any?, error: Error?) in })
202-
webView.evaluateJavaScript("document.body.getElementsByClassName(\"flex-grow-1\")[0].style.paddingRight = \"\(safeAreaInsets.leading)px\";", completionHandler: { (jsonRaw: Any?, error: Error?) in })
203-
}
204-
}
205-
206197
public func reset(){
207198
authError = nil
208199
clearToken()
@@ -237,7 +228,7 @@ struct AuthView: UIViewRepresentable {
237228

238229
func makeUIView(context: Context) -> WKWebView {
239230
guard let url = URL(string: httpsUrl) else {
240-
return FullScreenWebView()
231+
return WKWebView()
241232
}
242233

243234
if doReset{
@@ -269,13 +260,6 @@ struct AuthView: UIViewRepresentable {
269260
}
270261

271262
func updateUIView(_ uiView: WKWebView, context: Context) {
272-
authController.updatePageSafeArea()
273-
}
274-
}
275-
276-
class FullScreenWebView: WKWebView {
277-
override var safeAreaInsets: UIEdgeInsets {
278-
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
279263
}
280264
}
281265

Django Files/Views/ContentView.swift

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct ContentView: View {
1919
@State private var selectedServer: DjangoFilesSession? = DjangoFilesSession()
2020
@State private var selectedSession: DjangoFilesSession? // Track session for settings
2121
@State private var showingSelector = false // Show SessionSelector
22+
@State private var needsRefresh = false // Added to handle refresh after adding server
2223

2324
@State private var token: String?
2425

@@ -42,14 +43,12 @@ struct ContentView: View {
4243
Label("Settings", systemImage: "gear")
4344
}
4445
.tint(.indigo)
45-
}
46+
}
4647
}
4748
}
48-
.onDelete(perform: deleteItems) }
49+
.onDelete(perform: deleteItems)
50+
}
4951
.toolbar {
50-
ToolbarItem(placement: .navigationBarTrailing) {
51-
EditButton()
52-
}
5352
ToolbarItem {
5453
Button(action: {
5554
self.showingEditor.toggle()
@@ -60,24 +59,26 @@ struct ContentView: View {
6059
}
6160
}
6261
} detail: {
63-
AuthViewContainer(viewingSettings: $viewingSettings, selectedServer: $selectedServer, columnVisibility: $columnVisibility, showingEditor: $showingEditor)
62+
AuthViewContainer(viewingSettings: $viewingSettings, selectedServer: $selectedServer, columnVisibility: $columnVisibility, showingEditor: $showingEditor, needsRefresh: $needsRefresh)
6463
.navigationViewStyle(StackNavigationViewStyle())
6564
}
6665
.sheet(isPresented: $showingEditor){
67-
SessionEditor(session: nil) // New session case
66+
SessionEditor(session: nil)
67+
.onDisappear {
68+
// When editor is dismissed, trigger a refresh and select the new server
69+
if items.count > 0 {
70+
needsRefresh = true
71+
selectedServer = items.last
72+
}
73+
}
6874
}
6975
.sheet(item: $selectedSession) { session in
7076
SessionSelector(session: session)
7177
}
7278
.onAppear() {
7379
setDefaultServer()
74-
if items.count > 0{
75-
selectedServer = items.first(where: {
76-
$0.defaultSession == true
77-
})
78-
if selectedServer == nil{
79-
selectedServer = items.first
80-
}
80+
if items.count > 0 {
81+
selectedServer = items.first(where: { $0.defaultSession }) ?? items.first
8182
}
8283
}
8384
.frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -123,9 +124,10 @@ public struct AuthViewContainer: View {
123124
var selectedServer: Binding<DjangoFilesSession?>
124125
var columnVisibility: Binding<NavigationSplitViewVisibility>
125126
var showingEditor: Binding<Bool>
127+
var needsRefresh: Binding<Bool>
128+
126129
@State private var toolbarHidden: Bool = false
127130
@State private var authError: Bool = false
128-
129131
@State private var authController: AuthController = AuthController()
130132

131133
var backButton : some View { Button(action: {
@@ -193,31 +195,34 @@ public struct AuthViewContainer: View {
193195
.onAppear(){
194196
authController.setSafeAreaInsets(geometry.safeAreaInsets)
195197
columnVisibility.wrappedValue = .automatic
198+
if needsRefresh.wrappedValue {
199+
authController.reset()
200+
needsRefresh.wrappedValue = false
201+
}
196202
}
197203
.onChange(of: geometry.safeAreaInsets){
198204
authController.setSafeAreaInsets(geometry.safeAreaInsets)
199-
authController.updatePageSafeArea()
200-
}
201-
.toolbar(toolbarHidden && UIDevice.current.userInterfaceIdiom == .phone ? .hidden : .visible)
202-
.navigationTitle(Text(""))
203-
.navigationBarBackButtonHidden(true)
204-
.navigationBarItems(leading: backButton)
205-
.alert(isPresented: $authError){
206-
Alert(title: Text("Error"), message: Text(authController.getAuthErrorMessage() ?? "Unknown Error"))
207205
}
208206
}
209207
.frame(maxWidth: .infinity, maxHeight: .infinity)
210208
.edgesIgnoringSafeArea(.all)
209+
.toolbar(toolbarHidden && UIDevice.current.userInterfaceIdiom == .phone ? .hidden : .visible)
210+
.navigationTitle(Text(""))
211+
.navigationBarBackButtonHidden(true)
212+
.navigationBarItems(leading: backButton)
213+
.alert(isPresented: $authError){
214+
Alert(title: Text("Error"), message: Text(authController.getAuthErrorMessage() ?? "Unknown Error"))
215+
}
211216
}
212-
else{
217+
else {
213218
Text("Loading...")
214219
.onAppear(){
215220
columnVisibility.wrappedValue = .automatic
216221
}
217222
}
218223
}
219224
else{
220-
Text("Select an item")
225+
Text("Select a Server")
221226
.onAppear(){
222227
columnVisibility.wrappedValue = .automatic
223228
}

Django Files/Views/SessionEditor.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct SessionEditor: View {
2525
session.token = token
2626
session.auth = false
2727
} else {
28-
let session = DjangoFilesSession ()
28+
let session = DjangoFilesSession()
2929
session.url = url?.absoluteString ?? ""
3030
for item in items{
3131
item.defaultSession = false
@@ -37,7 +37,7 @@ struct SessionEditor: View {
3737
do {
3838
try modelContext.save()
3939
} catch {
40-
40+
print("Error saving session: \(error)")
4141
}
4242
}
4343
}
@@ -73,6 +73,7 @@ struct SessionEditor: View {
7373
))
7474
.disableAutocorrection(true)
7575
.textInputAutocapitalization(.never)
76+
.accessibilityIdentifier("urlTextField")
7677
} label: {
7778
Text("URL:")
7879
}
@@ -104,8 +105,9 @@ struct SessionEditor: View {
104105
}
105106
})
106107
{
107-
Text("Add Session")
108+
Text(editorTitle)
108109
}
110+
.accessibilityIdentifier("serverSubmitButton")
109111
.alert(isPresented: $badURL){
110112
Alert(title: Text("Invalid URL"), message: Text("Invalid URL format or scheme (http or https).\nExample: https://df.myserver.com"))
111113
}

0 commit comments

Comments
 (0)