# Xamarin.ios 學習筆記
###### tags: `xamarin` , `intern`
:::success
>**Homework**
>build a log in / log out Android page
> [time=Mon, Jul 9, 2018]
> [API in swagger](https://app.swaggerhub.com/apis/dentall7/sample-api/0.0.1#/)
> [color=#28b51e]
:::
##### Xamarin
* [Xamarin developer api](https://developer.xamarin.com/api/)
* [youtube tutorial](https://www.youtube.com/channel/UCCYR9GpcE3skVnyMU8Wx1kQ)
##### mac
* [mac 快捷鍵們](https://support.apple.com/zh-tw/ht201236)
* [build ios with VS for mac](https://docs.microsoft.com/en-us/xamarin/ios/get-started/hello-ios/)
### POST
[referrence](https://stackoverflow.com/questions/36458551/send-http-post-request-in-xamarin-forms-c-sharp)
```csharp=
public async Task PostFuncAsync(string json)
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://sample.dentall.io");
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
response = await client.PostAsync("/api/authenticate", content);
// this result string should be something like: "{"token":"rgh2ghgdsfds"}"
var result = await response.Content.ReadAsStringAsync();
//response.StatusCode is `Enum`
Debug.WriteLine($"code: {(int)response.StatusCode}"); //404,200,401...
}
```
### GET
```csharp=
async public void GetUserInfo(string token)
{
//use token to request user info
var client = new HttpClient();
string url = "http://sample.dentall.io/api/photos";
//add token get from `/api/authenticate/`
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync(url);
int status = (int)response.StatusCode;
if(status == 200){
var result = await response.Content.ReadAsStringAsync();
Debug.WriteLine(result);
}
}
```
### JSON
#### parse class into json form:
```csharp=
//`newUser` is a class
using Newtonsoft.Json;
string json = JsonConvert.SerializeObject(newUser);
```
#### parse json into object form:
##### 方法1
```csharp=
// define the inout type corresponding to the json form
var definition = new { id_token = "" };
//`result` is the response.content
var id1 = JsonConvert.DeserializeAnonymousType(result, definition);
Debug.WriteLine(Convert.ToString(id1.id_token));
```
##### 方法2
```csharp
public class Img{
public int id { get; set; }
public string url { get; set; }
}
Img[] id1 = JsonConvert.DeserializeObject<Img[]>(result);
```
### User Preference
[referrence](https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/user-defaults#writing-default-values)
要存取一些使用者的 Global Variable (ex.`token`)時,iOS有特定的`NSUserDefaults`套件可以使用
```csharp
//自訂屬性`userToken` 並設為字串 token
var t = NSUserDefaults.StandardUserDefaults;
t.SetString(token, "userToken");
```
這樣整個專案的任何檔案就可以拿到同一份使用者資料了
```csharp
var plist = NSUserDefaults.StandardUserDefaults;
var useHeader = plist.StringForKey("userToken"); //token
```
### Page Navigation
#### 1. Navigation Controller
[youtube tutorial](https://www.youtube.com/watch?v=b-cwEQdGkS0)
可以像影片教的那樣直接拉UI,可是按鈕事件一定要手動補上去!
```csharp
partial void CreateAccount_TouchUpInside(UIButton sender)
{
//裡面什麼都不寫也沒關係
}
```
#### 2. manually create ui
[referrence](https://docs.microsoft.com/zh-tw/xamarin/ios/app-fundamentals/ios-code-only?tabs=vsmac)
###### step 1
在 `Main.storyBoard`裡選相對應的 view,設定它在 `ViewController`中的`StoryBoard ID`屬性(這邊我取`main`)
###### step 2
在 `AppDelegate.cs` 裡
```csharp=
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
var storyboard = UIStoryboard.FromName("Main", null);
var mainController = storyboard.InstantiateViewController("main") as ViewController;
var navController = new UINavigationController(mainController);
// set `main` view to the root view
Window.RootViewController = navController;
return true;
}
```
###### step 3
在 `ViewController.cs` 裡
```csharp=
partial void CreateAccount_TouchUpInside(UIButton sender)
{
//這樣是產生一個新的粉紅畫面指過去
var user = new UIViewController();
user.View.BackgroundColor = UIColor.Magenta;
this.NavigationController.PushViewController(user, true);
//這樣是指到名為`logIn`的 view
var storyboard = UIStoryboard.FromName("Main", null);
var mainController = storyboard.InstantiateViewController("LogIn") as LogInPageViewController;
this.NavigationController.PushViewController(mainController, true);
}
```
#### 用法們~
###### 1. ShowViewController(viewController, sender)
是為 `viewController` 開一個新頁
###### 2. PushViewController(viewController, true)
功能同上,但據說是舊式寫法(?),會搭配`NavigationController`使用
```csharp
this.NavigationController.PushViewController(mainController, true);
```
###### 3. PresentViewController(alert, true, null)
把 alert 以跳出視窗 `modal` 的形式呈現
###### 4. PopViewController(true)
把 `Navigation view stack` 上最上層的 view pop 掉
### Modal
[youtube tutorial](https://www.youtube.com/watch?v=DmWv-JtQH4Q)
感覺要用ui拉,因為現在沒有 `PushModalAsync` 的 function
* present modaly
* add `view` (which will be your modal)
* add `blur effect` on the background
* model view 的 `background` 設成 `clear color` (背景變黑表示不顯示 :+1: )
* `segue` 上一定要設 `presentation : over current context`
*
### Update UI after async
[referrence](https://docs.microsoft.com/zh-tw/xamarin/ios/user-interface/ios-ui/ui-thread)
```csharp=
async partial void LogInBtn_TouchUpInside(UIButton sender)
{
UiIndicator.StartAnimating();
var logInSuccess = await PostFuncAsync(json);
/* 下面的程式會等到 await 後面的 function 執行完畢才接著做 */
UiIndicator.StopAnimating();
}
```
### Dynamic Arrangement
>[youtube tutorial](https://www.youtube.com/watch?v=pDlCx0C3v74)
> 要有強大的耐心啦啦啦[color=#ffc802]
### Load Image URL and HTTP Exception
`storyboard` 那邊拉好一個`ImageView`之後(取名為`userImg`),想用 `url` 當 `Image Source` 的方式:
```csharp
public static UIImage UIImageFromUrl(string uri)
{
using (var url = new NSUrl(uri))
using (var data = NSData.FromUrl(url))
return UIImage.LoadFromData(data);
}
//用法
userImg.Image = UIImageFromUrl("https://.../picture.jpg");
```
> 注意!!
> 如果網址是`http`就會跳 Error 説 data is null
> 但真正原因是ios會把不安全連線擋下來以保護使用者
> [color=red]
這時 debug view 那邊會顯示:
`Transport security has blocked a cleartext HTTP
`
如果信任對方http是沒有危險也確定想給予特權的話
可以在 `Info.plist` 加上這一段:
```htmlmixed=
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>sample.dentall.io</key> <!--Include your domain at this line -->
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>
```
參考>> [stackOverflow](https://stackoverflow.com/questions/31254725/transport-security-has-blocked-a-cleartext-http/32560433#32560433)
> 如果 visual studio 不知道怎麼改
> 可以帥帥的去 terminal 那邊 `vim Info.plist` XD
> [name=遠志][time=Sat, Jul 14, 2018 12:15 PM]
### PopUP Alert
[youtube tutorial](https://www.youtube.com/watch?v=y3I9FoOFfyc) : should be modified when displaying alert
[referrence](https://docs.microsoft.com/zh-tw/xamarin/ios/user-interface/controls/alerts) :100:
```csharp=
//create alert
var alert = UIAlertController.Create("Success", "Go and check your email to activate your account!", UIAlertControllerStyle.Alert);
//create an option "OK" that changes view when clicked
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, (UIAlertAction obj) => {
this.NavigationController.PopViewController(true);
}));
//another way: do nothing when the option is clicked
alert.AddAction(UIAlertAction.Create ("OK", UIAlertActionStyle.Default, null));
//display the alert as a popover view on top of the current view
PresentViewController(alert, true, null);
```
### Defensive Programming
```csharp=
try
{
//程式主執行區或可能發生錯誤的地方
}
catch (Exception ex)
{
//例外的處理方法,如秀出警告
}
finally
{
//不論是否發生例外事件都會執行的區塊
}
```
### 滿滿的錯誤大平台 XD
:::danger
**[Error]** Unreachable code after throw
:::
* The exception `System.NotImplementedException ();` is thrown when a requested method or operation is not implemented.
* If there is any inplementation after it, those operations will never be executed.
> [solution](https://stackoverflow.com/questions/14890246/unreachable-code-after-throw-new-system-notimplementedexception)
:::danger
**[Error]** This Class is not Key Value Coding-Compliant for the Key <keyname>
:::
**發生情形** 在做 PageNavigation 時,同時給一個 view 特定的 `Name` 和 `StoryBoard ID` (in its `View Controller`)
> [solution](http://www.tech-recipes.com/rx/52021/how-do-i-fix-the-issue-this-class-is-not-key-value-coding-compliant-for-the-key-in-xcode-6/)
> 看一下 `<keyname>`指的是什麼
> 我這次刪掉 `Name` 就行了,應該是因為重複命名的關係
> [name=遠志][time=Fri, Jul 13, 2018 10:05 AM]