+7 (495) 215-5316

info@appropio.com

ул. Заозерная, 8

196084, Санкт-Петербург

09:00 - 19:00

сб.,вс. - выходные

Начальная сборка iOS

 Этапы создания    
< Назад

Для того, чтобы собрать iOS-проект Appropio, необходимо выполнить следующие этапы:

1. Убедиться, что при создании проекта был верно указан AppID в соответствии с маской:com.notissimus.{project_name}

  • Указать минимальную версию iOS – 10.0

2. Привести info.plist проекта к соответствующему виду и добавить необходимые разрешения:

  • NSLocationUsageDescription
  • NSLocationAlwaysUsageDescription
  • NSLocationWhenInUseUsageDescription
  • NSLocationAlwaysAndWhenInUseUsageDescription
  • NSAppTransportSecurity
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleName</key>
  <string>Progect_Name</string>
  <key>CFBundleIdentifier</key>
  <string>com.notissimus.Progect_Name</string>
  <key>CFBundleShortVersionString</key>
  <string>1.0</string>
  <key>CFBundleVersion</key>
  <string>1.1</string>
  <key>LSRequiresIPhoneOS</key>
  <true/>
  <key>MinimumOSVersion</key>
  <string>10.0</string>
  <key>UIDeviceFamily</key>
  <array>
    <integer>1</integer>
  </array>
  <key>UIRequiredDeviceCapabilities</key>
  <array>
    <string>armv7</string>
  </array>
  <key>UISupportedInterfaceOrientations</key>
  <array>
    <string>UIInterfaceOrientationPortrait</string>
  </array>
  <key>NSLocationUsageDescription</key>
  <string>Мы не передаем данные о вашей локации третьим лицам и используем их для вашего удобства</string>
  <key>NSLocationAlwaysUsageDescription</key>
  <string>Мы не передаем данные о вашей локации третьим лицам и используем их для вашего удобства</string>
  <key>NSLocationWhenInUseUsageDescription</key>
  <string>Мы не передаем данные о вашей локации третьим лицам и используем их для вашего удобства</string>
  <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
  <string>Мы не передаем данные о вашей локации третьим лицам и используем их для вашего удобства</string>
  <key>NSCalendarsUsageDescription</key>
  <string>Необходим для корректной работы приложения</string>
  <key>NSAppTransportSecurity</key>
  <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
  </dict>
  <key>XSAppIconAssets</key>
  <string>Assets.xcassets/AppIcon.appiconset</string>
  <key>UIBackgroundModes</key>
  <array>
    <string>remote-notification</string>
  </array>
  <key>XSLaunchImageAssets</key>
  <string>Assets.xcassets/LaunchImage.launchimage</string>
</dict>
</plist>

3. Удалить лишние для проекта файлы (ViewController.cs, Main.storyboard, LaunchScreen.storyboard)

4. Подключить в References dll-файлы в соответствии с необходимыми модулями и настройками (Базовые, Меню + Каталог (Продукты) + Корзина). Добавить скаченные ранее Dll из ветки Master (lib/release).

Базовые библиотеки:

  • AppRopio.Models.Base
  • AppRopio.Base.API
  • AppRopio.Base.Core
  • AppRopio.Base.iOS

Контакты:

  • AppRopio.Models.Contacts
  • AppRopio.Base.Contacts.API
  • AppRopio.Base.Contacts.Core
  • AppRopio.Base.Contacts.iOS

Фильтры:

  • AppRopio.Models.Filters
  • AppRopio.Base.Filters.API
  • AppRopio.Base.Filters.Core
  • AppRopio.Base.Filters.iOS

Информация:

  • AppRopio.Models.Information
  • AppRopio.Base.Information.API
  • AppRopio.Base.Information.Core
  • AppRopio.Base.Information.iOS

Адреса на карте:

  • AppRopio.Models.Map
  • AppRopio.Base.Map.API
  • AppRopio.Base.Map.Core
  • AppRopio.Base.Map.iOS

Настройки:

  • AppRopio.Models.Settings
  • AppRopio.Base.Settings.API
  • AppRopio.Base.Settings.Core
  • AppRopio.Base.Settings.iOS

Корзина:

  • AppRopio.Models.Basket
  • AppRopio.ECommerce.Basket.API
  • AppRopio.ECommerce.Basket.Core
  • AppRopio.ECommerce.Basket.iOS

Отзывы:

  • AppRopio.Models.Feedback
  • AppRopio.Feedback.API
  • AppRopio.Feedback.Core
  • AppRopio.Feedback.iOS

История заказов:

  • AppRopio.Models.HistoryOrders.API
  • AppRopio.ECommerce.HistoryOrders.API
  • AppRopio.ECommerce.HistoryOrders.Core
  • AppRopio.ECommerce.HistoryOrders.iOS

