Android: SharedPreferences


There are several data storage options in Android. Such as,

  1. Shared Preferences
    • Stores primitive data types (int, String, float, boolean)
    • Stores as key-value pair
    • Not accessible to other applications
  2. Internal Storage
    • Stores data inside apps private storage
    • Not accessible to other applications
  3. External Storage
    • Stores data on shared external storage (SD Card)
    • Accessible to other applications/user
  4. SQLite Database
    • Stores structured data in apps private storage
    • Not accessible to other applications in general, but can be shared using ContentProviders.
  5. Network
    • Stores data in your own/company server
    • You need an active network connection for accessing/storing the data

We have two options to get the SharedPreferences object,

  1. getPreferences(int mode)
  2. getSharedPreferences(String name, int mode)

There is only one valid value for int mode and that is Context.MODE_PRIVATE

Previously, there were other valid values, such as Context.MODE_WORLD_READABLE and Context.MODE_WORLD_WRITEABLE, but these values got deprecated in API 17 and will throw a SecurityException from Android N.

If we use Context.MODE_PRIVATE then it means, no other apps will be allowed to access our shared preference values.

getPreferences

If we use getPreferences(int mode) then an XML file will be created by name of the Activity and it’s access level will be within the Activity. Separate XML files will be created for each Activity.

getSharedPreferences

If we use getSharedPreferences(String name, int mode) then an XML file will be created by name of String name and it’s access level will be within the Application (i.e any Activity can access the values). If you use a different name then a different XML by that name will be created.

Eaxmple#1

private void saveDataInActivitySharedPreferences() {
    SharedPreferences sharedPrefs = getPreferences(Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPrefs.edit();

    editor.putString("key_string", "value_string");
    editor.putBoolean("key_boolean", true);
    editor.putInt("key_int", 5);

    editor.apply();
    // apply will not return any value and work asynchronously

    // or you could call, editor.commit();
    // commit returns boolean based on un/successful operation and works synchronously
}

private void loadDataFromActivitySharedPreferences() {
    SharedPreferences sharedPrefs = getPreferences(Context.MODE_PRIVATE);

    String valueString = sharedPrefs.getString("key_string", "Default value");
    boolean valueBoolean = sharedPrefs.getBoolean("key_boolean", false);
    int valueInt = sharedPrefs.getInt("key_int", 0);

    String logMsg = valueString + " " + valueBoolean + " " + valueInt;

    Log.d("DEBUG_TAG", logMsg);
}

You can see the XML file content by selecting Tools -> Android -> Android Device Monitor -> (Tab) File Explorer -> data -> data -> APP_PACKAGE_NAME -> shared_prefs -> ACTIVITY_NAME.xml

File content should like below,

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="key_string">value_string</string>
    <boolean name="key_boolean" value="true" />
    <int name="key_int" value="5" />
</map>

If you tap on CLEAR DATA from Settings then this fill will be deleted. DB and Cache files will be deleted as well.

We can not load the values from another Activity because the created file has Activity level access. If we want to access the values from another Activity then we need to call getSharedPreferences instead of getPreferences. Remember that, in case of getSharedPreferences too we have only one value value for int mode = Context.MODE_PRIVATE

Example#2

editor.remove("key_string");
// the key and the associated value will be deleted.
editor.apply();
// do not forget to call apply() method.

editor.clear();
// clear all the data, but the XML file will not be deleted.
editor.apply();
// do not forget to call apply() method.

Demo contacts for Android emulator/genymotion


When we create an emulator or genymotion device, it is kind of blank. What if we need a list of contacts to experiment with. In that case we have two options

  1. We can use our PERSONAL device. urgh!
  2. We can insert a bunch of demo contacts into emulator/ genymotion.

Cons

What if we accidentally delete/call/send text to our personal contacts? shoot!

Or, we might need to discard the emulator/genymotion and create a new one. In that case all our demo data will be gone!

Besides, our demo data will always look like a DEMO data. I mean, anybody can take a look at our contacts list and can identify the data are dummy.

Solution

I have created some dummy data (which would look like real data to most of sane human beings, hopefully) and exported the contacts into a .vcf file format. Anybody can transfer the file into your emulator/genymotion and import the file from contacts app.

And voila! you will get a bunch of contacts (52 contacts as of now). I have also post the file in github repository and of course you will get the latest data from there.

Github repo: github.com/tausiq/demo-contacts-for-Android

Contacts.vcf: https://github.com/tausiq/demo-contacts-for-Android/blob/master/contacts.vcf

 

 

screenshot_1488355668

screenshot_1488355704

Data source: http://www.fakenamegenerator.com and http://pngimg.com

Swift 3: Beyond the first look


Version:

I am using Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)

