A Page Object is simply a design pattern for creating an API for UI manipulation.
All you have to do it to provide to the page object a way to fetch a low-level UI manipulation tool, such as Selenium. For instance, through a singleton.
class MyTests {
@Test
fun test1 {
val page = FactoryOfPageObjects.newPage("login");
val homePage = page.login("user", "1234");
assertTrue(homePage.amILogged());
}
}
class LogiPage {
val driver = // You can inject any implementation of Driver here
fun login(username, password) {
driver.find("#usernameField").insert(username);
driver.find("#passwordField").insert(password);
driver.find("#loginButton").click;
return new LoggedPage(driver);
}
}
interface Driver {
void insert(text);
void click();
}
class SeleniumDriver implements Driver {...}
class AppiumDriver implements Driver {...}
See how above both your tests and your page objects have no idea about the low-level implementation of which driver you are using at run-time. Meaning that if you already have a working implementation without page objects, you can simply refactor it to isolate all references to your driver to the Driver derivatives.
See Martin Folwer's post on it for more details.
(Note for a future edit: If you provide the working implementation without the page object, the community can help you to refactor this in order to isolate the low-level details from your tests and page objects).