Лояльность:

  • AppRopio.Models.Loyalty
  • AppRopio.ECommerce.Loyalty.Abstractions
  • AppRopio.ECommerce.Loyalty.API
  • AppRopio.ECommerce.Loyalty.Core
  • AppRopio.ECommerce.Loyalty.iOS

Избранное:

  • AppRopio.Models.Marked
  • AppRopio.ECommerce.Marked.API
  • AppRopio.ECommerce.Marked.Core
  • AppRopio.ECommerce.Marked.iOS

Меню:

  • AppRopio.Navigation.Menu.Core
  • AppRopio.Navigation.Menu.iOS

Каталог:

  • AppRopio.Models.Products
  • AppRopio.ECommerce.Products.API
  • AppRopio.ECommerce.Products.Core
  • AppRopio.ECommerce.Products.iOS

Маячки (Beacons):

  • AppRopio.Models.Beacons
  • AppRopio.Beacons.API
  • AppRopio.Beacons.Core
  • AppRopio.Beacons.iOS

Оплата в приложении:

  • AppRopio.Models.Payments
  • AppRopio.Payments.API
  • AppRopio.Payments.Core
  • AppRopio.Payments.iOS

Прочие библиотеки добавляются по мере необходимости и доработок функционала приложения, однако для базовой версии они не нужны.

5. Добавляем файлы локализации вручную. Создаем в корне проекта папку LocalizationResx и добавляем в нее файлы Resources (.resx) – LocalizationResx. Эти скаченные файлы нельзя скопировать в папку, а использовать только для копирования кода в созданные одноименные файлы. Так как в приложении предусмотрена реализация локализации на два языка – английский и русский, то необходимо учитывать, что названия русской и английской версии должны совпадать, за тем исключением, что в русской версии к имени файлу добавляется “.ru”. Таким образом название одного из файлов английской версии будет Base.resx, а русской – Base.ru.resx.

В созданную папку добавляем новый файл в соответствии с изображением. Необходимо выбрать файл ресурсов в разделе Misc и указать название

Так как при создании файла русской локализации нельзя использовать в названии символ “.”, то сделать по аналогии с изображением выше – добавить к названию файла произвольный символ (в указанном примере стоит цифра “2”).  Далее просто переименовать файл в “.ru” и не забыть добавить изменения в подфайл Base.ru.Designer.cs. Повторить действие с каждым из файлов в папке.

6. В корне проекта разместить папку Settings, в которой есть вложенные папки под названием Configs и ThemeConfigs, исходные данные скачать по данной ссылке – Settings.

7. Добавить папку Images в корень проекта

8. Далее необходимо добавить Nuget-пакеты в приложение. Для этого в папке Packages выбираем “Add NuGet-Packages” и попадаем в раздел добавления.

Актуальные версии Nuget-пакетов были вынесены в данный раздел – https://appropio.com/knowledge-base/nuget-org-versii-paketov/

9. Добавить в корень проекта LinkerPleaseInclude.cs:

using System;
using System.Collections.Specialized;
using System.Windows.Input;
using Foundation;
using MvvmCross.Binding.BindingContext;
using MvvmCross.iOS.Views;
using MvvmCross.Platform.WeakSubscription;
using UIKit;

namespace project_name.iOS
{
    // This class is never actually executed, but when Xamarin linking is enabled it does how to ensure types and properties
    // are preserved in the deployed app
    [Foundation.Preserve(AllMembers = true)]
    public class LinkerPleaseInclude
    {
        public void Include(ConsoleColor color)
        {
            Console.Write("");
            Console.WriteLine("");
            color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.ForegroundColor = ConsoleColor.White;
            Console.ForegroundColor = ConsoleColor.Gray;
            Console.ForegroundColor = ConsoleColor.DarkGray;
        }

        public void Include(MvxTaskBasedBindingContext c)
        {
            c.Dispose();
            var c2 = new MvxTaskBasedBindingContext();
            c2.ClearAllBindings();
            c2.Dispose();
        }

        public void Include(UIButton uiButton)
        {
            uiButton.TouchUpInside += (s, e) =>
                uiButton.SetTitle(uiButton.Title(UIControlState.Normal), UIControlState.Normal);
            uiButton.Selected = uiButton.Selected;
        }

        public void Include(UIBarButtonItem barButton)
        {
            barButton.Clicked += (s, e) =>
                barButton.Title = barButton.Title + "";
            barButton.WeakSubscribe(nameof(barButton.Clicked), null);
        }

        public void Include(UITextField textField)
        {
            textField.Text = textField.Text + "";
            textField.EditingChanged += (sender, args) => { textField.Text = ""; };
            textField.AttributedText = new NSAttributedString(textField.AttributedText.ToString() + "");
            textField.WeakSubscribe(nameof(textField.EditingChanged), null);
        }