How do I know which Swift version I am using?
Run the command in Terminal,

xcrun swift -version

Swift is a type-safe language. It infers data types from default value provided for variable/constant.

Variable

Each variable need an initial value OR have to be declared with a type.

var aStringVariable = "Hello world"
var anotherStringVariable: String
var anIntVariable: Int                  // 64-bit signed integer
var anInt8Variable: Int8                // 8-bit signed integer
var anInt16Variable: Int16              // 16-bit signed integer
var anInt32Variable: Int32              // 32-bit signed integer
var anInt64Variable: Int64              // 64-bit signed integer
var anUnsignedIntVariable: UInt         // 64-bit unsigned integer
var anUnsignedInt8Variable: UInt8       // 8-bit unsigned integer
var anUnsignedInt16Variable: UInt16     // 16-bit unsigned integer
var anUnsignedInt32Variable: UInt32     // 32-bit unsigned integer
var anUnsignedInt64Variable: UInt64     // 64-bit unsigned integer
var aDoubleVariable: Double
var aFloatVariable: Float
var aBooleanVariable: Bool
var aCharacterVariable: Character

Character can be double quoted in Swift. If not defined explicitly, it will be inferred as String

var myChar : Character = "a"

In general, there is no syntactical differences between Float and Double, but when you print the values you will get higher precision in Double. But, Double isn’t always better — in cases where speed is more important than accuracy, you might want to choose Float instead.

var pi: Float = 3.14159265359
var pi2: Double = 3.14159265359

print(pi)
print(pi2)

/* Output
3.14159
3.14159265359
*/

Constant

Constant starts with ‘let’ keyword in Swift. Constant value can be set after the declaration, but only once.

let aConstant = 32
let anotherConstant: Int
anotherConstant = 64

Using ‘let’ is highly recommended whenever possible. Besides type safety there are some internal performance optimization by Swift compiler.

Literal vs Constants
A literal is a value that is written exactly as it’s meant to be interpreted. In contrast, a variable is a name that can represent different values during the execution of the program. And a constant is a name that represents the same value throughout a program. But a literal is not a name — it is the value itself.
Source:
http://stackoverflow.com/questions/485119/what-does-the-word-literal-mean

String

let aString = String() 

Print all the characters in a String

var anotherString = "Hello"
for character in anotherString.characters {
    print (character)
}

Notice that String doesn’t have any length property

let aString = "Hello World"
print(theTruth.characters.count)

Reverse a String

var simpleString = "Hola"
var reversedString = simpleString.characters.reversed()
// Here, reversedString is a ReversedCollection<String.CharacterView>
// So you can not just write print(reversedString). Instead,

for character in reversedString {
    print (character)
}

Conditional statement basics

  • Curly braces are REQUIRED, even if there is a single statement.
  • Parenthesis are not needed around the condition.
  • Pre-increment/decrement is deprecated and will be removed in Swift 3. (Post-increment/decrement too)

If-else condition

let x = 5

if x < 5 {
    print("x < 5") } else if x == 5 {     print("x == 5") } else {     print ("x >= 5")
}

While loop

var index = 0
let x = 5

while index < x {
    index += 1      // index++ is deprecated
}

For loop

for i in 0 ... 10 { // for (var i = 0; i <= 10; i++ )
    print("i = \(i)")
}

for i in 0 ..< 10 { // for (var i = 0; i < 10; i++ )
    print("i = \(i) and i * i = \(i * i)")
}

