Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: iOS

on:
pull_request:
paths: ['ios/**', '.github/workflows/ios.yml']
push:
paths: ['ios/**', '.github/workflows/ios.yml']

jobs:
build-and-test:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
permissions:
contents: read
runs-on: macos-15
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security Workflow Actions Are Mutable

The workflow executes both actions/checkout@v4 here and actions/upload-artifact@v4 on line 35 through mutable tags. If either upstream tag is repointed, the workflow will execute substituted code on the macOS runner with access to the checked-out repository and its read-scoped token. Pin both actions to full commit SHAs so the executed dependencies are immutable.

How this was verified: Both workflow steps reference movable v4 tags and run with repository read access.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/ios.yml
Line: 15

Comment:
**Workflow Actions Are Mutable**

The workflow executes both `actions/checkout@v4` here and `actions/upload-artifact@v4` on line 35 through mutable tags. If either upstream tag is repointed, the workflow will execute substituted code on the macOS runner with access to the checked-out repository and its read-scoped token. Pin both actions to full commit SHAs so the executed dependencies are immutable.

**How this was verified:** Both workflow steps reference movable `v4` tags and run with repository read access.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

with:
persist-credentials: false
- name: Test persistence and backups
run: swift test --package-path ios/FlowCore
- name: Build iOS app
run: >-
xcodebuild -project ios/Flow.xcodeproj -scheme Flow
-destination 'generic/platform=iOS Simulator'
-derivedDataPath ios/.build/DerivedData CODE_SIGNING_ALLOWED=NO build
- name: Select an available iPhone simulator
run: |
DEVICE_ID=$(xcrun simctl list devices available -j | python3 -c 'import json,sys; devices=json.load(sys.stdin)["devices"]; print(next(d["udid"] for group in devices.values() for d in group if d["name"].startswith("iPhone")))')
echo "FLOW_SIMULATOR_ID=$DEVICE_ID" >> "$GITHUB_ENV"
- name: Test UIKit flows
run: >-
xcodebuild -project ios/Flow.xcodeproj -scheme Flow
-destination "platform=iOS Simulator,id=$FLOW_SIMULATOR_ID"
-derivedDataPath ios/.build/DerivedData -resultBundlePath ios/.build/UITests.xcresult
CODE_SIGNING_ALLOWED=NO test
- uses: actions/upload-artifact@v4
if: failure()
with:
name: iOS-test-results
path: ios/.build/UITests.xcresult
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@
.externalNativeBuild
.cxx
local.properties

# iOS and Swift
ios/.build/
ios/FlowCore/.build/
ios/FlowCore/.swiftpm/
DerivedData/
*.xcresult
xcuserdata/
*.xcuserstate
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Flow
</h1>

Notes & Tasks app for Android. Fully native. Just your stuff, no bullshit
Notes & Tasks app for Android and iOS. Fully native. Just your stuff, no bullshit