        public void Include(UITextView textView)
        {
            textView.Text = textView.Text + "";
            textView.Changed += (sender, args) => { textView.Text = ""; };
            textView.LayoutManager.TextStorage.DidProcessEditing += (sender, e) => { };
            textView.LayoutManager.TextStorage.WeakSubscribe<NSTextStorage, NSTextStorageEventArgs>(nameof(textView.LayoutManager.TextStorage.DidProcessEditing), null);
        }

        public void Include(NSLayoutManager layoutManager)
        {
            layoutManager.TextStorage.DidProcessEditing += (sender, e) => { };
            layoutManager.TextStorage.WeakSubscribe<NSTextStorage, NSTextStorageEventArgs>(nameof(layoutManager.TextStorage.DidProcessEditing), null);
        }

        public void Include(NSTextStorage textStorage)
        {
            textStorage.DidProcessEditing += (sender, e) => { };
            textStorage.WeakSubscribe<NSTextStorage, NSTextStorageEventArgs>(nameof(textStorage.DidProcessEditing), null);
        }

        public void Include(UILabel label)
        {
            label.Text = label.Text + "";
            label.AttributedText = new NSAttributedString(label.AttributedText.ToString() + "");
        }

        public void Include(UIImageView imageView)
        {
            imageView.Image = new UIImage(imageView.Image.CGImage);
        }

        public void Include(UIDatePicker datePicker)
        {
            datePicker.Date = datePicker.Date.AddSeconds(1);
            datePicker.ValueChanged += (sender, args) => { datePicker.Date = NSDate.DistantFuture; };
            datePicker.WeakSubscribe<UIDatePicker, EventArgs>(nameof(datePicker.ValueChanged), null);
        }

        public void Include(UISlider slider)
        {
            slider.Value = slider.Value + 1;
            slider.ValueChanged += (sender, args) => { slider.Value = 1; };
        }

        public void Include(UIProgressView progress)
        {
            progress.Progress = progress.Progress + 1;
        }

        public void Include(UISwitch sw)
        {
            sw.On = !sw.On;
            sw.ValueChanged += (sender, args) => { sw.On = false; };
            sw.WeakSubscribe(nameof(sw.ValueChanged), null);
        }

        public void Include(MvxViewController vc)
        {
            vc.Title = vc.Title + "";
        }

        public void Include(UIStepper s)
        {
            s.Value = s.Value + 1;
            s.ValueChanged += (sender, args) => { s.Value = 0; };
        }

        public void Include(UIPageControl pageControl)
        {
            pageControl.Pages = pageControl.Pages + 1;
            pageControl.ValueChanged += (sender, args) => { pageControl.Pages = 0; };
            pageControl.WeakSubscribe(nameof(pageControl.ValueChanged), null);
        }

        public void Include(INotifyCollectionChanged changed)
        {
            changed.CollectionChanged += (s, e) => { var test = $"{e.Action}{e.NewItems}{e.NewStartingIndex}{e.OldItems}{e.OldStartingIndex}"; };
        }

        public void Include(ICommand command)
        {
            command.CanExecuteChanged += (s, e) => { if (command.CanExecute(null)) command.Execute(null); };
        }

        public void Include(MvvmCross.Platform.IoC.MvxPropertyInjector injector)
        {
            injector = new MvvmCross.Platform.IoC.MvxPropertyInjector();
        }

        public void Include(System.ComponentModel.INotifyPropertyChanged changed)
        {
            changed.PropertyChanged += (sender, e) => { var test = e.PropertyName; };
        }

        public void Include(MvxWeakEventSubscription<object, EventArgs> subscription)
        {
            typeof(object).GetEvent("");
            subscription = new MvxWeakEventSubscription<object, EventArgs>(null, "", null);
            subscription.Dispose();
        }

        public void Include(MvxNotifyPropertyChangedEventSubscription subsctiption)
        {
            subsctiption = new MvxNotifyPropertyChangedEventSubscription(null, null);
            subsctiption.Dispose();
        }

        public void Include(MvxCanExecuteChangedEventSubscription subsctiption)
        {
            subsctiption = new MvxCanExecuteChangedEventSubscription(null, null);
            subsctiption.Dispose();
        }

        public void Include(MvxGeneralEventSubscription subsctiption)
        {
            subsctiption = new MvxGeneralEventSubscription(null, null, null);
            subsctiption.Dispose();
        }

        public void Include(UISearchBar searchBar)
        {
            searchBar.TextChanged += (sender, e) => { };
            searchBar.WeakSubscribe<UISearchBar, UISearchBarTextChangedEventArgs>(nameof(searchBar.TextChanged), null);
        }
    }
}