Do while loop

var index = 0
let x = 5

repeat {
    index += 1
} while index < x

Optional basics

  • Variable/constant has to be intialized before used. Otherwise it will produce compile error
  • Optional variable/constant can have a value or it will be ‘nil’, no exception
  • Optional variable/constant is strongly typed too. Either contain a valid value or ‘nil’
  • Opitonal variable/constant can be set to ‘nil’ explicitely
  • Optional variable/constant need to be unwrapped before use. Before unwrap you need to be sure it has a value.
var optionalString: String?
var optionalInt: Int?

if optionalInt != nil {
    var anIntVariable = optionalInt! // unwrapping
    print (anIntVariable)
}

// OR

if let anIntVariable = optionalInt {
    // execution will be here iff optinalInt != nil
    print(anIntVariable) // by default unwrapped
} else {
    print("optionalInt is nil")
}

String format

let a = 5
let b = "World"
let c = 3.6

print("Hello \(a) and \(b) but \(c)")

Function basics

  • Function params are by default constant.
  • On calling a function, you need to specify the name of all variable as key:value (except the first one)
func foo() {
    // no params, no return type
}

func boo() -> String {
    return ""
    // no params, String return type
}

func coo(first: String) -> String {
    return first
    // one String param, String return type
}

func doo(first: String, second: Int) -> Int {
    return second
    // one String param, one Int param, Int return type
}

func moo(first: String, second: Int, third: Int = 2) -> Int {
    return third
    // Default param third set to 2
}

func koo(first: String) {
    // first = "Haha" // Error: params are constant
    // var first: String will make it a varaible
}

foo()
boo()
coo("Haha")
doo("Haha", second: 5) // Error: doo("Haha, 5)
moo("Haha", second: 5) // omit the default param
moo("Haha", second: 5, third: 3) // default param value

Class

  • Instance variable need to initialized with default value OR the class need to have a constructor
  • Constructor is a special function with name ‘init’
  • Current instance reference is ‘self’
class Person {
    var firstName: String = "Sherlock"
    var lastName: String = "Holms"
    var age: Int

    init() {
        // Constructor
        self.age = 50
    }

    // Method
    func getDescription() -> String {
        return "FirstName: \(self.firstName) and LastName: \(self.lastName) and Age: \(self.age)"
    }
}

var p = Person()    // Creating object
print("FirstName: \(p.firstName)")
print(p.getDescription())

instanceof OR is of type

class Person {
    var firstName: String = "Sherlock"
    var lastName: String = "Holms"
}

class Student: Person {
    var id: String = "0123456789"
}

var s = Student()

if s is Person {
    print("s is a Person too")

    var p = s as Person // casting to Person
    print(p.firstName)

} else {
    print ("This will not be executed")
}

Android Library: Gson (Usage and Example)


Serialization

Serialization is a process of writing the state of an object into a byte stream. Similarly, deserialization is the process of turning the byte stream into an object.

Installation

You can get the latest version of Gson library from here.
Right now the latest version is 2.7. Click on the version from the link and you will get the instruction to add the library for your favorite build system.

For Gradle, I would use,

compile group: 'com.google.code.gson', name: 'gson', version: '2.7'

this can be simplified to,

compile "com.google.code.gson:gson:2.7"

Example#1

We have a class named User

public class User {

    private String firstName;

    private String lastName;

    private int age;

    private List<String> emails;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getEmails() {
        return emails;
    }

    public void setEmails(List<String> emails) {
        this.emails = emails;
    }
}

Now let’s create an object of class User and convert the object to String and after that we will create an User object from the String.


User user = new User();
user.setFirstName("Android");
user.setLastName("Studio");
user.setAge(3);
user.setEmails(Arrays.asList("android.studio@gmail.com", "android.studio@android.com"));

// converting the User object to string
Gson gson = new Gson();
String jsonStringUser = gson.toJson(user, User.class);

// Creating the User object from string
User returnedUser = gson.fromJson(jsonStringUser, User.class);

Example#2

Now, suppose, we have a generic class.