[**Features**](#features) · [**Usage**](https://github.com/jvqtil/Flow/wiki/Usage) · [**Screenshots**](#screenshots) · [**Contributing**](#contributing)

Expand Down Expand Up @@ -55,7 +55,11 @@ Notes & Tasks app for Android. Fully native. Just your stuff, no bullshit

## Contributing

### Building
### iOS

Open [`ios/Flow.xcodeproj`](ios/Flow.xcodeproj) in Xcode 16 or later and run the **Flow** scheme on an iPhone or iPad simulator. The native UIKit app supports iOS 16+, local notes and tasks, folders, attachments, and Android-compatible `.flow` backups. See the [iOS guide](ios/README.md) for build commands, tests, and platform details.

### Building Android
Clone the repository and open it in Android Studio.

Build it with:
Expand Down
276 changes: 276 additions & 0 deletions ios/Flow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
// !$*UTF8*$!
{
"archiveVersion" = 1;
"classes" = {
};
"objectVersion" = 77;
"objects" = {
"000000000000000000000001" = {
"isa" = "PBXProject";
"attributes" = {
"BuildIndependentTargetsInParallel" = "YES";
"LastUpgradeCheck" = "1600";
"TargetAttributes" = {
"00000000000000000000000A" = {
"CreatedOnToolsVersion" = "16.0";
};
"000000000000000000000014" = {
"CreatedOnToolsVersion" = "16.0";
"TestTargetID" = "00000000000000000000000A";
};
};
};
"buildConfigurationList" = "00000000000000000000001E";
"compatibilityVersion" = "Xcode 16.0";
"developmentRegion" = "en";
"hasScannedForEncodings" = 0;
"knownRegions" = ("en", "Base");
"mainGroup" = "000000000000000000000002";
"minimizedProjectReferenceProxies" = 1;
"packageReferences" = ("000000000000000000000032");
"preferredProjectObjectVersion" = 77;
"productRefGroup" = "000000000000000000000005";
"projectDirPath" = "";
"projectRoot" = "";
"targets" = ("00000000000000000000000A", "000000000000000000000014");
};
"000000000000000000000002" = {
"isa" = "PBXGroup";
"children" = ("000000000000000000000003", "000000000000000000000004", "000000000000000000000005");
"sourceTree" = "<group>";
};
"000000000000000000000003" = {
"isa" = "PBXFileSystemSynchronizedRootGroup";
"exceptions" = ("000000000000000000000006");
"path" = "Flow";
"sourceTree" = "<group>";
};
"000000000000000000000004" = {
"isa" = "PBXFileSystemSynchronizedRootGroup";
"path" = "FlowUITests";
"sourceTree" = "<group>";
};
"000000000000000000000005" = {
"isa" = "PBXGroup";
"children" = ("00000000000000000000000B", "000000000000000000000015");
"name" = "Products";
"sourceTree" = "<group>";
};
"000000000000000000000006" = {
"isa" = "PBXFileSystemSynchronizedBuildFileExceptionSet";
"membershipExceptions" = ("Info.plist");
"target" = "00000000000000000000000A";
};
"00000000000000000000000A" = {
"isa" = "PBXNativeTarget";
"buildConfigurationList" = "000000000000000000000021";
"buildPhases" = ("00000000000000000000000C", "00000000000000000000000D", "00000000000000000000000E");
"buildRules" = ();
"dependencies" = ();
"fileSystemSynchronizedGroups" = ("000000000000000000000003");
"name" = "Flow";
"packageProductDependencies" = ("000000000000000000000033");
"productName" = "Flow";
"productReference" = "00000000000000000000000B";
"productType" = "com.apple.product-type.application";
};
"00000000000000000000000B" = {
"isa" = "PBXFileReference";
"explicitFileType" = "wrapper.application";
"includeInIndex" = 0;
"path" = "Flow.app";
"sourceTree" = "BUILT_PRODUCTS_DIR";
};
"00000000000000000000000C" = {
"isa" = "PBXSourcesBuildPhase";
"buildActionMask" = 2147483647;
"files" = ();
"runOnlyForDeploymentPostprocessing" = 0;
};
"00000000000000000000000D" = {
"isa" = "PBXFrameworksBuildPhase";
"buildActionMask" = 2147483647;
"files" = ("000000000000000000000034");
"runOnlyForDeploymentPostprocessing" = 0;
};
"00000000000000000000000E" = {
"isa" = "PBXResourcesBuildPhase";
"buildActionMask" = 2147483647;
"files" = ();
"runOnlyForDeploymentPostprocessing" = 0;
};
"000000000000000000000014" = {
"isa" = "PBXNativeTarget";
"buildConfigurationList" = "000000000000000000000024";
"buildPhases" = ("000000000000000000000016", "000000000000000000000017", "000000000000000000000018");
"buildRules" = ();
"dependencies" = ("000000000000000000000019");
"fileSystemSynchronizedGroups" = ("000000000000000000000004");
"name" = "FlowUITests";
"packageProductDependencies" = ();
"productName" = "FlowUITests";
"productReference" = "000000000000000000000015";
"productType" = "com.apple.product-type.bundle.ui-testing";
};
"000000000000000000000015" = {
"isa" = "PBXFileReference";
"explicitFileType" = "wrapper.cfbundle";
"includeInIndex" = 0;
"path" = "FlowUITests.xctest";
"sourceTree" = "BUILT_PRODUCTS_DIR";
};
"000000000000000000000016" = {
"isa" = "PBXSourcesBuildPhase";
"buildActionMask" = 2147483647;
"files" = ();
"runOnlyForDeploymentPostprocessing" = 0;
};
"000000000000000000000017" = {
"isa" = "PBXFrameworksBuildPhase";
"buildActionMask" = 2147483647;
"files" = ();
"runOnlyForDeploymentPostprocessing" = 0;
};
"000000000000000000000018" = {
"isa" = "PBXResourcesBuildPhase";
"buildActionMask" = 2147483647;
"files" = ();
"runOnlyForDeploymentPostprocessing" = 0;
};
"000000000000000000000019" = {
"isa" = "PBXTargetDependency";
"target" = "00000000000000000000000A";
"targetProxy" = "00000000000000000000001A";
};
"00000000000000000000001A" = {
"isa" = "PBXContainerItemProxy";
"containerPortal" = "000000000000000000000001";
"proxyType" = 1;
"remoteGlobalIDString" = "00000000000000000000000A";
"remoteInfo" = "Flow";
};
"00000000000000000000001E" = {
"isa" = "XCConfigurationList";
"buildConfigurations" = ("00000000000000000000001F", "000000000000000000000020");
"defaultConfigurationIsVisible" = 0;
"defaultConfigurationName" = "Release";
};
"00000000000000000000001F" = {
"isa" = "XCBuildConfiguration";
"name" = "Debug";
"buildSettings" = {
"CLANG_ENABLE_MODULES" = "YES";
"CLANG_ENABLE_OBJC_ARC" = "YES";
"IPHONEOS_DEPLOYMENT_TARGET" = "16.0";
"SDKROOT" = "iphoneos";
"SWIFT_VERSION" = "5.0";
"DEBUG_INFORMATION_FORMAT" = "dwarf";
"ENABLE_TESTABILITY" = "YES";
"GCC_OPTIMIZATION_LEVEL" = "0";
"ONLY_ACTIVE_ARCH" = "YES";
"SWIFT_ACTIVE_COMPILATION_CONDITIONS" = "DEBUG";
"SWIFT_OPTIMIZATION_LEVEL" = "-Onone";
};
};
"000000000000000000000020" = {
"isa" = "XCBuildConfiguration";
"name" = "Release";
"buildSettings" = {
"CLANG_ENABLE_MODULES" = "YES";
"CLANG_ENABLE_OBJC_ARC" = "YES";
"IPHONEOS_DEPLOYMENT_TARGET" = "16.0";
"SDKROOT" = "iphoneos";
"SWIFT_VERSION" = "5.0";
"DEBUG_INFORMATION_FORMAT" = "dwarf-with-dsym";
"SWIFT_COMPILATION_MODE" = "wholemodule";
"SWIFT_OPTIMIZATION_LEVEL" = "-O";
"VALIDATE_PRODUCT" = "YES";
};
};
"000000000000000000000021" = {
"isa" = "XCConfigurationList";
"buildConfigurations" = ("000000000000000000000022", "000000000000000000000023");
"defaultConfigurationIsVisible" = 0;
"defaultConfigurationName" = "Release";
};
"000000000000000000000022" = {
"isa" = "XCBuildConfiguration";
"name" = "Debug";
"buildSettings" = {
"ASSETCATALOG_COMPILER_APPICON_NAME" = "AppIcon";
"CODE_SIGN_STYLE" = "Automatic";
"CURRENT_PROJECT_VERSION" = "1";
"GENERATE_INFOPLIST_FILE" = "NO";
"INFOPLIST_FILE" = "Flow/Info.plist";
"LD_RUNPATH_SEARCH_PATHS" = ("$(inherited)", "@executable_path/Frameworks");
"MARKETING_VERSION" = "1.0";
"PRODUCT_BUNDLE_IDENTIFIER" = "dev.jvqtil.flow.ios";
"PRODUCT_NAME" = "$(TARGET_NAME)";
"SUPPORTED_PLATFORMS" = "iphoneos iphonesimulator";
"SUPPORTS_MACCATALYST" = "NO";
"TARGETED_DEVICE_FAMILY" = "1,2";
};
};
"000000000000000000000023" = {
"isa" = "XCBuildConfiguration";
"name" = "Release";
"buildSettings" = {
"ASSETCATALOG_COMPILER_APPICON_NAME" = "AppIcon";
"CODE_SIGN_STYLE" = "Automatic";
"CURRENT_PROJECT_VERSION" = "1";
"GENERATE_INFOPLIST_FILE" = "NO";
"INFOPLIST_FILE" = "Flow/Info.plist";
"LD_RUNPATH_SEARCH_PATHS" = ("$(inherited)", "@executable_path/Frameworks");
"MARKETING_VERSION" = "1.0";
"PRODUCT_BUNDLE_IDENTIFIER" = "dev.jvqtil.flow.ios";
"PRODUCT_NAME" = "$(TARGET_NAME)";
"SUPPORTED_PLATFORMS" = "iphoneos iphonesimulator";
"SUPPORTS_MACCATALYST" = "NO";
"TARGETED_DEVICE_FAMILY" = "1,2";
};
};
"000000000000000000000024" = {
"isa" = "XCConfigurationList";
"buildConfigurations" = ("000000000000000000000025", "000000000000000000000026");
"defaultConfigurationIsVisible" = 0;
"defaultConfigurationName" = "Release";
};
"000000000000000000000025" = {
"isa" = "XCBuildConfiguration";
"name" = "Debug";
"buildSettings" = {
"CODE_SIGN_STYLE" = "Automatic";
"GENERATE_INFOPLIST_FILE" = "YES";
"PRODUCT_BUNDLE_IDENTIFIER" = "dev.jvqtil.flow.ios.uitests";
"PRODUCT_NAME" = "$(TARGET_NAME)";
"TARGETED_DEVICE_FAMILY" = "1,2";
"TEST_TARGET_NAME" = "Flow";
};
};
"000000000000000000000026" = {
"isa" = "XCBuildConfiguration";
"name" = "Release";
"buildSettings" = {
"CODE_SIGN_STYLE" = "Automatic";
"GENERATE_INFOPLIST_FILE" = "YES";
"PRODUCT_BUNDLE_IDENTIFIER" = "dev.jvqtil.flow.ios.uitests";
"PRODUCT_NAME" = "$(TARGET_NAME)";
"TARGETED_DEVICE_FAMILY" = "1,2";
"TEST_TARGET_NAME" = "Flow";
};
};
"000000000000000000000032" = {
"isa" = "XCLocalSwiftPackageReference";
"relativePath" = "FlowCore";
};
"000000000000000000000033" = {
"isa" = "XCSwiftPackageProductDependency";
"productName" = "FlowCore";
};
"000000000000000000000034" = {
"isa" = "PBXBuildFile";
"productRef" = "000000000000000000000033";
};
};
"rootObject" = "000000000000000000000001";
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions ios/Flow.xcodeproj/xcshareddata/xcschemes/Flow.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme LastUpgradeVersion="1600" version="1.3">
<BuildAction parallelizeBuildables="YES" buildImplicitDependencies="YES"><BuildActionEntries>
<BuildActionEntry buildForTesting="YES" buildForRunning="YES" buildForProfiling="YES" buildForArchiving="YES" buildForAnalyzing="YES"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="00000000000000000000000A" BuildableName="Flow.app" BlueprintName="Flow" ReferencedContainer="container:Flow.xcodeproj"/></BuildActionEntry>
</BuildActionEntries></BuildAction>
<TestAction buildConfiguration="Debug" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.IDEFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv="YES">
<Testables><TestableReference skipped="NO" parallelizable="NO"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="000000000000000000000014" BuildableName="FlowUITests.xctest" BlueprintName="FlowUITests" ReferencedContainer="container:Flow.xcodeproj"/></TestableReference></Testables>
</TestAction>
<LaunchAction buildConfiguration="Debug" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.IDEFoundation.Launcher.LLDB" launchStyle="0" useCustomWorkingDirectory="NO" ignoresPersistentStateOnLaunch="NO" debugDocumentVersioning="YES" allowLocationSimulation="YES">
<BuildableProductRunnable runnableDebuggingMode="0"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="00000000000000000000000A" BuildableName="Flow.app" BlueprintName="Flow" ReferencedContainer="container:Flow.xcodeproj"/></BuildableProductRunnable>
</LaunchAction>
<ProfileAction buildConfiguration="Release" shouldUseLaunchSchemeArgsEnv="YES" useCustomWorkingDirectory="NO" debugDocumentVersioning="YES"><BuildableProductRunnable runnableDebuggingMode="0"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="00000000000000000000000A" BuildableName="Flow.app" BlueprintName="Flow" ReferencedContainer="container:Flow.xcodeproj"/></BuildableProductRunnable></ProfileAction>
<AnalyzeAction buildConfiguration="Debug"/>
<ArchiveAction buildConfiguration="Release" revealArchiveInOrganizer="YES"/>
</Scheme>
11 changes: 11 additions & 0 deletions ios/Flow/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import UIKit

@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let configuration = UISceneConfiguration(name: "Flow", sessionRole: connectingSceneSession.role)
configuration.delegateClass = SceneDelegate.self
return configuration
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions ios/Flow/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"images":[{"filename":"AppIcon.png","idiom":"universal","platform":"ios","size":"1024x1024"}],"info":{"author":"xcode","version":1}}
1 change: 1 addition & 0 deletions ios/Flow/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"info":{"author":"xcode","version":1}}
Loading
Loading