10. Добавить в корень проекта loader.html, указать как BundleResource:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Untitled Document</title>
</head>
<body>
    <style>
        body {
            background: rgba(0, 0, 0, 0);
        }

        #loader {
            animation: animate 1.5s linear infinite;
            clip: rect(0, 80px, 80px, 40px);
            height: 80px;
            width: 80px;
            position: absolute;
            left: calc(50% - 40px);
            top: calc(50% - 40px);
        }

        @keyframes animate {
            0% {
                transform: rotate(0deg)
            }

            100% {
                transform: rotate(220deg)
            }
        }

        #loader:after {
            animation: animate2 1.5s ease-in-out infinite;
            clip: rect(0, 80px, 80px, 40px);
            content: '';
            border-radius: 50%;
            height: 80px;
            width: 80px;
            position: absolute;
        }

        @keyframes animate2 {
            0% {
                box-shadow: inset #fff 0 0 0 17px;
                transform: rotate(-140deg);
            }

            50% {
                box-shadow: inset #fff 0 0 0 2px;
            }

            100% {
                box-shadow: inset #fff 0 0 0 17px;
                transform: rotate(140deg);
            }
        }
    </style>
    <div id="loader"></div>

</body>
</html>

11. Создать в корне проекта файл AppDelegate.cs:

using AppRopio.Base.iOS;
using AppRopio.Base.iOS.UIExtentions;
using AppRopio.Navigation.Menu.iOS;
using AppRopio.Navigation.Menu.iOS.Navigation;
using Foundation;
using MvvmCross.iOS.Platform;
using MvvmCross.iOS.Views.Presenters;
using UIKit;

namespace Project_Name.iOS
{
    [Register("AppDelegate")]
    public class AppDelegate : ARApplicationDelegate
    {
        protected override MvxIosViewPresenter CreatePresenter(IMvxApplicationDelegate appDelegate, UIWindow window)
        {
            return new MenuNavigationPresenter(this, window);
        }

        protected override MvxAsyncIosSetup CreateSetup(IMvxApplicationDelegate appDelegate, MvxIosViewPresenter presenter)
        {
            return new MenuSetup(this, presenter);
        }

        protected override UIViewController ConstructDefaultViCo()
        {
            var viCo = base.ConstructDefaultViCo();

            viCo.View.BackgroundColor = "FFFFFF".ToUIColor();

            return viCo;
        }

        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
        {

            return base.FinishedLaunching(application, launchOptions);
        }
    }
}

12. Создать в корне проекта файл Main.cs:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Foundation;
using UIKit;

namespace progect_name.iOS
{
    public class Application
    {
        static void Main(string[] args)
        {

#if DEBUG
            AppRopio.Base.Core.ServicesDebug.StartupTimerService.Instance.StartTimer();
#endif
            try
            {
                UIApplication.Main(args, null, "AppDelegate");
            }
            catch (Exception ex)
            {
                //#if DEBUG
                Debug.Write(ex.BuildAllMessagesAndStackTrace());
                throw;
                //#endif
            }
        }
    }
}

13. Создать в корне проекта файл FodyWeavers.xml:

<?xml version="1.0" encoding="utf-8"?>
<Weavers>
  <RealmWeaver/>
  
</Weavers>

14. Создать в корне папку Bootstrap и добавить в нее bootstrap-файлы – Bootstrap (не забыть поменять окружение на соответствующее новому проекту)

15. В корневом разделе проекта Assets.xcassets добавить из sketch-файла проекта иконки и загрузочные экраны в соответствии с разрешениями экрана под разные модели устройств.

16. Прописать в настройках проекта –linkskip=MvvmCross –linkskip=MvvmCross.Core –linkskip=MvvmCross.Binding –linkskip=MvvmCross.Platform в аргументы mtouch (необходимо только при дебаге)

17. В Configs/App.json прописать apiKey и companyId (сгенерировать их с помощью предоставленной другими разработчиками программой и передать данные серверному разработчику, не забыв сказать id и название компании. Описание приведено здесь – https://appropio.com/knowledge-base/proekt-dlja-generacii-id/)

После выполнения данных шагов, запустить проект. Если все сделано верно, то запустится приложение с базой xml-выгрузки нужного проекта, но без дизайна, прорисованного в sketch-файле. Дальнейшие шаги будут направлены на приведения проекта в соответствие с утвержденным дизайном.

18. Взять иконки из Sketch-файла, заменив на те, что имеются в папке Images, не забыв изменить названия на те, что указаны в папке.

19. Изменение отображение каталога первого и второго уровней, работа с элементами меню и карточки товара, а также цветов происходит в папке Settings. Подробная информация об этом будет приведена в следующем разделе.