public class Employee<T> {

	private T person;

	public T getPerson() {
		return person;
	}

	public void setPerson(T person) {
		this.person = person;
	}
}

Now let’s create an object of class Employee and convert the object to String and after that we will create an Employee object from the String.

User user = new User();
user.setFirstName("Android");
user.setLastName("Studio");
user.setAge(3);
user.setEmails(Arrays.asList("android.studio@gmail.com", "android.studio@android.com"));

Employee<User> employee = new Employee<>();
employee.setPerson(user);

Gson gson = new Gson();

// This will not work
String jsonString = gson.toJson(employee, Employee.class);

We could have put any class object inside Employee. There is no way the JVM will know the object type until runtime. There is a concept of Java type erasure that will explain why the simple Employee.class will not work with Gson. To resolve the issue we need to get the type at runtime using TypeToken

User user = new User();
user.setFirstName("Android");
user.setLastName("Studio");
user.setAge(3);
user.setEmails(Arrays.asList("android.studio@gmail.com", "android.studio@android.com"));

Employee<User> employee = new Employee<>();
employee.setPerson(user);

Gson gson = new Gson();

Type type = new TypeToken<Employee<User>>() {
}.getType();

String jsonString = gson.toJson(employee, type);

// load the value
Employee<User> returnedEmployee = gson.fromJson(jsonString, type);
User returnedUser = returnedEmployee.getPerson();

iOS 9: Beyond the first look


Get all subviews

let childViews = view.subviews

for aView in childViews {
    if aView is UILabel {
        let aLabel = aView as UILabel
    } else if aView is UIButton {
        let aButton = aView as UIButton
    } else if aView is UIImageView {
        let aImageView = aView as UIImageView
    }
}

Table views

  • iOS table view is one column wide (with multiple rows)
  • One row contains one cell. This cell contains the views of that particular row

Let’s create a simple tableview

  1. Drag a Table View (not Table View Controller) into your main view controller.
  2. Resolve auto layout issues (Select Table View => Editor => Resolve Auto Layout Issues => Reset to Suggested Constraints)
  3. Connect dataSource and delegate Outlets to view controller (from Connections Inspector)

UITableViewDataSource controls the following

  • Configuring a table view
  • Inserting or deleting table rows
  • Reordering table rows

Configuring a table view

There are several methods you can use to configure your table view. Look at the apple documentation for elaborated information. For a quick overview look at the list

– tableView:cellForRowAtIndexPath: (Required)
Asks the data source for a cell to insert in a particular location of the table view.

func tableView(_ tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell

– tableView:numberOfRowsInSection: (Required)
Tells the data source to return the number of rows in a given section of a table view.

func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int

– numberOfSectionsInTableView:
Asks the data source to return the number of sections in the table view.

optional func numberOfSectionsInTableView
(_ tableView: UITableView) -> Int

Implementation of the required methods

class ViewController: UIViewController, UITableViewDataSource {

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel!.text = "Cell number \(indexPath.row)"

        return cell
    }
}

Now we will create some dummy data and make use of the tableview accordingly

class ViewController: UIViewController, UITableViewDataSource {

    let section1Data = ["Section 1a", "Section 1b", "Section 1c", "Section 1d"]
    let section2Data = ["Section 2a", "Section 2b", "Section 2c", "Section 2d", "Section 2e"]
    let section3Data = ["Section 3a", "Section 3b", "Section 3c"]

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 3
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch section {
        case 0: return section1Data.count
        case 1: return section2Data.count
        case 2: return section3Data.count
        default: return 0
        }
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell()

        var item : String

        switch indexPath.section {
        case 0:
            item = section1Data [indexPath.row]
            break
        case 1:
            item = section2Data [indexPath.row]
            break
        case 2:
            item = section3Data [indexPath.row]
            break
        default:
            item = ""
        }

        cell.textLabel!.text = item

