iOS file picker support (#3175)

* ios: folder picker and readdir
* update README.md

Co-authored-by: Weihua Lu <luwh364@gmail.com>
Co-authored-by: leizhe <leizhe@leizhedeMacBook-Air.local>
This commit is contained in:
Tienson Qin
2021-11-17 16:19:48 +08:00
committed by GitHub
parent c7fa6cae5e
commit 425f688082
10 changed files with 225 additions and 10206 deletions

View File

@@ -14,7 +14,9 @@
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
7435D10C2704659F00AB88E0 /* FolderPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7435D10B2704659F00AB88E0 /* FolderPicker.swift */; };
7435D10F2704660B00AB88E0 /* FolderPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 7435D10E2704660B00AB88E0 /* FolderPicker.m */; };
BDDF3BC6333CA3EA105F48B2 /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A16FBA319D846498E23D2B9 /* Pods_App.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -27,9 +29,12 @@
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
6A16FBA319D846498E23D2B9 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7435D10B2704659F00AB88E0 /* FolderPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPicker.swift; sourceTree = "<group>"; };
7435D10D2704660A00AB88E0 /* App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "App-Bridging-Header.h"; sourceTree = "<group>"; };
7435D10E2704660B00AB88E0 /* FolderPicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FolderPicker.m; sourceTree = "<group>"; };
A693BFCEA424C37DF4D9AF1B /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
B9A79754543D95E609C23F91 /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -37,28 +42,20 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */,
BDDF3BC6333CA3EA105F48B2 /* Pods_App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {
isa = PBXGroup;
children = (
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
504EC2FB1FED79650016851F = {
isa = PBXGroup;
children = (
504EC3061FED79650016851F /* App */,
504EC3051FED79650016851F /* Products */,
7F8756D8B27F46E3366F6CEA /* Pods */,
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
D337740F89DEEAD18C87762B /* Pods */,
9FC5AB18C7E7E43B09B33A61 /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -81,17 +78,29 @@
504EC3131FED79650016851F /* Info.plist */,
2FAD9762203C412B000D30F8 /* config.xml */,
50B271D01FEDC1A000F3C39B /* public */,
7435D10B2704659F00AB88E0 /* FolderPicker.swift */,
7435D10E2704660B00AB88E0 /* FolderPicker.m */,
7435D10D2704660A00AB88E0 /* App-Bridging-Header.h */,
);
path = App;
sourceTree = "<group>";
};
7F8756D8B27F46E3366F6CEA /* Pods */ = {
9FC5AB18C7E7E43B09B33A61 /* Frameworks */ = {
isa = PBXGroup;
children = (
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,
6A16FBA319D846498E23D2B9 /* Pods_App.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
D337740F89DEEAD18C87762B /* Pods */ = {
isa = PBXGroup;
children = (
B9A79754543D95E609C23F91 /* Pods-App.debug.xcconfig */,
A693BFCEA424C37DF4D9AF1B /* Pods-App.release.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
@@ -101,11 +110,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */;
buildPhases = (
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
818545F0788D5DB466761623 /* [CP] Check Pods Manifest.lock */,
504EC3001FED79650016851F /* Sources */,
504EC3011FED79650016851F /* Frameworks */,
504EC3021FED79650016851F /* Resources */,
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
4BF32F1E9453A6AB603D7CD2 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -123,11 +132,11 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0920;
LastUpgradeCheck = 1310;
TargetAttributes = {
504EC3031FED79650016851F = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
LastSwiftMigration = 1250;
ProvisioningStyle = Automatic;
};
};
@@ -167,25 +176,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {
4BF32F1E9453A6AB603D7CD2 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -200,6 +191,28 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
818545F0788D5DB466761623 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -208,6 +221,8 @@
buildActionMask = 2147483647;
files = (
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
7435D10F2704660B00AB88E0 /* FolderPicker.m in Sources */,
7435D10C2704659F00AB88E0 /* FolderPicker.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -247,6 +262,7 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -254,8 +270,10 @@
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -304,6 +322,7 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -311,8 +330,10 @@
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -342,9 +363,10 @@
};
504EC3171FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
baseConfigurationReference = B9A79754543D95E609C23F91 /* Pods-App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@@ -353,6 +375,8 @@
PRODUCT_BUNDLE_IDENTIFIER = com.logseq.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -360,9 +384,10 @@
};
504EC3181FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
baseConfigurationReference = A693BFCEA424C37DF4D9AF1B /* Pods-App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@@ -370,6 +395,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.logseq.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};

View File

@@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

View File

@@ -0,0 +1,12 @@
//
// FolderPicker.m
// App
//
// Created by weihua on 9/29/21.
//
#import <Capacitor/Capacitor.h>
CAP_PLUGIN(FolderPicker, "FolderPicker",
CAP_PLUGIN_METHOD(pickFolder, CAPPluginReturnPromise);
)

View File

@@ -0,0 +1,53 @@
//
// FolderPicker.swift
// App
//
// Created by weihua on 9/29/21.
//
import Capacitor
import Foundation
import MobileCoreServices
@objc(FolderPicker)
public class FolderPicker: CAPPlugin, UIDocumentPickerDelegate {
public var _call: CAPPluginCall? = nil
@objc func pickFolder(_ call: CAPPluginCall) {
self._call = call
DispatchQueue.main.async { [weak self] in
let documentPicker = UIDocumentPickerViewController(
documentTypes: [String(kUTTypeFolder)],
in: UIDocumentPickerMode.open
)
documentPicker.allowsMultipleSelection = false
documentPicker.delegate = self
documentPicker.modalPresentationStyle = UIModalPresentationStyle.fullScreen
self?.bridge?.viewController?.present(
documentPicker,
animated: true,
completion: nil
)
}
}
public func documentPicker(
_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
){
var items: [String] = []
for url in urls {
items.append(url.absoluteString)
}
self._call?.resolve([
"path": items.first
])
}
}

View File

@@ -1,10 +1,19 @@
{
"appId": "com.logseq.app",
"appName": "Logseq",
"webDir": "public",
"bundledWebRuntime": false,
"server": {
"url": "http://192.168.50.122:3001",
"cleartext": true
"webDir": "public",
"plugins": {
"SplashScreen": {
"launchShowDuration": 3000,
"launchAutoHide": false,
"androidScaleType": "CENTER_CROP",
"splashImmersive": true,
"backgroundColor": "#002b36"
}
}
"server": {
"url": "http://192.168.0.104:3001",
"cleartext": true
}
}

View File

@@ -10,6 +10,7 @@ def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
end
target 'App' do

33
ios/README.md Normal file
View File

@@ -0,0 +1,33 @@
Installation:
* Install Xcode 13 from App Store.
* Install [CocoaPods](https://cocoapods.org/)
```shell
sudo gem install cocoapods
```
Note: use the following commands from *ios/App* directory to fix **ffi_c.bundle** related issue for M1 MacBook [^1].
```shell
sudo arch -x86_64 gem install ffi
arch -x86_64 pod install
```
* Run `yarn && yarn app-watch` from the logseq project root directory in terminal.
* Open Logseq project in Xcode by running the following command in termimal.
```shell
npx cap open ios
```
Note: for the first time after a fresh clone.
- Run `npx cap copy ios` to copy web assets from public to *ios/App/App/public*, and create *capacitor.config.json* in *ios/App/App*.
- Run `npx cap update ios` to update iOS plugins.
- Add the following code to *ios/App/App/capacitor.config.json*, and replace `server url` with your local-ip-address:3001 (run ifconfig to check)
```json
"server": {
"url": "http://your-own-id-address:3001",
"cleartext": true}
```
* Run logseq
```shell
npx cap run ios
```
[^1] https://github.com/CocoaPods/CocoaPods/issues/10220#issuecomment-730963835

View File

@@ -91,6 +91,7 @@
"mldoc": "1.1.8",
"path": "^0.12.7",
"pixi-graph-fork": "^0.1.6",
"pixi.js": "^6.2.0",
"posthog-js": "^1.10.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",

View File

@@ -46,7 +46,7 @@
files-dir (->> files-with-stats
(filterv
(fn [{:keys [type]}]
(= type "directory")))
(contains? #{"directory" "NSFileTypeDirectory"} type)))
(mapv :uri))
files-result
@@ -54,7 +54,7 @@
(->> files-with-stats
(filter
(fn [{:keys [type]}]
(= type "file")))
(contains? #{"file" "NSFileTypeRegular"} type)))
(filter
(fn [{:keys [uri]}]
(some #(string/ends-with? uri %)
@@ -77,10 +77,10 @@
(mkdir! [this dir]
(prn "mkdir: " dir)
(p/let [result (.mkdir Filesystem
(clj->js
{:path dir
;; :directory (.-ExternalStorage Directory)
}))]
(clj->js
{:path dir
;; :directory (.-ExternalStorage Directory)
}))]
(js/console.log result)
result))
(mkdir-recur! [this dir]
@@ -101,29 +101,35 @@
(read-file [this dir path _options]
(let [path (str dir path)]
(p/let [content (.readFile Filesystem
(clj->js
{:path path
:directory (.-ExternalStorage Directory)
:encoding (.-UTF8 Encoding)}))]
(clj->js
{:path path
:directory (.-ExternalStorage Directory)
:encoding (.-UTF8 Encoding)}))]
content)))
(write-file! [this repo dir path content {:keys [ok-handler error-handler] :as opts}]
(let [path (if (string/starts-with? path (config/get-repo-dir repo))
(let [path (cond
(= (util/platform) "ios")
path
(string/starts-with? path (config/get-repo-dir repo))
path
:else
(-> (str dir "/" path)
(string/replace "//" "/")))]
(p/catch
(p/let [result (.writeFile Filesystem
(clj->js
{:path path
:data content
:encoding (.-UTF8 Encoding)
:recursive true}))]
(when ok-handler
(ok-handler repo path result)))
(fn [error]
(if error-handler
(error-handler error)
(log/error :write-file-failed error))))))
(p/let [result (.writeFile Filesystem
(clj->js
{:path path
:data content
:encoding (.-UTF8 Encoding)
:recursive true}))]
(when ok-handler
(ok-handler repo path result)))
(fn [error]
(if error-handler
(error-handler error)
(log/error :write-file-failed error))))))
(rename! [this repo old-path new-path]
nil)
(stat [this dir path]
@@ -132,19 +138,17 @@
{:path path
;; :directory (.-ExternalStorage Directory)
}))]
result)))
result)))
(open-dir [this ok-handler]
(case (util/platform)
"android"
(p/let [_ (check-permission-android)
path (p/chain
(.pickFolder util/folder-picker)
#(js->clj % :keywordize-keys true)
:path)
files (readdir path)]
(js/console.log path)
(js/console.log files)
(into [] (concat [{:path path}] files)))))
(p/let [_ (when (= (util/platform) "android") (check-permission-android))
path (p/chain
(.pickFolder util/folder-picker)
#(js->clj % :keywordize-keys true)
:path)
files (readdir path)]
(js/console.log path)
(js/console.log files)
(into [] (concat [{:path path}] files))))
(get-files [this path-or-handle _ok-handler]
(readdir path-or-handle))
(watch-dir! [this dir]

10124
yarn.lock

File diff suppressed because it is too large Load Diff