1678531926
Привет, друзья,
Сегодня я объясню простой пример того, как загрузить файл с помощью Laravel 10 и ajax. В этом видео показано, как использовать Ajax для загрузки файла в папку в Laravel 10. В этом руководстве мы покажем вам, как загрузить файл на примере ajax и jQuery в Laravel 10. Этот пример проведет вас через Пошаговое руководство по загрузке файлов Ajax для Laravel 10. В приложении Laravel 10 нам часто нужно сохранять данные файла с помощью ajax-запроса без перезагрузки страницы. Я покажу, как загрузить файл с помощью ajax-вызова в этом руководстве по загрузке файлов Jquery в Laravel 10. Кроме того, мы рассмотрим, как проверить файл перед его загрузкой на сервер.
Загрузить файл с помощью ajax в Laravel и сохранить его в базе данных довольно просто, если вы знакомы с отправкой форм Laravel с использованием ajax. Укажите путь, куда вы хотите поместить загруженный файл при вызове метода хранилища.
В Laravel 10 загрузка файлов с помощью Ajax-запроса довольно проста. Давайте начнем наш учебник по загрузке файла ajax laravel 10 с нуля.
Шаг 1: Загрузите Laravel
Установка свежего приложения Laravel запустит учебник. Если проект уже создан, пропустите следующий шаг.
composer create-project laravel/laravel example-app
Шаг 2: Добавьте миграцию и модель
Здесь мы создадим миграцию для таблицы «файлы», давайте запустим следующую команду и обновим код.
php artisan make:migration create_images_table
2022_02_23_054554_create_images_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('images');
}
};
Затем запустите создание новой миграции с помощью команды миграции laravel, как показано ниже:
php artisan migrate
Теперь мы создадим модель файла с помощью следующей команды:
php artisan make:model File
приложение/Модели/Файл.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class File extends Model
{
use HasFactory;
protected $table = 'images';
protected $fillable = [
'name'
];
}
Step 3: Add Controller
In this step, we will create a new FileUploadController; in this file, we will add two method index() and store() for render view and store files into folder and database logic.
Let's create FileUploadController by following command:
php artisan make:controller FileUploadController
next, let's update the following code to Controller File.
app/Http/Controllers/FileUploadController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\File;
class FileUploadController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('fileUpload');
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate([
'file' => 'required|mimes:doc,docx,pdf,zip,rar|max:2048',
]);
$fileName = time().'.'.$request->file->extension();
$request->file->move(public_path('file'), $fileName);
File::create(['name' => $fileName]);
return response()->json('File uploaded successfully');
}
}
Store Files in Storage Folder
$file->storeAs('files', $fileName);
// storage/app/files/file.pdf
Store Files in Public Folder
$file->move(public_path('files'), $fileName);
// public/files/file.pdf
Store Files in S3
$file->storeAs('files', $fileName, 's3');
Step 4: Add Routes
Furthermore, open routes/web.php file and add the routes to manage GET and POST requests for render view and store file logic.
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\FileUploadController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::controller(FileUploadController::class)->group(function(){
Route::get('file-upload', 'index');
Route::post('file-upload', 'store')->name('file.store');
});
Step 5: Add Blade File
At last step we need to create fileUpload.blade.php file and in this file we will create form with file input button and written jquery ajax code. So copy bellow and put on that file.
resources/views/fileUpload.blade.php
<!DOCTYPE html>
<html>
<head>
<title>How to Upload File using Ajax in Laravel 10? </title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div class="container">
<div class="panel panel-primary card mt-5">
<div class="panel-heading text-center mt-4">
<h2>How to Upload File using Ajax in Laravel 10?</h2>
</div>
<div class="panel-body card-body">
<form action="{{ route('file.store') }}" method="POST" id="file-upload" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label class="form-label" for="inputFile">Select File:</label>
<input
type="file"
name="file"
id="inputFile"
class="form-control">
<span class="text-danger" id="file-input-error"></span>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success">Upload</button>
</div>
</form>
</div>
</div>
</div>
</body>
<script type="text/javascript">
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$('#file-upload').submit(function(e) {
e.preventDefault();
let formData = new FormData(this);
$('#file-input-error').text('');
$.ajax({
type:'POST',
url: "{{ route('file.store') }}",
data: formData,
contentType: false,
processData: false,
success: (response) => {
if (response) {
this.reset();
alert('File has been uploaded successfully');
}
},
error: function(response){
$('#file-input-error').text(response.responseJSON.message);
}
});
});
</script>
</html>
Run Laravel App:
Все шаги выполнены, теперь вам нужно ввести данную команду и нажать Enter, чтобы запустить приложение laravel:
php artisan serve
Теперь вам нужно открыть веб-браузер, ввести указанный URL-адрес и просмотреть вывод приложения:
http://localhost:8000/file-upload
Я надеюсь, что это может помочь вам...
Оригинальный источник статьи: https://www.nicesnippets.com/
1678528141
嗨朋友们,
今天,我将解释一个简单的示例,说明如何使用 Laravel 10 和 ajax 上传文件。该视频向您展示了如何使用 Ajax 将文件上传到 Laravel 10 中的文件夹。在本教程中,我们将向您展示如何使用 Laravel 10 中的 ajax 和 jQuery 示例上传文件。该示例将引导您完成Laravel 10 分步 Ajax 文件上传教程。对于 Laravel 10 应用程序,我们经常需要在不重新加载页面的情况下使用 ajax 请求保存文件数据。我将在 Laravel 10 Jquery 文件上传教程中演示如何使用 ajax 调用上传文件。此外,我们还将了解如何在将文件上传到服务器之前验证文件。
如果您熟悉使用 ajax 的 Laravel 表单提交,那么在 Laravel 中使用 ajax 上传文件并将其保存到数据库是非常简单的。调用store方法时指定要放置上传文件的路径。
在 Laravel 10 中,使用 Ajax 请求上传文件非常简单。让我们从头开始我们的 laravel 10 ajax 文件上传教程。
第 1 步:下载 Laravel
安装一个新的 Laravel 应用程序将开始本教程。如果项目已经创建,则跳过下一步。
composer create-project laravel/laravel example-app
第 2 步:添加迁移和模型
在这里,我们将为“文件”表创建迁移,让我们运行下面的命令并更新代码。
php artisan make:migration create_images_table
2022_02_23_054554_create_images_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('images');
}
};
接下来,使用 laravel 迁移命令运行创建新迁移,如下所示:
php artisan migrate
现在我们将使用以下命令创建文件模型:
php artisan make:model File
应用程序/模型/File.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class File extends Model
{
use HasFactory;
protected $table = 'images';
protected $fillable = [
'name'
];
}
第 3 步:添加控制器
在这一步中,我们将创建一个新的 FileUploadController;在这个文件中,我们将添加两个方法 index() 和 store() 用于呈现视图并将文件存储到文件夹和数据库逻辑中。
让我们通过以下命令创建 FileUploadController:
php artisan make:controller FileUploadController
接下来,让我们将以下代码更新到 Controller File。
应用程序/Http/Controllers/FileUploadController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\File;
class FileUploadController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('fileUpload');
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate([
'file' => 'required|mimes:doc,docx,pdf,zip,rar|max:2048',
]);
$fileName = time().'.'.$request->file->extension();
$request->file->move(public_path('file'), $fileName);
File::create(['name' => $fileName]);
return response()->json('File uploaded successfully');
}
}
将文件存储在存储文件夹中
$file->storeAs('files', $fileName);
// storage/app/files/file.pdf
将文件存储在公用文件夹中
$file->move(public_path('files'), $fileName);
// public/files/file.pdf
在 S3 中存储文件
$file->storeAs('files', $fileName, 's3');
第 4 步:添加路线
此外,打开 routes/web.php 文件并添加路由来管理渲染视图和存储文件逻辑的 GET 和 POST 请求。
路线/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\FileUploadController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::controller(FileUploadController::class)->group(function(){
Route::get('file-upload', 'index');
Route::post('file-upload', 'store')->name('file.store');
});
第 5 步:添加刀片文件
最后一步我们需要创建 fileUpload.blade.php 文件,在这个文件中我们将创建带有文件输入按钮的表单和编写的 jquery ajax 代码。所以复制下面的内容并放在那个文件上。
资源/视图/fileUpload.blade.php
<!DOCTYPE html>
<html>
<head>
<title>How to Upload File using Ajax in Laravel 10? </title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div class="container">
<div class="panel panel-primary card mt-5">
<div class="panel-heading text-center mt-4">
<h2>How to Upload File using Ajax in Laravel 10?</h2>
</div>
<div class="panel-body card-body">
<form action="{{ route('file.store') }}" method="POST" id="file-upload" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label class="form-label" for="inputFile">Select File:</label>
<input
type="file"
name="file"
id="inputFile"
class="form-control">
<span class="text-danger" id="file-input-error"></span>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success">Upload</button>
</div>
</form>
</div>
</div>
</div>
</body>
<script type="text/javascript">
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$('#file-upload').submit(function(e) {
e.preventDefault();
let formData = new FormData(this);
$('#file-input-error').text('');
$.ajax({
type:'POST',
url: "{{ route('file.store') }}",
data: formData,
contentType: false,
processData: false,
success: (response) => {
if (response) {
this.reset();
alert('File has been uploaded successfully');
}
},
error: function(response){
$('#file-input-error').text(response.responseJSON.message);
}
});
});
</script>
</html>
运行 Laravel 应用程序:
所有步骤都已完成,现在您必须键入给定的命令并按回车键来运行 laravel 应用程序:
php artisan serve
现在,您必须打开 Web 浏览器,输入给定的 URL 并查看应用程序输出:
http://localhost:8000/file-upload
我希望它可以帮助你...
文章原文出处:https: //www.nicesnippets.com/
1678524360
Hi friends,
Today, I'll explain a straightforward example of how to upload a file using Laravel 10 and ajax. This video shows you how to use Ajax to upload a file to a folder in Laravel 10. In this tutorial, we'll show you how to upload a file using an example of ajax and jQuery in Laravel 10. This example walks you through the Ajax file upload tutorial for Laravel 10 step-by-step. With a Laravel 10 application, we frequently need to save the file data with an ajax request without reloading the page. I'll demonstrate how to upload a file using an ajax call in this Laravel 10 Jquery File Upload tutorial. Also, we'll look at how to verify a file before uploading it to a server.
Uploading a file using ajax in Laravel and saving it to the database is quite simple if you are familiar with Laravel form submission using ajax. Specify the path where you want to put the uploaded file when calling the store method.
In Laravel 10, uploading files using an Ajax request is quite simple. Let's begin our tutorial on laravel 10 ajax file upload from scratch.
Step 1: Download Laravel
Installing a fresh Laravel application will kick off the tutorial. If the project has already been created, skip the next step.
composer create-project laravel/laravel example-app
Step 2: Add Migration and Model
Here, we will create migration for "files" table, let's run bellow command and update code.
php artisan make:migration create_images_table
2022_02_23_054554_create_images_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('images');
}
};
Next, run create new migration using laravel migration command as bellow:
php artisan migrate
Now we will create File model by using following command:
php artisan make:model File
app/Models/File.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class File extends Model
{
use HasFactory;
protected $table = 'images';
protected $fillable = [
'name'
];
}
Step 3: Add Controller
In this step, we will create a new FileUploadController; in this file, we will add two method index() and store() for render view and store files into folder and database logic.
Let's create FileUploadController by following command:
php artisan make:controller FileUploadController
next, let's update the following code to Controller File.
app/Http/Controllers/FileUploadController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\File;
class FileUploadController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('fileUpload');
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate([
'file' => 'required|mimes:doc,docx,pdf,zip,rar|max:2048',
]);
$fileName = time().'.'.$request->file->extension();
$request->file->move(public_path('file'), $fileName);
File::create(['name' => $fileName]);
return response()->json('File uploaded successfully');
}
}
Store Files in Storage Folder
$file->storeAs('files', $fileName);
// storage/app/files/file.pdf
Store Files in Public Folder
$file->move(public_path('files'), $fileName);
// public/files/file.pdf
Store Files in S3
$file->storeAs('files', $fileName, 's3');
Step 4: Add Routes
Furthermore, open routes/web.php file and add the routes to manage GET and POST requests for render view and store file logic.
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\FileUploadController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::controller(FileUploadController::class)->group(function(){
Route::get('file-upload', 'index');
Route::post('file-upload', 'store')->name('file.store');
});
Step 5: Add Blade File
At last step we need to create fileUpload.blade.php file and in this file we will create form with file input button and written jquery ajax code. So copy bellow and put on that file.
resources/views/fileUpload.blade.php
<!DOCTYPE html>
<html>
<head>
<title>How to Upload File using Ajax in Laravel 10? </title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div class="container">
<div class="panel panel-primary card mt-5">
<div class="panel-heading text-center mt-4">
<h2>How to Upload File using Ajax in Laravel 10?</h2>
</div>
<div class="panel-body card-body">
<form action="{{ route('file.store') }}" method="POST" id="file-upload" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label class="form-label" for="inputFile">Select File:</label>
<input
type="file"
name="file"
id="inputFile"
class="form-control">
<span class="text-danger" id="file-input-error"></span>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success">Upload</button>
</div>
</form>
</div>
</div>
</div>
</body>
<script type="text/javascript">
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$('#file-upload').submit(function(e) {
e.preventDefault();
let formData = new FormData(this);
$('#file-input-error').text('');
$.ajax({
type:'POST',
url: "{{ route('file.store') }}",
data: formData,
contentType: false,
processData: false,
success: (response) => {
if (response) {
this.reset();
alert('File has been uploaded successfully');
}
},
error: function(response){
$('#file-input-error').text(response.responseJSON.message);
}
});
});
</script>
</html>
Run Laravel App:
All steps have been done, now you have to type the given command and hit enter to run the laravel app:
php artisan serve
Now, you have to open web browser, type the given URL and view the app output:
http://localhost:8000/file-upload
I hope it can help you...
Original article source at: https://www.nicesnippets.com/
1678216860
HFS is the best way via web to access or share files from your disk.
This is a full rewrite of the Delphi version.
Assets
hfs
filelocalhost
address, so you can configure the rest in the Admin-panel.create-admin <PASSWORD>
If you access Admin-panel via localhost, by default HFS won't require you to login. If you don't like this behavior, disable it in the Admin-panel or enter this console command config localhost_admin false
.
If your system is not Windows/Linux/Mac, you can try this alternative version:
sudo npm -g i hfs
hfs
Configuration and other files will be stored in %HOME%/.vfs
With this installation method, you can update with sudo npm -g update hfs
.
If you want to run HFS as a service
npm
on Windowsnpx qckwinsvc2 install name="HFS" description="HFS" path="%APPDATA%\npm\node_modules\hfs\src\index.js" args="--cwd %HOMEPATH%\.hfs" now
npx qckwinsvc2 uninstall name="HFS"
npm -g update hfs
It is possible to show the Front-end in other languages. In the Languages section of the Admin-panel you'll be able to install lang files. You can find some of these files at https://github.com/rejetto/hfs/tree/main/langs To download a file: open it, right-click on the "Raw" button, Save.
Files must be named hfs-lang-CODE.json
(lowercase), where CODE
is the ISO code for your language (e.g. pt-br for Brazilian).
Translation is applied automatically based on the configuration of the visitor's browser. Check the language configuration of your browser.
To install a plugin you just copy its folder inside plugins
folder.
Delete it to uninstall.
HFS will ignore all folders with -disabled
at the end of the name.
As you can see from the list of features, we already have some goods that you cannot find in HFS 2. Other than that, you can also consider:
But you may still want to stay with HFS 2.x (so far) for the following reasons
If you have access to HFS' console, you can enter commands. Start with help
to have a full list.
Configuration can be done in several ways
config NAME VALUE
--NAME VALUE
config.yaml
file. As soon as you save it is reloaded and changes are appliedNAME
stands for the property name that you want to change. See the complete list below.
Configuration is stored in the file config.yaml
, which is stored in the same folder of hfs.exe
if you are using this kind of distribution on Windows, or USER_FOLDER/.hfs
on other systems.
You can decide a different file and location by passing --config SOME_FILE
at command line, or inside an env called HFS_CONFIG
. Any relative path provided is relative to the cwd.
port
where to accept http connections. Default is 80.vfs
the files and folders you want to expose. For details see the dedicated following section.log
path of the log file. Default is access.log
.log_rotation
frequency of log rotation. Accepted values are daily
, weekly
, monthly
, or empty string to disable. Default is weekly
.error_log
path of the log file for errors. Default is error.log
.errors_in_main_log
if you want to use a single file for both kind of entries. Default is false.accounts
list of accounts. For details see the dedicated following section.mime
command what mime-type to be returned with some files. E.g.: "*.jpg": image/jpeg
You can specify multiple entries, or separate multiple file masks with a p|pe. You can use the special value auto
to attempt automatic detection.max_kbps
throttle output speed. Default is Infinity.max_kbps_per_ip
throttle output speed on a per-ip basis. Default is Infinity.zip_calculate_size_for_seconds
how long should we wait before the zip archive starts streaming, trying to understand its finale size. Default is 1.open_browser_at_start
should HFS open browser on localhost on start? Default is true.https_port
listen on a specific port. Default is 443.cert
use this file for https certificate. Minimum to start https is to give a cert and a private_key. Default is none.private_key
use this file for https private key. Default is none.allowed_referer
you can decide what domains can link to your files. Wildcards supported. Default is empty, meaning any.block
a list of rules that will block connections. E.g.:block:
- ip: 192.168.0.90
*
as wildcard and CIDR format.plugins_config
this is a generic place where you can find/put configuration for each plugin, at least those that need configuration.enable_plugins
if a plugin is not present here, it won't run. Defaults is [ antibrute ]
.localhost_admin
should Admin be accessed without credentials when on localhost. Default is true.proxies
number of proxies between server and clients to be trusted about providing clients' IP addresses. Default is 0.keep_unfinished_uploads
should unfinished uploads be deleted immediately when interrupted. Default is true.favicon
path to file to be used as favicon. Default is none.The virtual file system is a tree of files and folders, collectively called nodes. By default, a node is a folder, unless you provide for it a source that's a file. Valid keys in a node are:
name
: this is the name we'll use to display this file/folder. If not provided, HFS will infer it from the source. At least name
or source
must be provided.source
: absolute or relative path of where to get the contentchildren
: just for folders, specify its virtual children. Value is a list and its entries are nodes.rename
: similar to name, but it's from the parent node point. Use this to change the name of entries that are read from the source, not listed in the VFS. Value is a dictionary, where the key is the original name.mime
: specify what mime to use for this resource. Use "auto" for automatic detection.default
: to be used with a folder where you want to serve a default html. E.g.: "index.html". Using this will make mime
default to "auto".can_read
: specify who can download this entry. Value is a WhoCan
descriptor, which is one of these valuestrue
: anyone can, even people who didn't log in. This is normally the default value.false
: no one can."*"
: any account can, i.e. anyone who logged in.[ frank, peter ]
: the list of accounts who can.can_see
: specify who can see this entry. Even if a user can download you can still make the file not appear in the list. Remember that to see in the list you must also be able to download (read), or else you won't see it anyway. Value is a WhoCan
descriptor, refer above.can_upload
specify who can upload. Applies to folders with a source. Default is none.can_delete
specify who can delete. Applies to folders with a source. Default is none.masks
: maps a file mask to a set of properties as the one documented in this section. E.g."**/*.mp3":
can_read: false
"*.jpg|*.png":
mime: auto
Permissions set on an inner element will override inherited permissions. This means that you can restrict access to folder1, and yet decide to give free access to folder1/subfolder2.
All accounts go under accounts:
property, as a dictionary where the key is the username. E.g.
accounts:
admin:
password: hello123
belongs: group1
guest:
password: guest
group1:
As soon as the config is read HFS will encrypt passwords (if necessary) in a non-reversible way. It means that password
property is replaced with an encrypted property: srp
.
As you can see in the example, group1
has no password. This implies that you cannot log in as group1
, but still group1
exists and its purpose is to gather multiple accounts and refer to them collectively as group1
, so you can quickly share powers among several accounts.
For each account entries, this is the list of properties you can have:
ignore_limits
to ignore speed limits. Default is false
.redirect
provide a URL if you want the user to be redirected upon login. Default is none.admin
set true
if you want to let this account log in to the Admin-panel. Default is false
.belongs
an array of usernames of other accounts from which to inherit their permissions. Default is none.Author: Rejetto
Source Code: https://github.com/rejetto/hfs
License: GPL-3.0 license
1677909420
Although not very often, there are times when you need to find out how many files are in a given directory. For example, if you run out of inodes on your Linux system, you’ll need to find which directory contains thousands or millions of files.
In this article, we will show you several different ways to find the number of files in a directory in Linux.
The simplest way to count files in a directory is to list one file per line with ls
and pipe the output to wc
to count the lines:
ls -1U DIR_NAME | wc -l
The command above will give you a sum of all files, including directories and symlinks. The -1
option means list one file per line and -U
tells ls
to do not sort the output which makes the execution of the command faster.
ls -1U
command doesn’t count hidden files (dotfiles).
If you want to count only files and not include the directories use the following:
ls -1Up DIR_NAME | grep -v / | wc -l
The -p
option forces ls
to append slash (/
) indicator to directories. The output is piped to the grep -v
command that exclude the directories.
To have more control over what files are listed, use the find
command instead of ls
:
find DIR_NAME -maxdepth 1 -type f | wc -l
-type f
option tells find
to list only files (including dotfiles), and -maxdepth 1
limit search to the first-level directory.
To recursively count files in directory run the find
command as follows:
find DIR_NAME -type f | wc -l
Another command that can be used to count files is tree
that lists contents of directories in a tree-like format:
tree DIR_NAME
The last line of output will show the total number of files and directories listed:
15144 directories, 91311 files
We have shown you how to count files in directory using the ls
, find
and tree
commands.
Original article source at: https://linuxize.com/
1677824721
Utilities to read/write extended and legacy GSLIB files in Julia.
The GSLIB file format was introduced a long time ago for storing geospatial data over Cartesian grids or point sets in text files that are easy to read. Unfortantely, the format specification is incomplete:
(nx, ny, nz)
)(ox, oy, oz)
, (sx, sy, sz)
)-999
)This package introduces an extended GSLIB format that addresses these issues. It also provides helper functions to load data in legacy format.
Get the latest stable release with Julia's package manager:
] add GslibIO
Please use save
and load
for the extended GSLIB file format and save_legacy
and load_legacy
for the legacy GSLIB file format. Consult the docstring of each function for more information.
An usual workflow consists of loading a legacy file with load_legacy
by setting the options manually, and then saving the data back to disk in extended format with save
. The new extended format can then be loaded without human intervention.
using GslibIO
# load grid data stored in legacy format
data = GslibIO.load_legacy("legacy.gslib", (100,100,50), na=-999)
# save grid data in new extended format
GslibIO.save("extended.gslib", data)
# now it can be loaded without special options
GslibIO.load("extended.gslib")
Author: JuliaEarth
Source Code: https://github.com/JuliaEarth/GslibIO.jl
License: MIT license
1677091800
A Comma-Separated Values (CSV) file is a plain text file that uses a comma as a delimiter to separate values. It stores data in a tabular format where each row consists of one or more fields, and each column represents a specific field.
CSV is a widely used data exchange format in the industry due to its simplicity and better integration with existing applications. These files are usually used for exporting and importing large data sets.
In this tutorial, we shall learn how to export and download the data as a CSV file in a Spring Boot project. Data export (JSON, CSV, PDF, etc.) is a common feature implemented in many Java enterprise applications.
Since Java does not provide native support for creating and parsing CSV files, we shall use OpenCSV, a 3rd-party library for parsing and creating CSV files.
Here is what our build.gradle file looks like:
build.gradle
plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.attacomsian'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.opencsv:opencsv:4.5'
}
If you are working with a maven project, make sure you include the following maven dependency to the project's pom.xml file:
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>4.5</version>
</dependency>
Here is our User model class being used to write to a CSV file.
User.java
package com.attacomsian.exportcsv.data;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;
public class User {
private long id;
private String name;
private String email;
private String country;
private int age;
public User(long id, String name, String email, String country, int age) {
this.id = id;
this.name = name;
this.email = email;
this.country = country;
this.age = age;
}
// getters and setters removed for the sake of brevity
}
Since we want to generate a CSV file from a list of users and then return it to the client for downloading, let's create a dummy service that acts as a data source and returns a list of users.
UserService.java
package com.attacomsian.exportcsv.data;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserService {
public List<User> listUsers() {
List<User> users = new ArrayList<>();
//create dummy users
users.add(new User(1, "Jack Lee", "jack@example.com", "Germany", 35));
users.add(new User(2, "Jovan Srovoki", "jovan@srovoki.me", "Russia", 21));
users.add(new User(3, "Atta", "atta@gmail.com", "Pakistan", 29));
return users;
}
}
The UserService above is just for demo purposes. You may want to populate a list of users from the database or any other source.
The following Spring MVC controller class handles the export and download of data as a CSV file.
UserController.java
package com.attacomsian.exportcsv.controllers;
import com.attacomsian.exportcsv.data.User;
import com.attacomsian.exportcsv.data.UserService;
import com.opencsv.CSVWriter;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletResponse;
@Controller
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/export-users")
public void exportCSV(HttpServletResponse response) throws Exception {
//set file name and content type
String filename = "users.csv";
response.setContentType("text/csv");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"");
//create a csv writer
StatefulBeanToCsv<User> writer = new StatefulBeanToCsvBuilder<User>(response.getWriter())
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.withSeparator(CSVWriter.DEFAULT_SEPARATOR)
.withOrderedResults(false)
.build();
//write all users to csv file
writer.write(userService.listUsers());
}
}
The above UserController class contains an exportCSV() method that is mapped to /export-users HTTP route and returns a CSV file as attachment for browser to download. This method does the following:
Below is the main application class used for running the Spring Boot project:
Application.java
package com.attacomsian.exportcsv;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Let's run the application by typing the following command in your terminal from the root directory of the project:
$ ./gradlew bootRun
After the Spring Boot application is started, open http://localhost:8080/export-users link in your favorite browser to generate and download the users.csv file. Here is the content of the generated CSV file:
users.csv
age,country,email,id,name
35,Germany,jack@example.com,1,Jack Lee
21,Russia,jovan@srovoki.me,2,Jovan Srovoki
29,Pakistan,atta@gmail.com,3,Atta
Notice the first line. OpenCSV automatically generated column headers using User class members. Another thing to note is the order of the columns in the CSV file. OpenCSV sorts the column names in ascending order before writing them into the CSV file.
There is no built-in functionality in OpenCSV that allows writing bean to CSV with custom column names and ordering. However, using the @CsvBindByPosition annotation, you can control the column positions in the generated CSV file. But the downside of this annotation is that it removes column headers from the generated CSV file.
public class User {
@CsvBindByPosition(position = 0)
private long id;
@CsvBindByPosition(position = 1)
private String name;
@CsvBindByPosition(position = 2)
private String email;
@CsvBindByPosition(position = 3)
private String country;
@CsvBindByPosition(position = 4)
private int age;
//constructor, getting and settings
}
@CsvBindByPosition specifies a binding between a column number of the CSV file and a field in a bean. This column number is zero-based (means position starts from 0).
Source code: Download the complete source code from GitHub available under MIT license.
That's all for explaining the usage of the OpenCSV library to generate and download a CSV file in Spring Boot. If you are uncomfortable using a 3rd-party library, you can write your own CSV writer. Writing a CSV file is similar to writing a text file with few exceptions.
Original article source at: https://attacomsian.com/
1677087660
In this article, you will learn how to convert Textarea Text to File using JavaScript. This is a great project for beginners. There is a box here, you can convert the contents of that box into a file. Many times in a project you need to save the text content to the file later. In that case, you can use this JavaScript Create and Save text file project.
We usually use Notepad when we want to create any kind of files like HTML, CSS, or text. This project will make that task much easier. There is a small box in which you can input some text or information.
Then there is another small box where you can use the rename of your choice. Then there is a download button that will save the file by clicking on it.
I used JavaScript, HTML, and CSS to create this project. First, we created its basic structure using HTML CSS. Then I implemented this project (Create and Save the text file in JavaScript) using JavaScript.
First I designed the webpage and made a box on it. I added a heading at the beginning of this box. The h1 tag has been used to enhance this heading. Then I created a box using HTML’s Textarea. In this box, you can input some text. Then create another input box in which you can add the rename of the file of your choice. Here you can create any type of file.
If you open this Create and save a file project with a browser, your content will be downloaded automatically. If you open it using a code editor, you will be asked for permission to download the file.
To build it you need to have a basic idea about HTML CSS and JavaScript. The video below will help you learn how it works.
You can also use the demo section above to know How to save form data in a Text file using JavaScript. If you just want the source code, you can use the download button at the bottom of the article. However, I request you to follow the step-by-step tutorials below.
Below I have shown step-by-step how to save text to a client-side file using JavaScript. Hereafter each step I have given possible results which will help you to understand better.
I have created the basics of this project (Create and Save the text file in JavaScript) using the following HTML and CSS code.
A box has been created here with width: 430px and the background color is white. Here I have used the background-color blue of the webpage.
<div id=”container”>
</div>
* {
box-sizing: border-box;
}
body {
height: 100vh;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: #044b82;
font-family: “Kanit”, Verdana, Arial, sans-serif;
}
#container {
width: 430px;
padding: 40px 20px;
background: white;
}
Now I have added a heading that will be at the very beginning of this box. HTML’s h1 tag has been used to create this heading. I used font-size: 30px and color blue to increase the text size.
<h1>Save the text to a file</h1>
h1 {
color: #0773d7;
font-size: 30px;
width: 100%;
margin-top: -15px;
margin-bottom: 30px;
text-align: center;
text-transform: uppercase;
}
Now a box has been created to input the text or contents. This box has been created with the help of HTML Textarea.
<textarea placeholder=”Type your text here…” id=”text”></textarea>
#text {
display: block;
width: 100%;
background-color: transparent;
color: #021652;
border: 2px solid #3ba9f4;
border-radius: 2px;
resize: none;
margin-bottom: 35px;
height: 200px;
padding: 10px;
font-size: 20px;
}
Now a box has been created to input the file name. This box is created using the input function of HTML. The width of the box is 100% and the height of the box is 50px.
<input id=”filename” placeholder=”Specify a filename…” />
#filename {
width: calc(100% – 200px);
border: 2px solid #3ba9f4;
border-radius: 2px;
background-color: transparent;
color: #052a53;
padding: 0 10px;
height: 50px;
line-height: 50px;
font-size: 20px;
margin-right: 20px;
}
Now it needs to create a download button. Clicking on this button will download all the text files. Box width: 174px, height: 50px and background color blue has been used.
<button id=”download”>Download file</button>
#download {
background-color: #3ba9f4;
color: #fff;
font-size: 20px;
height: 50px;
border: none;
border-radius: 2px;
width: 174px;
cursor: pointer;
}
This project has been implemented using the following JavaScript. If you know Basic JavaScript, you can easily create this project (Save Textarea Text to a File using JavaScript).
Here I have put the necessary information at the top of each line why I used that code. I hope the following explanations will help you to understand this JavaScript.
function downloadFile(filename, content) {
// It works on all HTML5 Ready browsers as it uses the download attribute of the <a> element:
const element = document.createElement(‘a’);
//A blob is a data type that can store binary data
// “type” is a MIME type
// It can have a different value, based on a file you want to save
const blob = new Blob([content], { type: ‘plain/text’ });
//createObjectURL() static method creates a DOMString containing a URL representing the object given in the parameter.
const fileUrl = URL.createObjectURL(blob);
//setAttribute() Sets the value of an attribute on the specified element.
element.setAttribute(‘href’, fileUrl); //file location
element.setAttribute(‘download’, filename); // file name
element.style.display = ‘none’;
//use appendChild() method to move an element from one element to another
document.body.appendChild(element);
element.click();
//The removeChild() method of the Node interface removes a child node from the DOM and returns the removed node
document.body.removeChild(element);
};
window.onload = () => {
document.getElementById(‘download’).
addEventListener(‘click’, e => {
//The value of the file name input box
const filename = document.getElementById(‘filename’).value;
//The value of what has been input in the textarea
const content = document.getElementById(‘text’).value;
// The && (logical AND) operator indicates whether both operands are true. If both operands have nonzero values, the result has the value 1 . Otherwise, the result has the value 0.
if (filename && content) {
downloadFile(filename, content);
}
});
};
If there is any problem then, of course, you can follow the video tutorial. Hopefully, the above tutorial has helped you to know how to convert text to file using JavaScript.
If there is any difficulty then you can definitely let me know by commenting. If you like this project (How to save textbox value to file) then be sure to share it with your friends.
Original article source at: https://foolishdeveloper.com/
1676972880
Linux file encryption involves rewriting the plaintext documents into a format that can only be accessed by those with the right password or decryption key. This is done to prevent the unauthorized access to delicate information. Linux supports a number of encryption methods including symmetric encryption which employs the very same key for encryption and decryption, filesystem-level encryption, and public-private key asymmetric encryption. Linux users frequently use GnuPG, OpenSSL, and dm-crypt as encryption tools. In this guide, we will use the GPG utility to encrypt the Linux files and decrypt them.
The first thing to do is to update your system before the installation of any utility. The following command updates the package list on an Ubuntu system. Using the “sudo” command, the “omar” user runs the command with superuser rights. The system is connected to a package repository at “http://pk.archive.ubuntu.com/ubuntu” and is looking for updates.
omar@virtualbox:~$ sudo apt-get update
The GPG is the tool that is used in Linux to encrypt the files. The following command is used to install the “gpg” (GNU Privacy Guard) software package on the system. Run the command with administrative rights by typing “sudo.” A utility to manage the packages on Debian-based computers is called “apt-get.” The “install” command instructs the apt-get to set up the chosen “gpg” package. The output shows that the “gpg” package is already installed on the system and is the newest version (2.2.27-3ubuntu2.1). The package is also set to be manually installed which means that it is installed by a user rather than being a dependency of another package. The output also states that two packages, “libflashrom1” and “libftdi1-2”, are no longer required and can be removed using the “apt autoremove” command. Lastly, the output states that there are 0 upgraded packages, 0 newly installed packages, 0 packages to be removed, and 37 un-upgraded packages. No changes are made to the system’s packages and all packages are up-to-date.
omar@virtualbox:~$ sudo apt-get install gpg
To encrypt in Linux, we should have a file with some important content in it. Thus, we use the “touch” instruction to create a new file in the current working directory which is pass.txt.
omar@virtualbox:~$ touch pass.txt
The “ls” query of Linux shows that the newly created “pass.txt” file is listed in the current working directory.
omar@virtualbox:~$ ls
Desktop Downloads new Pictures snap Videos
Documents Music pass.txt Public Templates
Make sure to add some content to your newly made file. We also add some information regarding our system user in the “pass.txt” file by manually opening it. The “Cat” instruction can be used to display the contents of any sort of file as shown in the attached command and output:
omar@virtualbox:~$ cat pass.txt
Password: Omar
The “gpg -c pass.txt” command uses the GNU Privacy Guard (GPG) tool to encrypt a file called “pass.txt” using the symmetric-key encryption. The “-c” option tells GPG to use the symmetric-key encryption and prompts the user to enter any passphrase to use as the encryption key. The encrypted file is created using the same title as the original file and the “.gpg” file extension.
omar@virtualbox:~$ gpg -c pass.txt
The dialog box appears on your screen which prompts you to enter the passphrase as shown in the image. We add the passphrase and tapp the “OK” button:
The very next screen shows a warning if you enter an insecure passphrase. Choose the “Take this one anyway” option to proceed.
The list instruction displays the encrypted “pass.txt.gpg” file which is listed with the other files in the current directory.
omar@virtualbox:~$ ls
Desktop Downloads new pass.txt.gpg Public Templates
Documents Music pass.txt Pictures snap Videos
The “file” instruction is applied to establish the type of a “pass.txt.gpg” file based on its contents, rather than its file name or file extension. The output indicates that the “pass.txt.gpg” file is a GPG symmetrically encrypted file, and it’s encrypted using the AES256 cipher. The AES256 cipher is a symmetric key encryption algorithm. AES (Advanced Encryption Standard) is a widely used encryption standard, and 256 refers to the key size which means that it has a key size of 256-bit.
omar@virtualbox:~$ file pass.txt.gpg
pass.txt.gpg: GPG symmetrically encrypted data (AES256 cipher)
Now, when you try to display the contents of an encrypted “pass.txt.gpg” file, we should get the following output using the “Cat” instruction along with the file name:
omar@virtualbox:~$ cat pass.txt.gpg
� ��7$�Z$��K��^��On���
����k.�K�{��dE�֛_���$�
��6ay�jȚ�N:�*�w�:�껎~��4j
After the encryption of a “pass.txt” file to a new file, there is no need to remain using the original file which is the pass.txt. Therefore, we remove it using the “rm” instruction.
omar@virtualbox:~$ rm pass.txt
It’s time to decrypt the original data from the encrypted “pass.txt.gpg” file. For this, we don’t need the original “pass.txt” file here since we already deleted it as per the “ls” command.
omar@virtualbox:~$ ls
Desktop Downloads new Pictures snap Videos
Documents Music pass.txt.gpg Public Templates
To decrypt the encrypted “pass.txt.gpg” file, we cast off the following “gpg” instruction on the shell. It uses the “>” operator to pass the decrypted content to a “pass.txt” file that uses the GNU Privacy Guard (GPG) tool to decrypt a “pass.txt.gpg” file using symmetric-key decryption. The “–decrypt” option tells GPG to perform the decryption, and the “> pass.txt” redirects the output of the decryption process to a file called “pass.txt”.
The first output line indicates that the data in the pass.txt.gpg file is encrypted using the AES256 cipher in CFB(Cipher Feedback) mode. The second output line, “gpg: encrypted with 1 passphrase”, indicates that the pass.txt.gpg file is encrypted with a single passphrase. A passphrase is a sequence of words or other text that is used to encrypt a file, and it is required to decrypt the file.
omar@virtualbox:~$ gpg --decrypt pass.txt.gpg > pass.txt
gpg: AES256.CFB encrypted data
gpg: encrypted with 1 passphrase
Now, we have a pass.txt file back in the current working directory as per the following “ls” instruction output:
omar@virtualbox:~$ ls
Desktop Downloads new pass.txt Pictures snap Videos
Documents Music pass.txt.gpg Public Templates
When you try to display the content of a pass.txt file on the Linux shell using the cat instruction, it displays the original content before the encryption of a file.
omar@virtualbox:~$ cat pass.txt
Password: Omar
The introduction demonstrates the use of encryption in Linux systems and discusses its types as well. To support the topic, we installed the GPG utility that is specifically designed for the encryption of Linux-based files. After its installation, we generated a simple text file and encrypted it using the “gpg” utility and added the passphrase for encryption as an example. Lastly, we tried the GPG utility to decrypt a file to its original form and display the original content on the shell.
Original article source at: https://linuxhint.com/
1676774280
To read a text file into an array in Node.js:
const fsPromises = require('fs/promises')
const readFileAsync = async () => {
try {
const contents = await fsPromises.readFile('file.txt', 'utf-8')
const arr = contents.split(/\r?\n/)
console.log(arr)
// [ 'this', 'is', 'an', 'example two two', 'text!' ]
} catch (err) {
console.error(err)
}
}
readFileAsync()
The fsPromises.readFile() method takes the path to the file as the first parameter and the encoding as the second. It asynchronously reads the contents of the given file.
If you skip the encoding parameter, fsPromises.readFile() will return a buffer. Otherwise, a string is returned.
We used the String.split() method to split the file contents on each newline character. We did the same for reading a file line by line in Node.js.
In case you need to read the file contents synchronously, use the fs.readFileSync() method instead:
const fs = require('fs')
const contents = fs.readFileSync('file.txt', 'utf-8')
const arr = contents.split(/\r?\n/)
console.log(arr)
// [ 'this', 'is', 'an', 'example two two', 'text!' ]
Original article source at: https://attacomsian.com/
1676660580
When you compile C and C++ code using GCC, you might see the following error:
fatal error: Python.h: No such file or directory
This error usually occurs when you add the Python header file to your C code, but that file can’t be found on your system.
This tutorial shows examples that cause this error and how you can fix it.
Suppose you have a file named main.c
with the following content:
#include<Python.h>
#include<stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
You instructed the program to include the Python.h
header file, then compile it using GCC:
gcc main.c
When GCC can’t find the Python.h
file in your system, it shows the following error:
main.c:1:9: fatal error: Python.h: No such file or directory
1 | #include<Python.h>
| ^~~~~~~~~~
compilation terminated.
To fix this error, you need to make GCC able to find the Python header file.
If you use Ubuntu OS, the Python.h
file is included in the python-dev
package.
You can use one of the following commands to install the python-dev
package.
If you have Python 3, then install the python3-dev
package:
#For apt (Ubuntu, Debian...):
sudo apt-get install python-dev
sudo apt-get install python3-dev
#For yum (CentOS, RHEL...):
sudo yum install python-devel
sudo yum install python3-devel
# For dnf (Fedora...):
sudo dnf install python2-devel
sudo dnf install python3-devel
#For zypper (openSUSE...):
sudo zypper in python-devel
sudo zypper in python3-devel
# For apk (Alpine...):
sudo apk add python2-dev
sudo apk add python3-dev
#For apt-cyg (Cygwin...):
apt-cyg install python-devel
apt-cyg install python3-devel
Once you install the Python-dev package, you could find the Python.h
file in /usr/include/pythonx.xx
with x.xx
being the version of the Python you installed.
You can also use the locate
command to find the file:
$ locate Python.h
/usr/include/python3.10/Python.h
Now that you have the location of Python.h
, you need to include the path to that file by adding the -I
flag when running GCC.
Here’s an example:
gcc main.c -I/usr/include/python3.10
Notice you no longer get the fatal error. You can run the output file a.out
to run the compiled C program.
If you use macOS, then the header file should be included when you install Python on your computer.
If you use Homebrew to install Python, the header file should be somewhere in /opt/homebrew/Cellar/
for Apple Silicon Macs or in /usr/local/Cellar
for Intel Macs.
I found my header file in /opt/homebrew/Cellar/python@3.10/3.10.10/Frameworks/Python.framework/Versions/3.10/include/python3.10
.
You can use the Search bar in Finder to find the file. Find the location by right-clicking on the file and select Get Info.
You can highlight and copy the path shown in the Where info.
Next, you need to include the path when compiling your code with GCC like this:
gcc main.c -I/opt/homebrew/Cellar/python@3.10/3.10.10/Frameworks/Python.framework/Versions/3.10/include/python3.10
The compilation no longer gives a fatal error, and you can run the compiled program.
The fatal error: Python.h: No such file or directory
occurs when GCC can’t find the Python.h
file you included in your code.
You might not have the python-dev
package installed on your system, or the path to Python.h
file isn’t included in the PATH variable.
You can add the -I
flag when compiling with GCC to include the path to the directory that holds the header file.
Original article source at: https://sebhastian.com/
1674820980
File uploads are a common feature in web applications. It allows users to upload and share files, such as images, videos, and documents.
CakePHP 4 already has built-in File handling classes to handle file uploads.
In this tutorial, I show how you can upload file with validation and display its preview after upload in CakePHP 4.
In the example, I am using Modelless form for creating an upload form.
UploadfileForm.php
file in src/Form/
folder. Create Form
folder if it does not exist.UploadfileForm
class.Create 3 methods –
<form >
element from here.<form >
validation. Here, fileel
is the file element name.Set it as required, and add rules for file type and file size.
Assign file instance to $attachment
. Read file details and assign them to the variables.
If $error == 0
then check if uploads
folder exists in webroot
or not. If not exists then create it.
Upload the file and return true
.
NOTE – Uploaded file will be stored in
webroot/uploads/
folder.
Completed Code
<?php
namespace App\Form;
use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;
use Cake\Filesystem\Folder;
class UploadfileForm extends Form
{
protected function _buildSchema(Schema $schema): Schema
{
return $schema;
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyFile('fileel')
->add('fileel', [
'mimeType' => [
'rule' => ['mimeType',['image/jpg','image/png','image/jpeg','application/pdf']],
'message' => 'File type must be .jpg,.jpeg,.png,.pdf',
],
'fileSize' => [
'rule' => ['fileSize','<', '2MB'],
'message' => 'File size must be less than 2MB',
]
]);
return $validator;
}
protected function _execute(array $data): bool
{
$attachment = $data['fileel'];
// File details
$filename = $attachment->getClientFilename();
$type = $attachment->getClientMediaType();
$size = $attachment->getSize();
$extension = pathinfo($filename, PATHINFO_EXTENSION);
$tmpName = $attachment->getStream()->getMetadata('uri');
$error = $attachment->getError();
// Upload file
if($error == 0){
$location = WWW_ROOT . 'uploads' . DS;
$folder = new Folder();
$checkfolder = $folder->inPath($location, true);
if(!$checkfolder){ // Not exists
if (!$folder->create($location)) {
return false;
}
}
$targetPath = $location.$filename;
$attachment->moveTo($targetPath);
return true;
}else{
return false;
}
}
}
FileuploadController.php
file in src/Controller/
folder.FileuploadController
Class that extends AppController
.App\Form\UploadfileForm
.Here, create 1 method –
UploadfileForm()
.If <form >
is POST then call $uploadfile->execute()
where pass POST data. If it returns true
then read file element data for preview.
Assign $filepath
to $filedetails['filepath']
and $extension
to $filedetails['extension']
.
Set filedetails
and uploadfile
.
Completed Code
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Form\UploadfileForm;
class FileuploadController extends AppController
{
public function index(){
$uploadfile = new UploadfileForm();
$filedetails = array();
if ($this->request->is('post')) {
if ($uploadfile->execute($this->request->getData())) {
// Read file for preview
$attachment = $this->request->getData('fileel');
$filename = $attachment->getClientFilename();
$extension = pathinfo($filename, PATHINFO_EXTENSION);
$filepath = "/uploads/".$filename;
$extension = $extension;
$filedetails['filepath'] = $filepath;
$filedetails['extension'] = $extension;
$this->Flash->success('File uploaded successfully.');
} else {
$this->Flash->error('File not uploaded.');
}
}
$this->set('filedetails', $filedetails);
$this->set('uploadfile', $uploadfile);
}
}
Create a new Fileupload
folder in templates/
folder. Now in the Fileupload
folder create index.php
file.
Create a <form >
set its action to fileupload/index
. Pass $uploadfile
as 1st parameter in create()
. Here, $uploadfile
is an instance of UploadfileForm
Class it is set from the controller.
In the <form >
create a file element and a submit button.
To display a preview check if $filedetails
Array is empty or not.
If not empty then check $filedetails['extension']
extension value. If the extension is image type then create <img >
element to display file otherwise create <a >
tag to create view link.
Completed Code
<div class="row">
<div class="col-6">
<?php
// Upload form
echo $this->Form->create($uploadfile,array('url'=>['controller' => 'fileupload','action' => 'index'],"enctype" => "multipart/form-data" ));
echo $this->Form->control('fileel',['label' => 'Select file','type' => 'file','class' => 'form-control','required' => true]);
echo $this->Form->button('Submit');
echo $this->Form->end();
?>
<?php
// Preview file
if(count($filedetails) > 0){
$image_exts = array("jpg","jpeg","png");
if(in_array($filedetails['extension'],$image_exts)){
echo $this->HTML->image($filedetails['filepath']);
}else{
echo $this->Html->link(
'View file',
$filedetails['filepath'],
['target' => '_blank']
);
}
}
?>
</div>
</div>
http://localhost:8765/fileupload
If you also want to save the file to the database then you don’t need to create a Modelless Form class. You need to pass ORM Entity in form and handle it in the controller.
If you found this tutorial helpful then don't forget to share.
Original article source at: https://makitweb.com/
1673474280
In JavaScript FileReader
class is available to read the data of the file. Using this you can read and view the file content before uploading it to the server.
In this tutorial, I show how you can use FileReader
class to read CSV file and display its content on the page using JavaScript.
In the example, I am using the following file structure –
S.no,Username,Name,Email
1,yssyogesh,Yogesh singh,yogesh@makitweb.com
2,bsonarika,Sonarika Bhadoria,bsonarika@makitweb.com
3,vishal,Vishal Sahu,vishal@makitweb.com
4,anilsingh,Anil singh,anilsingh@makitweb.com
Create a file element and a button. Using <table >
to list selected CSV file data. Add onclick
event on the button. It calls readCSVFile()
function.
Completed Code
<!-- CSS -->
<style type="text/css">
table th, table td{
padding: 5px;
}
</style>
<div>
<div>
<input type="file" name="file" id="file" accept=".csv" > <br><br>
<input type="button" id="btnsubmit" value="Submit" onclick="readCSVFile();" >
</div>
<br><br>
<!-- List CSV file data -->
<table id="tblcsvdata" border="1" style="border-collapse: collapse;">
<thead>
<tr>
<th>S.no</th>
<th>Username</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
Create readCSVFile()
function that calls on button click. Check if a file is selected or not.
Read CSV file and Display data –
FileReader
Class if a file is selected.readAsText()
to read its data as a string.onload
event on the reader
object. It calls when the file successfully gets read.result
property to get file data. Assign the data to csvdata
variable.<table > <tbody >
where need to append data.rowData
Array. Here, for not reading 1st row I assigned 1
to row
variable but you can assign 0
if you want to read 1st row.<tr>
– tbodyEl.insertRow()
.rowColData
Array to read column data. Create a cell and add column value – rowColData[col]
.Completed Code
function readCSVFile(){
var files = document.querySelector('#file').files;
if(files.length > 0 ){
// Selected file
var file = files[0];
// FileReader Object
var reader = new FileReader();
// Read file as string
reader.readAsText(file);
// Load event
reader.onload = function(event) {
// Read file data
var csvdata = event.target.result;
// Split by line break to gets rows Array
var rowData = csvdata.split('\n');
// <table > <tbody>
var tbodyEl = document.getElementById('tblcsvdata').getElementsByTagName('tbody')[0];
tbodyEl.innerHTML = "";
// Loop on the row Array (change row=0 if you also want to read 1st row)
for (var row = 1; row < rowData.length; row++) {
// Insert a row at the end of table
var newRow = tbodyEl.insertRow();
// Split by comma (,) to get column Array
rowColData = rowData[row].split(',');
// Loop on the row column Array
for (var col = 0; col < rowColData.length; col++) {
// Insert a cell at the end of the row
var newCell = newRow.insertCell();
newCell.innerHTML = rowColData[col];
}
}
};
}else{
alert("Please select a file.");
}
}
You can use this code to display a preview of file data or you can also modify the code for applying validation.
Adjust the code according to your CSV file while implementing this on your project.
If you found this tutorial helpful then don't forget to share.
Original article source at: https://makitweb.com/
1673369705
Codeable
Model file Generator For Swift 5Version v2.2
Version v2.1
Version v2.0 (Swift 5)
Codeable
version along with CodingKeys
.Optional
and non-optional variations.Building:
pod install
You will also need to install SwiftFormat
with brew install swiftformat
and SwiftLint
with brew install swiftlint
.
Download dmg: Download the .app (v2.2.0)
SwiftyJSONAccelerator
can't be opened because Apple cannot check it for malicious software.: Run the following command xattr -d com.apple.quarantine <app-path>
.A Swift model generator like the Objective-C JSONAccelerator. Formats and generates models for the given JSON and also breaks them into files making it easy to manage and share between several models.
Codeable
feature making encoding and decoding objects a thing of the past.Load folder with JSON files + Config
to generate all possible models for given folder with JSON files, note this needs a .config.json
as this uses the CLI logic internally.Any suggestions regarding code quality of the app, generated code's quality, Swift related improvements and pull requests are all very welcome. Please make sure you submit the pull request to the next release branch and not the master branch.
Author: insanoid
Source Code: https://github.com/insanoid/SwiftyJSONAccelerator
License: MIT license
1673365703
The following is a collection of tips I find to be useful when working with the Swift language. More content is available on my Twitter account!
📣 NEW 📣 Swift Tips are now available on YouTube 👇
Tips
Property Wrappers allow developers to wrap properties with specific behaviors, that will be seamlessly triggered whenever the properties are accessed.
While their primary use case is to implement business logic within our apps, it's also possible to use Property Wrappers as debugging tools!
For example, we could build a wrapper called @History
, that would be added to a property while debugging and would keep track of all the values set to this property.
import Foundation
@propertyWrapper
struct History<Value> {
private var value: Value
private(set) var history: [Value] = []
init(wrappedValue: Value) {
self.value = wrappedValue
}
var wrappedValue: Value {
get { value }
set {
history.append(value)
value = newValue
}
}
var projectedValue: Self {
return self
}
}
// We can then decorate our business code
// with the `@History` wrapper
struct User {
@History var name: String = ""
}
var user = User()
// All the existing call sites will still
// compile, without the need for any change
user.name = "John"
user.name = "Jane"
// But now we can also access an history of
// all the previous values!
user.$name.history // ["", "John"]
String
interpolationSwift 5 gave us the possibility to define our own custom String
interpolation methods.
This feature can be used to power many use cases, but there is one that is guaranteed to make sense in most projects: localizing user-facing strings.
import Foundation
extension String.StringInterpolation {
mutating func appendInterpolation(localized key: String, _ args: CVarArg...) {
let localized = String(format: NSLocalizedString(key, comment: ""), arguments: args)
appendLiteral(localized)
}
}
/*
Let's assume that this is the content of our Localizable.strings:
"welcome.screen.greetings" = "Hello %@!";
*/
let userName = "John"
print("\(localized: "welcome.screen.greetings", userName)") // Hello John!
structs
If you’ve always wanted to use some kind of inheritance mechanism for your structs, Swift 5.1 is going to make you very happy!
Using the new KeyPath-based dynamic member lookup, you can implement some pseudo-inheritance, where a type inherits the API of another one 🎉
(However, be careful, I’m definitely not advocating inheritance as a go-to solution 🙃)
import Foundation
protocol Inherits {
associatedtype SuperType
var `super`: SuperType { get }
}
extension Inherits {
subscript<T>(dynamicMember keyPath: KeyPath<SuperType, T>) -> T {
return self.`super`[keyPath: keyPath]
}
}
struct Person {
let name: String
}
@dynamicMemberLookup
struct User: Inherits {
let `super`: Person
let login: String
let password: String
}
let user = User(super: Person(name: "John Appleseed"), login: "Johnny", password: "1234")
user.name // "John Appleseed"
user.login // "Johnny"
NSAttributedString
through a Function BuilderSwift 5.1 introduced Function Builders: a great tool for building custom DSL syntaxes, like SwiftUI. However, one doesn't need to be building a full-fledged DSL in order to leverage them.
For example, it's possible to write a simple Function Builder, whose job will be to compose together individual instances of NSAttributedString
through a nicer syntax than the standard API.
import UIKit
@_functionBuilder
class NSAttributedStringBuilder {
static func buildBlock(_ components: NSAttributedString...) -> NSAttributedString {
let result = NSMutableAttributedString(string: "")
return components.reduce(into: result) { (result, current) in result.append(current) }
}
}
extension NSAttributedString {
class func composing(@NSAttributedStringBuilder _ parts: () -> NSAttributedString) -> NSAttributedString {
return parts()
}
}
let result = NSAttributedString.composing {
NSAttributedString(string: "Hello",
attributes: [.font: UIFont.systemFont(ofSize: 24),
.foregroundColor: UIColor.red])
NSAttributedString(string: " world!",
attributes: [.font: UIFont.systemFont(ofSize: 20),
.foregroundColor: UIColor.orange])
}
switch
and if
as expressionsContrary to other languages, like Kotlin, Swift does not allow switch
and if
to be used as expressions. Meaning that the following code is not valid Swift:
let constant = if condition {
someValue
} else {
someOtherValue
}
A common solution to this problem is to wrap the if
or switch
statement within a closure, that will then be immediately called. While this approach does manage to achieve the desired goal, it makes for a rather poor syntax.
To avoid the ugly trailing ()
and improve on the readability, you can define a resultOf
function, that will serve the exact same purpose, in a more elegant way.
import Foundation
func resultOf<T>(_ code: () -> T) -> T {
return code()
}
let randomInt = Int.random(in: 0...3)
let spelledOut: String = resultOf {
switch randomInt {
case 0:
return "Zero"
case 1:
return "One"
case 2:
return "Two"
case 3:
return "Three"
default:
return "Out of range"
}
}
print(spelledOut)
guard
statementsA guard
statement is a very convenient way for the developer to assert that a condition is met, in order for the execution of the program to keep going.
However, since the body of a guard
statement is meant to be executed when the condition evaluates to false
, the use of the negation (!
) operator within the condition of a guard
statement can make the code hard to read, as it becomes a double negative.
A nice trick to avoid such double negatives is to encapsulate the use of the !
operator within a new property or function, whose name does not include a negative.
import Foundation
extension Collection {
var hasElements: Bool {
return !isEmpty
}
}
let array = Bool.random() ? [1, 2, 3] : []
guard array.hasElements else { fatalError("array was empty") }
print(array)
init
without loosing the compiler-generated oneIt's common knowledge for Swift developers that, when you define a struct
, the compiler is going to automatically generate a memberwise init
for you. That is, unless you also define an init
of your own. Because then, the compiler won't generate any memberwise init
.
Yet, there are many instances where we might enjoy the opportunity to get both. As it turns out, this goal is quite easy to achieve: you just need to define your own init
in an extension
rather than inside the type definition itself.
import Foundation
struct Point {
let x: Int
let y: Int
}
extension Point {
init() {
x = 0
y = 0
}
}
let usingDefaultInit = Point(x: 4, y: 3)
let usingCustomInit = Point()
enum
Swift does not really have an out-of-the-box support of namespaces. One could argue that a Swift module can be seen as a namespace, but creating a dedicated Framework for this sole purpose can legitimately be regarded as overkill.
Some developers have taken the habit to use a struct
which only contains static
fields to implement a namespace. While this does the job, it requires us to remember to implement an empty private
init()
, because it wouldn't make sense for such a struct
to be instantiated.
It's actually possible to take this approach one step further, by replacing the struct
with an enum
. While it might seem weird to have an enum
with no case
, it's actually a very idiomatic way to declare a type that cannot be instantiated.
import Foundation
enum NumberFormatterProvider {
static var currencyFormatter: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.roundingIncrement = 0.01
return formatter
}
static var decimalFormatter: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.decimalSeparator = ","
return formatter
}
}
NumberFormatterProvider() // ❌ impossible to instantiate by mistake
NumberFormatterProvider.currencyFormatter.string(from: 2.456) // $2.46
NumberFormatterProvider.decimalFormatter.string(from: 2.456) // 2,456
Never
to represent impossible code pathsNever
is quite a peculiar type in the Swift Standard Library: it is defined as an empty enum enum Never { }
.
While this might seem odd at first glance, it actually yields a very interesting property: it makes it a type that cannot be constructed (i.e. it possesses no instances).
This way, Never
can be used as a generic parameter to let the compiler know that a particular feature will not be used.
import Foundation
enum Result<Value, Error> {
case success(value: Value)
case failure(error: Error)
}
func willAlwaysSucceed(_ completion: @escaping ((Result<String, Never>) -> Void)) {
completion(.success(value: "Call was successful"))
}
willAlwaysSucceed( { result in
switch result {
case .success(let value):
print(value)
// the compiler knows that the `failure` case cannot happen
// so it doesn't require us to handle it.
}
})
Decodable
enum
Swift's Codable
framework does a great job at seamlessly decoding entities from a JSON stream. However, when we integrate web-services, we are sometimes left to deal with JSONs that require behaviors that Codable
does not provide out-of-the-box.
For instance, we might have a string-based or integer-based enum
, and be required to set it to a default value when the data found in the JSON does not match any of its cases.
We might be tempted to implement this via an extensive switch
statement over all the possible cases, but there is a much shorter alternative through the initializer init?(rawValue:)
:
import Foundation
enum State: String, Decodable {
case active
case inactive
case undefined
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decodedString = try container.decode(String.self)
self = State(rawValue: decodedString) ?? .undefined
}
}
let data = """
["active", "inactive", "foo"]
""".data(using: .utf8)!
let decoded = try! JSONDecoder().decode([State].self, from: data)
print(decoded) // [State.active, State.inactive, State.undefined]
Dependency injection boils down to a simple idea: when an object requires a dependency, it shouldn't create it by itself, but instead it should be given a function that does it for him.
Now the great thing with Swift is that, not only can a function take another function as a parameter, but that parameter can also be given a default value.
When you combine both those features, you can end up with a dependency injection pattern that is both lightweight on boilerplate, but also type safe.
import Foundation
protocol Service {
func call() -> String
}
class ProductionService: Service {
func call() -> String {
return "This is the production"
}
}
class MockService: Service {
func call() -> String {
return "This is a mock"
}
}
typealias Provider<T> = () -> T
class Controller {
let service: Service
init(serviceProvider: Provider<Service> = { return ProductionService() }) {
self.service = serviceProvider()
}
func work() {
print(service.call())
}
}
let productionController = Controller()
productionController.work() // prints "This is the production"
let mockedController = Controller(serviceProvider: { return MockService() })
mockedController.work() // prints "This is a mock"
Singletons are pretty bad. They make your architecture rigid and tightly coupled, which then results in your code being hard to test and refactor. Instead of using singletons, your code should rely on dependency injection, which is a much more architecturally sound approach.
But singletons are so easy to use, and dependency injection requires us to do extra-work. So maybe, for simple situations, we could find an in-between solution?
One possible solution is to rely on one of Swift's most know features: protocol-oriented programming. Using a protocol
, we declare and access our dependency. We then store it in a private singleton, and perform the injection through an extension of said protocol
.
This way, our code will indeed be decoupled from its dependency, while at the same time keeping the boilerplate to a minimum.
import Foundation
protocol Formatting {
var formatter: NumberFormatter { get }
}
private let sharedFormatter: NumberFormatter = {
let sharedFormatter = NumberFormatter()
sharedFormatter.numberStyle = .currency
return sharedFormatter
}()
extension Formatting {
var formatter: NumberFormatter { return sharedFormatter }
}
class ViewModel: Formatting {
var displayableAmount: String?
func updateDisplay(to amount: Double) {
displayableAmount = formatter.string(for: amount)
}
}
let viewModel = ViewModel()
viewModel.updateDisplay(to: 42000.45)
viewModel.displayableAmount // "$42,000.45"
[weak self]
and guard
Callbacks are a part of almost all iOS apps, and as frameworks such as RxSwift
keep gaining in popularity, they become ever more present in our codebase.
Seasoned Swift developers are aware of the potential memory leaks that @escaping
callbacks can produce, so they make real sure to always use [weak self]
, whenever they need to use self
inside such a context. And when they need to have self
be non-optional, they then add a guard
statement along.
Consequently, this syntax of a [weak self]
followed by a guard
rapidly tends to appear everywhere in the codebase. The good thing is that, through a little protocol-oriented trick, it's actually possible to get rid of this tedious syntax, without loosing any of its benefits!
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
protocol Weakifiable: class { }
extension Weakifiable {
func weakify(_ code: @escaping (Self) -> Void) -> () -> Void {
return { [weak self] in
guard let self = self else { return }
code(self)
}
}
func weakify<T>(_ code: @escaping (T, Self) -> Void) -> (T) -> Void {
return { [weak self] arg in
guard let self = self else { return }
code(arg, self)
}
}
}
extension NSObject: Weakifiable { }
class Producer: NSObject {
deinit {
print("deinit Producer")
}
private var handler: (Int) -> Void = { _ in }
func register(handler: @escaping (Int) -> Void) {
self.handler = handler
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: { self.handler(42) })
}
}
class Consumer: NSObject {
deinit {
print("deinit Consumer")
}
let producer = Producer()
func consume() {
producer.register(handler: weakify { result, strongSelf in
strongSelf.handle(result)
})
}
private func handle(_ result: Int) {
print("🎉 \(result)")
}
}
var consumer: Consumer? = Consumer()
consumer?.consume()
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: { consumer = nil })
// This code prints:
// 🎉 42
// deinit Consumer
// deinit Producer
Asynchronous functions are a big part of iOS APIs, and most developers are familiar with the challenge they pose when one needs to sequentially call several asynchronous APIs.
This often results in callbacks being nested into one another, a predicament often referred to as callback hell.
Many third-party frameworks are able to tackle this issue, for instance RxSwift or PromiseKit. Yet, for simple instances of the problem, there is no need to use such big guns, as it can actually be solved with simple function composition.
import Foundation
typealias CompletionHandler<Result> = (Result?, Error?) -> Void
infix operator ~>: MultiplicationPrecedence
func ~> <T, U>(_ first: @escaping (CompletionHandler<T>) -> Void, _ second: @escaping (T, CompletionHandler<U>) -> Void) -> (CompletionHandler<U>) -> Void {
return { completion in
first({ firstResult, error in
guard let firstResult = firstResult else { completion(nil, error); return }
second(firstResult, { (secondResult, error) in
completion(secondResult, error)
})
})
}
}
func ~> <T, U>(_ first: @escaping (CompletionHandler<T>) -> Void, _ transform: @escaping (T) -> U) -> (CompletionHandler<U>) -> Void {
return { completion in
first({ result, error in
guard let result = result else { completion(nil, error); return }
completion(transform(result), nil)
})
}
}
func service1(_ completionHandler: CompletionHandler<Int>) {
completionHandler(42, nil)
}
func service2(arg: String, _ completionHandler: CompletionHandler<String>) {
completionHandler("🎉 \(arg)", nil)
}
let chainedServices = service1
~> { int in return String(int / 2) }
~> service2
chainedServices({ result, _ in
guard let result = result else { return }
print(result) // Prints: 🎉 21
})
Asynchronous functions are a great way to deal with future events without blocking a thread. Yet, there are times where we would like them to behave in exactly such a blocking way.
Think about writing unit tests and using mocked network calls. You will need to add complexity to your test in order to deal with asynchronous functions, whereas synchronous ones would be much easier to manage.
Thanks to Swift proficiency in the functional paradigm, it is possible to write a function whose job is to take an asynchronous function and transform it into a synchronous one.
import Foundation
func makeSynchrone<A, B>(_ asyncFunction: @escaping (A, (B) -> Void) -> Void) -> (A) -> B {
return { arg in
let lock = NSRecursiveLock()
var result: B? = nil
asyncFunction(arg) {
result = $0
lock.unlock()
}
lock.lock()
return result!
}
}
func myAsyncFunction(arg: Int, completionHandler: (String) -> Void) {
completionHandler("🎉 \(arg)")
}
let syncFunction = makeSynchrone(myAsyncFunction)
print(syncFunction(42)) // prints 🎉 42
Closures are a great way to interact with generic APIs, for instance APIs that allow to manipulate data structures through the use of generic functions, such as filter()
or sorted()
.
The annoying part is that closures tend to clutter your code with many instances of {
, }
and $0
, which can quickly undermine its readably.
A nice alternative for a cleaner syntax is to use a KeyPath
instead of a closure, along with an operator that will deal with transforming the provided KeyPath
in a closure.
import Foundation
prefix operator ^
prefix func ^ <Element, Attribute>(_ keyPath: KeyPath<Element, Attribute>) -> (Element) -> Attribute {
return { element in element[keyPath: keyPath] }
}
struct MyData {
let int: Int
let string: String
}
let data = [MyData(int: 2, string: "Foo"), MyData(int: 4, string: "Bar")]
data.map(^\.int) // [2, 4]
data.map(^\.string) // ["Foo", "Bar"]
userInfo
Dictionary
Many iOS APIs still rely on a userInfo
Dictionary
to handle use-case specific data. This Dictionary
usually stores untyped values, and is declared as follows: [String: Any]
(or sometimes [AnyHashable: Any]
.
Retrieving data from such a structure will involve some conditional casting (via the as?
operator), which is prone to both errors and repetitions. Yet, by introducing a custom subscript
, it's possible to encapsulate all the tedious logic, and end-up with an easier and more robust API.
import Foundation
typealias TypedUserInfoKey<T> = (key: String, type: T.Type)
extension Dictionary where Key == String, Value == Any {
subscript<T>(_ typedKey: TypedUserInfoKey<T>) -> T? {
return self[typedKey.key] as? T
}
}
let userInfo: [String : Any] = ["Foo": 4, "Bar": "forty-two"]
let integerTypedKey = TypedUserInfoKey(key: "Foo", type: Int.self)
let intValue = userInfo[integerTypedKey] // returns 4
type(of: intValue) // returns Int?
let stringTypedKey = TypedUserInfoKey(key: "Bar", type: String.self)
let stringValue = userInfo[stringTypedKey] // returns "forty-two"
type(of: stringValue) // returns String?
MVVM is a great pattern to separate business logic from presentation logic. The main challenge to make it work, is to define a mechanism for the presentation layer to be notified of model updates.
RxSwift is a perfect choice to solve such a problem. Yet, some developers don't feel confortable with leveraging a third-party library for such a central part of their architecture.
For those situation, it's possible to define a lightweight Variable
type, that will make the MVVM pattern very easy to use!
import Foundation
class Variable<Value> {
var value: Value {
didSet {
onUpdate?(value)
}
}
var onUpdate: ((Value) -> Void)? {
didSet {
onUpdate?(value)
}
}
init(_ value: Value, _ onUpdate: ((Value) -> Void)? = nil) {
self.value = value
self.onUpdate = onUpdate
self.onUpdate?(value)
}
}
let variable: Variable<String?> = Variable(nil)
variable.onUpdate = { data in
if let data = data {
print(data)
}
}
variable.value = "Foo"
variable.value = "Bar"
// prints:
// Foo
// Bar
typealias
to its fullestThe keyword typealias
allows developers to give a new name to an already existing type. For instance, Swift defines Void
as a typealias
of ()
, the empty tuple.
But a less known feature of this mechanism is that it allows to assign concrete types for generic parameters, or to rename them. This can help make the semantics of generic types much clearer, when used in specific use cases.
import Foundation
enum Either<Left, Right> {
case left(Left)
case right(Right)
}
typealias Result<Value> = Either<Value, Error>
typealias IntOrString = Either<Int, String>
forEach
Iterating through objects via the forEach(_:)
method is a great alternative to the classic for
loop, as it allows our code to be completely oblivious of the iteration logic. One limitation, however, is that forEach(_:)
does not allow to stop the iteration midway.
Taking inspiration from the Objective-C implementation, we can write an overload that will allow the developer to stop the iteration, if needed.
import Foundation
extension Sequence {
func forEach(_ body: (Element, _ stop: inout Bool) throws -> Void) rethrows {
var stop = false
for element in self {
try body(element, &stop)
if stop {
return
}
}
}
}
["Foo", "Bar", "FooBar"].forEach { element, stop in
print(element)
stop = (element == "Bar")
}
// Prints:
// Foo
// Bar
reduce()
Functional programing is a great way to simplify a codebase. For instance, reduce
is an alternative to the classic for
loop, without most the boilerplate. Unfortunately, simplicity often comes at the price of performance.
Consider that you want to remove duplicate values from a Sequence
. While reduce()
is a perfectly fine way to express this computation, the performance will be sub optimal, because of all the unnecessary Array
copying that will happen every time its closure gets called.
That's when reduce(into:_:)
comes into play. This version of reduce
leverages the capacities of copy-on-write type (such as Array
or Dictionnary
) in order to avoid unnecessary copying, which results in a great performance boost.
import Foundation
func time(averagedExecutions: Int = 1, _ code: () -> Void) {
let start = Date()
for _ in 0..<averagedExecutions { code() }
let end = Date()
let duration = end.timeIntervalSince(start) / Double(averagedExecutions)
print("time: \(duration)")
}
let data = (1...1_000).map { _ in Int(arc4random_uniform(256)) }
// runs in 0.63s
time {
let noDuplicates: [Int] = data.reduce([], { $0.contains($1) ? $0 : $0 + [$1] })
}
// runs in 0.15s
time {
let noDuplicates: [Int] = data.reduce(into: [], { if !$0.contains($1) { $0.append($1) } } )
}
UI components such as UITableView
and UICollectionView
rely on reuse identifiers in order to efficiently recycle the views they display. Often, those reuse identifiers take the form of a static hardcoded String
, that will be used for every instance of their class.
Through protocol-oriented programing, it's possible to avoid those hardcoded values, and instead use the name of the type as a reuse identifier.
import Foundation
import UIKit
protocol Reusable {
static var reuseIdentifier: String { get }
}
extension Reusable {
static var reuseIdentifier: String {
return String(describing: self)
}
}
extension UITableViewCell: Reusable { }
extension UITableView {
func register<T: UITableViewCell>(_ class: T.Type) {
register(`class`, forCellReuseIdentifier: T.reuseIdentifier)
}
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {
return dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
}
}
class MyCell: UITableViewCell { }
let tableView = UITableView()
tableView.register(MyCell.self)
let myCell: MyCell = tableView.dequeueReusableCell(for: [0, 0])
The C language has a construct called union
, that allows a single variable to hold values from different types. While Swift does not provide such a construct, it provides enums with associated values, which allows us to define a type called Either
that implements a union
of two types.
import Foundation
enum Either<A, B> {
case left(A)
case right(B)
func either(ifLeft: ((A) -> Void)? = nil, ifRight: ((B) -> Void)? = nil) {
switch self {
case let .left(a):
ifLeft?(a)
case let .right(b):
ifRight?(b)
}
}
}
extension Bool { static func random() -> Bool { return arc4random_uniform(2) == 0 } }
var intOrString: Either<Int, String> = Bool.random() ? .left(2) : .right("Foo")
intOrString.either(ifLeft: { print($0 + 1) }, ifRight: { print($0 + "Bar") })
If you're interested by this kind of data structure, I strongly recommend that you learn more about Algebraic Data Types.
Most of the time, when we create a .xib
file, we give it the same name as its associated class. From that, if we later refactor our code and rename such a class, we run the risk of forgetting to rename the associated .xib
.
While the error will often be easy to catch, if the .xib
is used in a remote section of its app, it might go unnoticed for sometime. Fortunately it's possible to build custom test predicates that will assert that 1) for a given class, there exists a .nib
with the same name in a given Bundle
, 2) for all the .nib
in a given Bundle
, there exists a class with the same name.
import XCTest
public func XCTAssertClassHasNib(_ class: AnyClass, bundle: Bundle, file: StaticString = #file, line: UInt = #line) {
let associatedNibURL = bundle.url(forResource: String(describing: `class`), withExtension: "nib")
XCTAssertNotNil(associatedNibURL, "Class \"\(`class`)\" has no associated nib file", file: file, line: line)
}
public func XCTAssertNibHaveClasses(_ bundle: Bundle, file: StaticString = #file, line: UInt = #line) {
guard let bundleName = bundle.infoDictionary?["CFBundleName"] as? String,
let basePath = bundle.resourcePath,
let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: basePath),
includingPropertiesForKeys: nil,
options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants]) else { return }
var nibFilesURLs = [URL]()
for case let fileURL as URL in enumerator {
if fileURL.pathExtension.uppercased() == "NIB" {
nibFilesURLs.append(fileURL)
}
}
nibFilesURLs.map { $0.lastPathComponent }
.compactMap { $0.split(separator: ".").first }
.map { String($0) }
.forEach {
let associatedClass: AnyClass? = bundle.classNamed("\(bundleName).\($0)")
XCTAssertNotNil(associatedClass, "File \"\($0).nib\" has no associated class", file: file, line: line)
}
}
XCTAssertClassHasNib(MyFirstTableViewCell.self, bundle: Bundle(for: AppDelegate.self))
XCTAssertClassHasNib(MySecondTableViewCell.self, bundle: Bundle(for: AppDelegate.self))
XCTAssertNibHaveClasses(Bundle(for: AppDelegate.self))
Many thanks Benjamin Lavialle for coming up with the idea behind the second test predicate.
Seasoned Swift developers know it: a protocol with associated type (PAT) "can only be used as a generic constraint because it has Self or associated type requirements". When we really need to use a PAT to type a variable, the goto workaround is to use a type-erased wrapper.
While this solution works perfectly, it requires a fair amount of boilerplate code. In instances where we are only interested in exposing one particular function of the PAT, a shorter approach using function types is possible.
import Foundation
import UIKit
protocol Configurable {
associatedtype Model
func configure(with model: Model)
}
typealias Configurator<Model> = (Model) -> ()
extension UILabel: Configurable {
func configure(with model: String) {
self.text = model
}
}
let label = UILabel()
let configurator: Configurator<String> = label.configure
configurator("Foo")
label.text // "Foo"
UIKit
exposes a very powerful and simple API to perform view animations. However, this API can become a little bit quirky to use when we want to perform animations sequentially, because it involves nesting closure within one another, which produces notoriously hard to maintain code.
Nonetheless, it's possible to define a rather simple class, that will expose a really nicer API for this particular use case 👌
import Foundation
import UIKit
class AnimationSequence {
typealias Animations = () -> Void
private let current: Animations
private let duration: TimeInterval
private var next: AnimationSequence? = nil
init(animations: @escaping Animations, duration: TimeInterval) {
self.current = animations
self.duration = duration
}
@discardableResult func append(animations: @escaping Animations, duration: TimeInterval) -> AnimationSequence {
var lastAnimation = self
while let nextAnimation = lastAnimation.next {
lastAnimation = nextAnimation
}
lastAnimation.next = AnimationSequence(animations: animations, duration: duration)
return self
}
func run() {
UIView.animate(withDuration: duration, animations: current, completion: { finished in
if finished, let next = self.next {
next.run()
}
})
}
}
var firstView = UIView()
var secondView = UIView()
firstView.alpha = 0
secondView.alpha = 0
AnimationSequence(animations: { firstView.alpha = 1.0 }, duration: 1)
.append(animations: { secondView.alpha = 1.0 }, duration: 0.5)
.append(animations: { firstView.alpha = 0.0 }, duration: 2.0)
.run()
Debouncing is a very useful tool when dealing with UI inputs. Consider a search bar, whose content is used to query an API. It wouldn't make sense to perform a request for every character the user is typing, because as soon as a new character is entered, the result of the previous request has become irrelevant.
Instead, our code will perform much better if we "debounce" the API call, meaning that we will wait until some delay has passed, without the input being modified, before actually performing the call.
import Foundation
func debounced(delay: TimeInterval, queue: DispatchQueue = .main, action: @escaping (() -> Void)) -> () -> Void {
var workItem: DispatchWorkItem?
return {
workItem?.cancel()
workItem = DispatchWorkItem(block: action)
queue.asyncAfter(deadline: .now() + delay, execute: workItem!)
}
}
let debouncedPrint = debounced(delay: 1.0) { print("Action performed!") }
debouncedPrint()
debouncedPrint()
debouncedPrint()
// After a 1 second delay, this gets
// printed only once to the console:
// Action performed!
Optional
booleansWhen we need to apply the standard boolean operators to Optional
booleans, we often end up with a syntax unnecessarily crowded with unwrapping operations. By taking a cue from the world of three-valued logics, we can define a couple operators that make working with Bool?
values much nicer.
import Foundation
func && (lhs: Bool?, rhs: Bool?) -> Bool? {
switch (lhs, rhs) {
case (false, _), (_, false):
return false
case let (unwrapLhs?, unwrapRhs?):
return unwrapLhs && unwrapRhs
default:
return nil
}
}
func || (lhs: Bool?, rhs: Bool?) -> Bool? {
switch (lhs, rhs) {
case (true, _), (_, true):
return true
case let (unwrapLhs?, unwrapRhs?):
return unwrapLhs || unwrapRhs
default:
return nil
}
}
false && nil // false
true && nil // nil
[true, nil, false].reduce(true, &&) // false
nil || true // true
nil || false // nil
[true, nil, false].reduce(false, ||) // true
Sequence
Transforming a Sequence
in order to remove all the duplicate values it contains is a classic use case. To implement it, one could be tempted to transform the Sequence
into a Set
, then back to an Array
. The downside with this approach is that it will not preserve the order of the sequence, which can definitely be a dealbreaker. Using reduce()
it is possible to provide a concise implementation that preserves ordering:
import Foundation
extension Sequence where Element: Equatable {
func duplicatesRemoved() -> [Element] {
return reduce([], { $0.contains($1) ? $0 : $0 + [$1] })
}
}
let data = [2, 5, 2, 3, 6, 5, 2]
data.duplicatesRemoved() // [2, 5, 3, 6]
Optional strings are very common in Swift code, for instance many objects from UIKit
expose the text they display as a String?
. Many times you will need to manipulate this data as an unwrapped String
, with a default value set to the empty string for nil
cases.
While the nil-coalescing operator (e.g. ??
) is a perfectly fine way to a achieve this goal, defining a computed variable like orEmpty
can help a lot in cleaning the syntax.
import Foundation
import UIKit
extension Optional where Wrapped == String {
var orEmpty: String {
switch self {
case .some(let value):
return value
case .none:
return ""
}
}
}
func doesNotWorkWithOptionalString(_ param: String) {
// do something with `param`
}
let label = UILabel()
label.text = "This is some text."
doesNotWorkWithOptionalString(label.text.orEmpty)
Every seasoned iOS developers knows it: objects from UIKit
can only be accessed from the main thread. Any attempt to access them from a background thread is a guaranteed crash.
Still, running a costly computation on the background, and then using it to update the UI can be a common pattern.
In such cases you can rely on asyncUI
to encapsulate all the boilerplate code.
import Foundation
import UIKit
func asyncUI<T>(_ computation: @autoclosure @escaping () -> T, qos: DispatchQoS.QoSClass = .userInitiated, _ completion: @escaping (T) -> Void) {
DispatchQueue.global(qos: qos).async {
let value = computation()
DispatchQueue.main.async {
completion(value)
}
}
}
let label = UILabel()
func costlyComputation() -> Int { return (0..<10_000).reduce(0, +) }
asyncUI(costlyComputation()) { value in
label.text = "\(value)"
}
A debug view, from which any controller of an app can be instantiated and pushed on the navigation stack, has the potential to bring some real value to a development process. A requirement to build such a view is to have a list of all the classes from a given Bundle
that inherit from UIViewController
. With the following extension
, retrieving this list becomes a piece of cake 🍰
import Foundation
import UIKit
import ObjectiveC
extension Bundle {
func viewControllerTypes() -> [UIViewController.Type] {
guard let bundlePath = self.executablePath else { return [] }
var size: UInt32 = 0
var rawClassNames: UnsafeMutablePointer<UnsafePointer<Int8>>!
var parsedClassNames = [String]()
rawClassNames = objc_copyClassNamesForImage(bundlePath, &size)
for index in 0..<size {
let className = rawClassNames[Int(index)]
if let name = NSString.init(utf8String:className) as String?,
NSClassFromString(name) is UIViewController.Type {
parsedClassNames.append(name)
}
}
return parsedClassNames
.sorted()
.compactMap { NSClassFromString($0) as? UIViewController.Type }
}
}
// Fetch all view controller types in UIKit
Bundle(for: UIViewController.self).viewControllerTypes()
I share the credit for this tip with Benoît Caron.
Update As it turns out, map
is actually a really bad name for this function, because it does not preserve composition of transformations, a property that is required to fit the definition of a real map
function.
Surprisingly enough, the standard library doesn't define a map()
function for dictionaries that allows to map both keys
and values
into a new Dictionary
. Nevertheless, such a function can be helpful, for instance when converting data across different frameworks.
import Foundation
extension Dictionary {
func map<T: Hashable, U>(_ transform: (Key, Value) throws -> (T, U)) rethrows -> [T: U] {
var result: [T: U] = [:]
for (key, value) in self {
let (transformedKey, transformedValue) = try transform(key, value)
result[transformedKey] = transformedValue
}
return result
}
}
let data = [0: 5, 1: 6, 2: 7]
data.map { ("\($0)", $1 * $1) } // ["2": 49, "0": 25, "1": 36]
nil
valuesSwift provides the function compactMap()
, that can be used to remove nil
values from a Sequence
of optionals when calling it with an argument that just returns its parameter (i.e. compactMap { $0 }
). Still, for such use cases it would be nice to get rid of the trailing closure.
The implementation isn't as straightforward as your usual extension
, but once it has been written, the call site definitely gets cleaner 👌
import Foundation
protocol OptionalConvertible {
associatedtype Wrapped
func asOptional() -> Wrapped?
}
extension Optional: OptionalConvertible {
func asOptional() -> Wrapped? {
return self
}
}
extension Sequence where Element: OptionalConvertible {
func compacted() -> [Element.Wrapped] {
return compactMap { $0.asOptional() }
}
}
let data = [nil, 1, 2, nil, 3, 5, nil, 8, nil]
data.compacted() // [1, 2, 3, 5, 8]
It might happen that your code has to deal with values that come with an expiration date. In a game, it could be a score multiplier that will only last for 30 seconds. Or it could be an authentication token for an API, with a 15 minutes lifespan. In both instances you can rely on the type Expirable
to encapsulate the expiration logic.
import Foundation
struct Expirable<T> {
private var innerValue: T
private(set) var expirationDate: Date
var value: T? {
return hasExpired() ? nil : innerValue
}
init(value: T, expirationDate: Date) {
self.innerValue = value
self.expirationDate = expirationDate
}
init(value: T, duration: Double) {
self.innerValue = value
self.expirationDate = Date().addingTimeInterval(duration)
}
func hasExpired() -> Bool {
return expirationDate < Date()
}
}
let expirable = Expirable(value: 42, duration: 3)
sleep(2)
expirable.value // 42
sleep(2)
expirable.value // nil
I share the credit for this tip with Benoît Caron.
map()
Almost all Apple devices able to run Swift code are powered by a multi-core CPU, consequently making a good use of parallelism is a great way to improve code performance. map()
is a perfect candidate for such an optimization, because it is almost trivial to define a parallel implementation.
import Foundation
extension Array {
func parallelMap<T>(_ transform: (Element) -> T) -> [T] {
let res = UnsafeMutablePointer<T>.allocate(capacity: count)
DispatchQueue.concurrentPerform(iterations: count) { i in
res[i] = transform(self[i])
}
let finalResult = Array<T>(UnsafeBufferPointer(start: res, count: count))
res.deallocate(capacity: count)
return finalResult
}
}
let array = (0..<1_000).map { $0 }
func work(_ n: Int) -> Int {
return (0..<n).reduce(0, +)
}
array.parallelMap { work($0) }
🚨 Make sure to only use parallelMap()
when the transform
function actually performs some costly computations. Otherwise performances will be systematically slower than using map()
, because of the multithreading overhead.
During development of a feature that performs some heavy computations, it can be helpful to measure just how much time a chunk of code takes to run. The time()
function is a nice tool for this purpose, because of how simple it is to add and then to remove when it is no longer needed.
import Foundation
func time(averagedExecutions: Int = 1, _ code: () -> Void) {
let start = Date()
for _ in 0..<averagedExecutions { code() }
let end = Date()
let duration = end.timeIntervalSince(start) / Double(averagedExecutions)
print("time: \(duration)")
}
time {
(0...10_000).map { $0 * $0 }
}
// time: 0.183973908424377
Concurrency is definitely one of those topics were the right encapsulation bears the potential to make your life so much easier. For instance, with this piece of code you can easily launch two computations in parallel, and have the results returned in a tuple.
import Foundation
func parallel<T, U>(_ left: @autoclosure () -> T, _ right: @autoclosure () -> U) -> (T, U) {
var leftRes: T?
var rightRes: U?
DispatchQueue.concurrentPerform(iterations: 2, execute: { id in
if id == 0 {
leftRes = left()
} else {
rightRes = right()
}
})
return (leftRes!, rightRes!)
}
let values = (1...100_000).map { $0 }
let results = parallel(values.map { $0 * $0 }, values.reduce(0, +))
Swift exposes three special variables #file
, #line
and #function
, that are respectively set to the name of the current file, line and function. Those variables become very useful when writing custom logging functions or test predicates.
import Foundation
func log(_ message: String, _ file: String = #file, _ line: Int = #line, _ function: String = #function) {
print("[\(file):\(line)] \(function) - \(message)")
}
func foo() {
log("Hello world!")
}
foo() // [MyPlayground.playground:8] foo() - Hello world!
Swift 4.1 has introduced a new feature called Conditional Conformance, which allows a type to implement a protocol only when its generic type also does.
With this addition it becomes easy to let Optional
implement Comparable
only when Wrapped
also implements Comparable
:
import Foundation
extension Optional: Comparable where Wrapped: Comparable {
public static func < (lhs: Optional, rhs: Optional) -> Bool {
switch (lhs, rhs) {
case let (lhs?, rhs?):
return lhs < rhs
case (nil, _?):
return true // anything is greater than nil
case (_?, nil):
return false // nil in smaller than anything
case (nil, nil):
return true // nil is not smaller than itself
}
}
}
let data: [Int?] = [8, 4, 3, nil, 12, 4, 2, nil, -5]
data.sorted() // [nil, nil, Optional(-5), Optional(2), Optional(3), Optional(4), Optional(4), Optional(8), Optional(12)]
Any attempt to access an Array
beyond its bounds will result in a crash. While it's possible to write conditions such as if index < array.count { array[index] }
in order to prevent such crashes, this approach will rapidly become cumbersome.
A great thing is that this condition can be encapsulated in a custom subscript
that will work on any Collection
:
import Foundation
extension Collection {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
let data = [1, 3, 4]
data[safe: 1] // Optional(3)
data[safe: 10] // nil
Subscripting a string with a range can be very cumbersome in Swift 4. Let's face it, no one wants to write lines like someString[index(startIndex, offsetBy: 0)..<index(startIndex, offsetBy: 10)]
on a regular basis.
Luckily, with the addition of one clever extension, strings can be sliced as easily as arrays 🎉
import Foundation
extension String {
public subscript(value: CountableClosedRange<Int>) -> Substring {
get {
return self[index(startIndex, offsetBy: value.lowerBound)...index(startIndex, offsetBy: value.upperBound)]
}
}
public subscript(value: CountableRange<Int>) -> Substring {
get {
return self[index(startIndex, offsetBy: value.lowerBound)..<index(startIndex, offsetBy: value.upperBound)]
}
}
public subscript(value: PartialRangeUpTo<Int>) -> Substring {
get {
return self[..<index(startIndex, offsetBy: value.upperBound)]
}
}
public subscript(value: PartialRangeThrough<Int>) -> Substring {
get {
return self[...index(startIndex, offsetBy: value.upperBound)]
}
}
public subscript(value: PartialRangeFrom<Int>) -> Substring {
get {
return self[index(startIndex, offsetBy: value.lowerBound)...]
}
}
}
let data = "This is a string!"
data[..<4] // "This"
data[5..<9] // "is a"
data[10...] // "string!"
By using a KeyPath
along with a generic type, a very clean and concise syntax for sorting data can be implemented:
import Foundation
extension Sequence {
func sorted<T: Comparable>(by attribute: KeyPath<Element, T>) -> [Element] {
return sorted(by: { $0[keyPath: attribute] < $1[keyPath: attribute] })
}
}
let data = ["Some", "words", "of", "different", "lengths"]
data.sorted(by: \.count) // ["of", "Some", "words", "lengths", "different"]
If you like this syntax, make sure to checkout KeyPathKit!
By capturing a local variable in a returned closure, it is possible to manufacture cache-efficient versions of pure functions. Be careful though, this trick only works with non-recursive function!
import Foundation
func cached<In: Hashable, Out>(_ f: @escaping (In) -> Out) -> (In) -> Out {
var cache = [In: Out]()
return { (input: In) -> Out in
if let cachedValue = cache[input] {
return cachedValue
} else {
let result = f(input)
cache[input] = result
return result
}
}
}
let cachedCos = cached { (x: Double) in cos(x) }
cachedCos(.pi * 2) // value of cos for 2π is now cached
When distinguishing between complex boolean conditions, using a switch
statement along with pattern matching can be more readable than the classic series of if {} else if {}
.
import Foundation
let expr1: Bool
let expr2: Bool
let expr3: Bool
if expr1 && !expr3 {
functionA()
} else if !expr2 && expr3 {
functionB()
} else if expr1 && !expr2 && expr3 {
functionC()
}
switch (expr1, expr2, expr3) {
case (true, _, false):
functionA()
case (_, false, true):
functionB()
case (true, false, true):
functionC()
default:
break
}
Using map()
on a range makes it easy to generate an array of data.
import Foundation
func randomInt() -> Int { return Int(arc4random()) }
let randomArray = (1...10).map { _ in randomInt() }
Using @autoclosure
enables the compiler to automatically wrap an argument within a closure, thus allowing for a very clean syntax at call sites.
import UIKit
extension UIView {
class func animate(withDuration duration: TimeInterval, _ animations: @escaping @autoclosure () -> Void) {
UIView.animate(withDuration: duration, animations: animations)
}
}
let view = UIView()
UIView.animate(withDuration: 0.3, view.backgroundColor = .orange)
When working with RxSwift, it's very easy to observe both the current and previous value of an observable sequence by simply introducing a shift using skip()
.
import RxSwift
let values = Observable.of(4, 8, 15, 16, 23, 42)
let newAndOld = Observable.zip(values, values.skip(1)) { (previous: $0, current: $1) }
.subscribe(onNext: { pair in
print("current: \(pair.current) - previous: \(pair.previous)")
})
//current: 8 - previous: 4
//current: 15 - previous: 8
//current: 16 - previous: 15
//current: 23 - previous: 16
//current: 42 - previous: 23
Using protocols such as ExpressibleByStringLiteral
it is possible to provide an init
that will be automatically when a literal value is provided, allowing for nice and short syntax. This can be very helpful when writing mock or test data.
import Foundation
extension URL: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self.init(string: value)!
}
}
let url: URL = "http://www.google.fr"
NSURLConnection.canHandle(URLRequest(url: "http://www.google.fr"))
Through some clever use of Swift private
visibility it is possible to define a container that holds any untrusted value (such as a user input) from which the only way to retrieve the value is by making it successfully pass a validation test.
import Foundation
struct Untrusted<T> {
private(set) var value: T
}
protocol Validator {
associatedtype T
static func validation(value: T) -> Bool
}
extension Validator {
static func validate(untrusted: Untrusted<T>) -> T? {
if self.validation(value: untrusted.value) {
return untrusted.value
} else {
return nil
}
}
}
struct FrenchPhoneNumberValidator: Validator {
static func validation(value: String) -> Bool {
return (value.count) == 10 && CharacterSet(charactersIn: value).isSubset(of: CharacterSet.decimalDigits)
}
}
let validInput = Untrusted(value: "0122334455")
let invalidInput = Untrusted(value: "0123")
FrenchPhoneNumberValidator.validate(untrusted: validInput) // returns "0122334455"
FrenchPhoneNumberValidator.validate(untrusted: invalidInput) // returns nil
With the addition of keypaths in Swift 4, it is now possible to easily implement the builder pattern, that allows the developer to clearly separate the code that initializes a value from the code that uses it, without the burden of defining a factory method.
import UIKit
protocol With {}
extension With where Self: AnyObject {
@discardableResult
func with<T>(_ property: ReferenceWritableKeyPath<Self, T>, setTo value: T) -> Self {
self[keyPath: property] = value
return self
}
}
extension UIView: With {}
let view = UIView()
let label = UILabel()
.with(\.textColor, setTo: .red)
.with(\.text, setTo: "Foo")
.with(\.textAlignment, setTo: .right)
.with(\.layer.cornerRadius, setTo: 5)
view.addSubview(label)
🚨 The Swift compiler does not perform OS availability checks on properties referenced by keypaths. Any attempt to use a KeyPath
for an unavailable property will result in a runtime crash.
I share the credit for this tip with Marion Curtil.
When a type stores values for the sole purpose of parametrizing its functions, it’s then possible to not store the values but directly the function, with no discernable difference at the call site.
import Foundation
struct MaxValidator {
let max: Int
let strictComparison: Bool
func isValid(_ value: Int) -> Bool {
return self.strictComparison ? value < self.max : value <= self.max
}
}
struct MaxValidator2 {
var isValid: (_ value: Int) -> Bool
init(max: Int, strictComparison: Bool) {
self.isValid = strictComparison ? { $0 < max } : { $0 <= max }
}
}
MaxValidator(max: 5, strictComparison: true).isValid(5) // false
MaxValidator2(max: 5, strictComparison: false).isValid(5) // true
Functions are first-class citizen types in Swift, so it is perfectly legal to define operators for them.
import Foundation
let firstRange = { (0...3).contains($0) }
let secondRange = { (5...6).contains($0) }
func ||(_ lhs: @escaping (Int) -> Bool, _ rhs: @escaping (Int) -> Bool) -> (Int) -> Bool {
return { value in
return lhs(value) || rhs(value)
}
}
(firstRange || secondRange)(2) // true
(firstRange || secondRange)(4) // false
(firstRange || secondRange)(6) // true
Typealiases are great to express function signatures in a more comprehensive manner, which then enables us to easily define functions that operate on them, resulting in a nice way to write and use some powerful API.
import Foundation
typealias RangeSet = (Int) -> Bool
func union(_ left: @escaping RangeSet, _ right: @escaping RangeSet) -> RangeSet {
return { left($0) || right($0) }
}
let firstRange = { (0...3).contains($0) }
let secondRange = { (5...6).contains($0) }
let unionRange = union(firstRange, secondRange)
unionRange(2) // true
unionRange(4) // false
By returning a closure that captures a local variable, it's possible to encapsulate a mutable state within a function.
import Foundation
func counterFactory() -> () -> Int {
var counter = 0
return {
counter += 1
return counter
}
}
let counter = counterFactory()
counter() // returns 1
counter() // returns 2
⚠️ Since Swift 4.2,
allCases
can now be synthesized at compile-time by simply conforming to the protocolCaseIterable
. The implementation below should no longer be used in production code.
Through some clever leveraging of how enums are stored in memory, it is possible to generate an array that contains all the possible cases of an enum. This can prove particularly useful when writing unit tests that consume random data.
import Foundation
enum MyEnum { case first; case second; case third; case fourth }
protocol EnumCollection: Hashable {
static var allCases: [Self] { get }
}
extension EnumCollection {
public static var allCases: [Self] {
var i = 0
return Array(AnyIterator {
let next = withUnsafePointer(to: &i) {
$0.withMemoryRebound(to: Self.self, capacity: 1) { $0.pointee }
}
if next.hashValue != i { return nil }
i += 1
return next
})
}
}
extension MyEnum: EnumCollection { }
MyEnum.allCases // [.first, .second, .third, .fourth]
The if-let syntax is a great way to deal with optional values in a safe manner, but at times it can prove to be just a little bit to cumbersome. In such cases, using the Optional.map()
function is a nice way to achieve a shorter code while retaining safeness and readability.
import UIKit
let date: Date? = Date() // or could be nil, doesn't matter
let formatter = DateFormatter()
let label = UILabel()
if let safeDate = date {
label.text = formatter.string(from: safeDate)
}
label.text = date.map { return formatter.string(from: $0) }
label.text = date.map(formatter.string(from:)) // even shorter, tough less readable
Author: Vincent-pradeilles
Source Code: https://github.com/vincent-pradeilles/swift-tips
License: MIT license