        return cell
    }

    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        switch section {
        case 0:
            return "Section 01"
        case 1:
            return "Section 02"
        case 2:
            return "Section 03"
        default:
            return ""
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

UITableViewDelegate controls the following

  • Behaviour
  • Appearance
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    ... 

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        print("User clicked. Section \(indexPath.section) and row \(indexPath.row)")
    }

    func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
        let deleteAction = UITableViewRowAction(style: .Default, title: "Delete", handler: {
            action, indexPath in
            
            // Delete from data
            switch indexPath.section {
            case 0:
                break
            case 1:
                break
            case 2:
                break
            }
            
            tableView.reloadData()
            
            tableView.editing = false
        })
        
        let retActions = [deleteAction]
        return retActions
    }

    ...
}

Alert view

let confirmAlert = UIAlertController(title: "Deleting row", message: "Do you really want to delete?", preferredStyle: .Alert)
let posAction = UIAlertAction(title: "Yes", style: .Destructive, handler: {
    action in
    print("Yes button clicked")
})
let negAction = UIAlertAction(title: "No", style: .Default, handler: nil)
        
confirmAlert.addAction(posAction)
confirmAlert.addAction(negAction)
        
presentViewController(confirmAlert, animated: true, completion: nil)

Add App icon

  • Select Assets.xcassets -> AppIcon -> Attirbutes Inspector
  • You should see all possible image sizes you would like to provide
  • Select each of the icon and see the Image section in Attirbutes Inspector
  • You will see the Size property, but it is in point mode, not pixel. Then you will see the Scale property and know the actual pixel sized image you need to provide. px = pt * scale

Launcher screen

  • Usually launcher screen would be the first screen of the application without any data loaded.
  • You can mimic the controls of the first screen and put those is same fashion in launcher screen.

For more informaiton, follow this guide

Add a new ViewController

  • Add a new ViewController (or, variant) in storyboard
  • Create a new Cocoa Touch Class (Swift file), make it a subclass of UIViewController (or, variant)
  • Select the ViewController from storyboard and open Identity Inspector and link with the Swift file
  • Now we need to make a way to go to the new screen. Let’s review show method first.
    • Create a button in FirstViewController and Control+Drag it to the SecondViewController and select Action Segue (Show)
  • Another way is to create a NavigationViewController.
    • Select the FirstViewController and Editor => Embed In => Navigation Controller
    • For displaying a title, go to viewDidLoad() method and override self.title = “”

Add a new TabbarController

  • We can start by opening a new project with Tabbed Application option
  • Create a new ViewController in storyboard and control+drag from TabbarController (bottom) to new ViewController
  • This time select Relationship Segues (view controllers)
  • Individual tabbar icon or text can be change by selecting that individual tab text/icon.

Auto Layout and handling multiple screens

  • Auto Layout is enabled by default. Still if you need to check, Open storyboard => Show File Inspector => Check (Use Auto Layout)
  • First place the controls in square sized view controller and then make use of Resolve Auto Layout Issues (Bottom-Right corner) => Reset to Suggessted Constraints
  • General suggestion: If you get yellow/red sign in Auto Layout then go to Document Outline screen of storyboard and tap on the top right corner button
  • Clear constraint of a paritcular view by selecting Resolve Auto Layout Issues (Bottom-Right corner) => Clear Constraints
  • For more info: https://developer.apple.com/design/adaptivity/
  • Alternatively you can use a handy library: http://snapkit.io

Stack Views

  • Stack views are much more like LinearLayout in Android. They can either be horizontal or vertical. Unlike LinearLayout they can NOT have any view properties (color etc)
  • Unlike Android, you are supposed to use nested stack views in iOS
  • There are 3 properties in stack views that you need to take care of
    • Alignment
    • Distribution
    • Spacing
  • General suggestion: First place controls roughly on the storyboard
    • Select the controls
    • Editor -> Embed In -> Stack view
    • Xcode should embed the controls with proper subviews. If not you can change it from Attributes Inspector.
  • Selecting stack view from storyboard can be tricky. Also, dragging the stack view to setup it’s width and height will not work as expected. Workaround is, select stack view from Document Outline view. Setup constraint using Pin button (Bottom-right corner)
  • Now play with the 3 attributes (Alignment, Distribution and Spacing) from Attributes Inspector to make things right your choice.
  • You might see some warnings regarding stack views and those might be a false alarm. Select the stack view, go to the Size Inspector and decrement the value of x by one and change it back to original. If it was a false alarm it should go away.

