Skip to content

Commit

Permalink
add iOS support
Browse files Browse the repository at this point in the history
  • Loading branch information
sun-jiao committed Feb 28, 2024
1 parent 43de313 commit d2f63de
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 26 deletions.
67 changes: 60 additions & 7 deletions ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import UIKit
import Flutter
import UniformTypeIdentifiers

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
Expand All @@ -15,9 +16,18 @@ import Flutter
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// This method is invoked on the UI thread.
if call.method == "dirAccess" {
self.dirAccess(result: result)
self!.dirAccess(result: result)
} else if call.method == "fileAccess" {
guard let args = call.arguments as? [String : Any] else {return}
let startPath = args["startPath"] as! String

self!.fileAccess(startPath: startPath, result: result)
} else if call.method == "renameFile" {
self.renameFile(result: result)
guard let args = call.arguments as? [String : Any] else {return}
let oldPath = args["oldPath"] as! String
let newPath = args["newPath"] as! String

self!.renameFile(oldPath: oldPath, newPath: newPath, result: result)
} else {
result(FlutterMethodNotImplemented)
}
Expand All @@ -26,21 +36,64 @@ import Flutter
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

var currentFR: FlutterResult?

private func dirAccess(result: @escaping FlutterResult) {
let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder])
let folderPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder], asCopy: false)
folderPicker.delegate = self
self.currentFR = result
window?.rootViewController?.present(folderPicker, animated: true, completion: nil)
}

private func fileAccess(startPath: String, result: @escaping FlutterResult) {
let startUrl = URL(fileURLWithPath: startPath)
let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.item], asCopy: false)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = true
documentPicker.directoryURL = startUrl
self.currentFR = result
window?.rootViewController?.present(documentPicker, animated: true, completion: nil)
}

private func renameFile(result: @escaping FlutterResult) {
return
private func renameFile(oldPath: String, newPath: String, result: FlutterResult) {
let oldUrl = URL(fileURLWithPath: oldPath)
let dirUrl = oldUrl.deletingLastPathComponent()
let newUrl = URL(fileURLWithPath: newPath)

do {
let access = oldUrl.startAccessingSecurityScopedResource()
let folderAccess = dirUrl.startAccessingSecurityScopedResource()
try FileManager.default.moveItem(at: oldUrl, to: newUrl)
dirUrl.stopAccessingSecurityScopedResource()
oldUrl.stopAccessingSecurityScopedResource()
result(NSNumber(value: true))
return
} catch let error as NSError {
result(FlutterError(code: error.description, message: error.localizedDescription, details: nil))
} catch let error {
result(FlutterError(code: error.localizedDescription, message: error.localizedDescription, details: nil))
}

result(NSNumber(value: false))
dirUrl.stopAccessingSecurityScopedResource()
oldUrl.stopAccessingSecurityScopedResource()
}
}

extension AppDelegate: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if let url = urls.first {
// result(url.path)
guard urls.first != nil && self.currentFR != nil else {
return
}

self.currentFR!(urls.map { $0.path })
}

func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
guard self.currentFR != nil else {
return
}

self.currentFR!(nil)
}
}
10 changes: 0 additions & 10 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,10 @@
<string>remote-notification</string>
</array>

<key>NSAppleMusicUsageDescription</key>
<string>Explain why your app uses music</string>

<key>UISupportsDocumentBrowser</key>
<true/>

<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>

<key>NSPhotoLibraryUsageDescription</key>
<string>Explain why your app uses photo library</string>

<key>NSDocumentsFolderUsageDescription</key>
<string>Explain why your app uses</string>

</dict>
</plist>
18 changes: 14 additions & 4 deletions lib/pages/files_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import '../entity/theme_extension.dart';
import '../l10n/l10n.dart';
import '../pages/android_file_picker_page.dart';
import '../tools/ios_platform.dart';
import '../tools/responsive.dart';
import '../entity/constants.dart';
import '../tools/ex_file.dart';
Expand Down Expand Up @@ -39,7 +40,7 @@ class FilesPageState extends State<FilesPage> {
String _filter = '';

Future<void> addFileFromPicker() async {
late List<FileSystemEntity> entities;
late Iterable<FileSystemEntity> entities;
if (Platform.isAndroid) {
final result = await Navigator.push(
context,
Expand All @@ -52,20 +53,29 @@ class FilesPageState extends State<FilesPage> {
return;
}
} else if (Platform.isIOS) {
final dirs = await PlatformFilePicker.dirAccess();
if (dirs == null || dirs.first == null) {
return;
}

final files = await PlatformFilePicker.fileAccess(dirs.first.toString());
if (files == null) {
return;
}

entities = files.skipWhile((e) => e == null).map((e) => e.toString()).map((e) => e.toFileSystemEntity());
} else {
FilePickerResult? result = await FilePicker.platform.pickFiles(allowMultiple: true);
if (result != null) {
entities = result.files
.where((e1) => e1.path != null && _files.every((e2) => e1.path != e2.path))
.map((e) => e.toFileSystemEntity())
.toList();
.map((e) => e.toFileSystemEntity());
} else {
return;
}
}
setState(() {
_files.addAll(entities);
_files.addAll(entities.skipWhile((eNew) => _files.any((eOld) => eNew.path == eOld.path)));
});
}

Expand Down
4 changes: 4 additions & 0 deletions lib/tools/ex_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ extension ExLink on Link {
FileSystemEntity toFileSystemEntity() => _toFileSystemEntity(this, (link) => link.targetSync());
}

extension ExPathString on String {
FileSystemEntity toFileSystemEntity() => _toFileSystemEntity(this, (str) => str);
}

FileSystemEntity _toFileSystemEntity<T>(T file, String Function(T file) func) {
FileSystemEntity entity;

Expand Down
33 changes: 28 additions & 5 deletions lib/tools/ios_platform.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
import 'package:flutter/services.dart';

class PlatformFilePicker {
static const MethodChannel _channel = MethodChannel('net.sunjiao.renamer/picker');
static const MethodChannel _channel =
MethodChannel('net.sunjiao.renamer/picker');

static Future<void> dirAccess() async {
static Future<List<Object?>?> dirAccess() async {
try {
await _channel.invokeMethod('dirAccess');
return await _channel.invokeMethod('dirAccess');
} on PlatformException catch (e) {
// show error message
rethrow;
}
}

static Future<void> renameFile() async {
static Future<List<Object?>?> fileAccess(String startPath) async {
try {
await _channel.invokeMethod('renameFile');
return await _channel.invokeMethod(
'fileAccess',
{
'startPath': startPath,
},
);
} on PlatformException catch (e) {
// show error message
rethrow;
}
}

static Future<bool> renameFile(String oldPath, String newPath) async {
try {
return await _channel.invokeMethod(
'renameFile',
{
'oldPath': oldPath,
'newPath': newPath,
},
);
} on PlatformException catch (e) {
// show error message
rethrow;
}
}
}
9 changes: 9 additions & 0 deletions lib/tools/rename.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:renamer/tools/ios_platform.dart';

import '../l10n/l10n.dart';
import '../tools/ex_file.dart';
Expand All @@ -22,6 +23,14 @@ Future<FileSystemEntity?> rename(

try {
file.newName = replaceSpecialCharacters(file.newName);
if (Platform.isIOS) {
final success = await PlatformFilePicker.renameFile(file.path, file.newPath);

if (success) {
return file.newPath.toFileSystemEntity();
}
}

return await file.rename(file.newPath);
} catch (e, s) {
debugPrint(e.toString());
Expand Down

0 comments on commit d2f63de

Please sign in to comment.