Dynamic layout for different screens

You can take advantage of size classes as it will allow you to design specific layout for specific screen. For example, you can reduce the font size only when the layout will render in iPhone 4 landscape mode. Remember, Auto layout constraints will be applied first than Size classes.

You should see “w any h Any” at the bottom of the storyboard. Tap on it and select a variation. At the bottm of the new dialog you can see the list of the screen this particular variation will take effect.

There are two keywords: Compact and Regular. Here, Compact essentially means, limited. You need to take extra care when you see compact width/height.

For more information, follow this guide

General suggestion: You should play with the size classes at the very end. In addition, you should do the experiments in separate branch, thus you can always revert back to the previous working branch.

After selecting a variant the bottom bar of the storyboard will turn into blue and the storyboard view controller will take the specific device frame that you selected in variant.

Change the font

  • Select the label.
  • Go to Attributes Inspector
  • Do not change the font. Instead, click on the (+) button beside font.
  • Select the proper variation base and you should see a separate font change inputbox
  • Change font and size using the input box.
  • Use Assistance Editor (Preview mode) to see how things will look finally in different devices

Change size of a view

  • Make sure the auto layout constraints have been set.
  • Select the control you would like to change the size.
  • Go to Size Inspector. In the Constraints section, change the constraint value (width/height) by clicked Edit button
  • Check your work in Assistance Editor (Preview mode)
  • You might need to set the auto layout contraints again based on the changes you made

Remove a view/item

  • Select the control you would like to delete.
  • Hit Command + Delete.
  • The control should still be visible in Document Outline view but greyed out

Xcode 7: Beyond the first look


Version:

I am using Xcode Version 7.3.1 (7D1014)

Most used keyboard shortcuts Xcode

Action Shortcut Symbol
Run Command + R ⌘ + R
Stop Command + . ⌘ + .
Show Project Navigator Command + 1 ⌘ + 1
Show Quick Help Inspector Option + Command + 2 ⌥ + ⌘ + 2

Most used keyboard shortcuts iOS simulator

Action Shortcut Symbol
Scale 100% Command + 1 ⌘ + 1
Scale 75% Command + 2 ⌘ + 2
Scale 50% Command + 3 ⌘ + 3
Scale 33% Command + 4 ⌘ + 4
Scale 25% Command + 5 ⌘ + 5

Recommended Preferences

  • Goto Xcode preferences (⌘ + ,)
    • Select tab (Text Editing)
      • Check (Line numbers)
    • Select tab (Accounts)
      • Select + (bottom left corner)
      • Add Apple ID
      • Add your apple account credential. This will enable you to run your app in your iOS device. From Xcode 7, you do not need to have a paid account for this. However, uploading app to the App store still require you to have a paid developer account.
    • Select tab (Components)
      • Select sub tab (Documentation)
      • Download the documentations you required. For example, for iOS development it would be helpful to have iOS x.x Documentation and Xcode x.x documentation.

Most used keyboard shortcuts Storyboard

Action Shortcut Symbol
Copy elements Option + Click + Drag ⌥ + Click + Drag
Align Left Edges Command + [ ⌘ + [
Align Right Edges Command + ] ⌘ + ]
Size to Fit Content Command + = ⌘ + =

Embed all views into a single view

  1. Select all the views, you want to embed in
  2. Editor => Embed In => View/ Scroll View/ Stack View
  3. Editor => Resolve Auto Layout Issues => Reset to Suggested Constraints

Debug views in Xcode

  • Run the app. While it is running locate a button in debug section: Debug View Hierarchy
  • See the slider? Drag it to right to zoom in and separate the views
  • Drag on blank area and you can rotate, move the views

Class structure quick look

  • Open a Swift class
  • Editor
    • Code Folding
      • Fold Methods and Functions
      • Fold Comment Blocks